feat: Enhance Dungeon Editor with Advanced Room Properties and Layout Visualization
- Introduced advanced room properties UI in the Dungeon Editor, allowing users to set effects and tags for rooms through dropdown menus. - Updated the rendering logic to visualize room layouts, including walls, floors, pits, water, and doors, with appropriate color coding for clarity. - Improved texture management by processing texture updates immediately to ensure objects appear correctly in the editor. - Organized ROM address constants into a new header file for better maintainability and clarity in the codebase. - Refactored existing code to utilize the new constants, ensuring a cleaner and more consistent naming convention.
This commit is contained in:
@@ -85,23 +85,69 @@ void DungeonCanvasViewer::DrawDungeonCanvas(int room_id) {
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
// Layer visibility controls in compact table
|
||||
// Advanced room properties (Effect, Tags, Layer Merge)
|
||||
ImGui::Separator();
|
||||
if (ImGui::BeginTable("##LayerControls", 3, ImGuiTableFlags_SizingStretchSame)) {
|
||||
if (ImGui::BeginTable("##AdvancedProperties", 3, ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Borders)) {
|
||||
ImGui::TableSetupColumn("Effect");
|
||||
ImGui::TableSetupColumn("Tag 1");
|
||||
ImGui::TableSetupColumn("Tag 2");
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
||||
// Effect dropdown
|
||||
ImGui::TableNextColumn();
|
||||
const char* effect_names[] = {"Nothing", "One", "Moving Floor", "Moving Water", "Four", "Red Flashes", "Torch Show Floor", "Ganon Room"};
|
||||
int effect_val = static_cast<int>(room.effect());
|
||||
if (ImGui::Combo("##Effect", &effect_val, effect_names, 8)) {
|
||||
room.SetEffect(static_cast<zelda3::EffectKey>(effect_val));
|
||||
}
|
||||
|
||||
// Tag 1 dropdown (abbreviated for space)
|
||||
ImGui::TableNextColumn();
|
||||
const char* tag_names[] = {"Nothing", "NW Kill", "NE Kill", "SW Kill", "SE Kill", "W Kill", "E Kill", "N Kill", "S Kill",
|
||||
"Clear Quad", "Clear Room", "NW Push", "NE Push", "SW Push", "SE Push", "W Push", "E Push",
|
||||
"N Push", "S Push", "Push Block", "Pull Lever", "Clear Level", "Switch Hold", "Switch Toggle"};
|
||||
int tag1_val = static_cast<int>(room.tag1());
|
||||
if (ImGui::Combo("##Tag1", &tag1_val, tag_names, 24)) {
|
||||
room.SetTag1(static_cast<zelda3::TagKey>(tag1_val));
|
||||
}
|
||||
|
||||
// Tag 2 dropdown
|
||||
ImGui::TableNextColumn();
|
||||
int tag2_val = static_cast<int>(room.tag2());
|
||||
if (ImGui::Combo("##Tag2", &tag2_val, tag_names, 24)) {
|
||||
room.SetTag2(static_cast<zelda3::TagKey>(tag2_val));
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
// Layer visibility and merge controls
|
||||
ImGui::Separator();
|
||||
if (ImGui::BeginTable("##LayerControls", 4, ImGuiTableFlags_SizingStretchSame)) {
|
||||
ImGui::TableNextRow();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
auto& layer_settings = GetRoomLayerSettings(room_id);
|
||||
ImGui::Checkbox("Show BG1", &layer_settings.bg1_visible);
|
||||
ImGui::Checkbox("BG1", &layer_settings.bg1_visible);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Checkbox("Show BG2", &layer_settings.bg2_visible);
|
||||
ImGui::Checkbox("BG2", &layer_settings.bg2_visible);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
// BG2 layer type dropdown
|
||||
const char* bg2_layer_types[] = {"Normal", "Trans", "Add", "Dark", "Off"};
|
||||
// BG2 layer type
|
||||
const char* bg2_types[] = {"Norm", "Trans", "Add", "Dark", "Off"};
|
||||
ImGui::SetNextItemWidth(-FLT_MIN);
|
||||
ImGui::Combo("##BG2Type", &layer_settings.bg2_layer_type, bg2_layer_types, 5);
|
||||
ImGui::Combo("##BG2Type", &layer_settings.bg2_layer_type, bg2_types, 5);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
// Layer merge type
|
||||
const char* merge_types[] = {"Off", "Parallax", "Dark", "On top", "Translucent", "Addition", "Normal", "Transparent", "Dark room"};
|
||||
int merge_val = room.layer_merging().ID;
|
||||
if (ImGui::Combo("##Merge", &merge_val, merge_types, 9)) {
|
||||
room.SetLayerMerging(zelda3::kLayerMergeTypeList[merge_val]);
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
@@ -391,20 +437,61 @@ void DungeonCanvasViewer::CalculateWallDimensions(const zelda3::RoomObject& obje
|
||||
}
|
||||
|
||||
// Room layout visualization
|
||||
void DungeonCanvasViewer::DrawRoomLayout(const zelda3::Room& room) {
|
||||
void DungeonCanvasViewer::DrawRoomLayout(zelda3::Room& room) {
|
||||
// Draw room layout structural elements (walls, floors, pits)
|
||||
// This provides visual context for where objects should be placed
|
||||
// This is the immovable BASE LAYER that defines room structure
|
||||
|
||||
const auto& layout = room.GetLayout();
|
||||
auto& layout = room.GetLayout();
|
||||
|
||||
// Get dimensions (64x64 tiles = 512x512 pixels)
|
||||
auto [width_tiles, height_tiles] = layout.GetDimensions();
|
||||
// Ensure layout is loaded (critical!)
|
||||
if (layout.GetObjects().empty()) {
|
||||
auto status = layout.LoadLayout(room.id());
|
||||
if (!status.ok()) {
|
||||
LOG_DEBUG("[DrawRoomLayout]", "Failed to load layout: %s", status.message().data());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Get layout objects by type
|
||||
// For now, draw a grid overlay to show the room structure
|
||||
// Future: Implement GetObjectsByType() in RoomLayout
|
||||
// Get structural elements by type
|
||||
auto walls = layout.GetObjectsByType(zelda3::RoomLayoutObject::Type::kWall);
|
||||
auto floors = layout.GetObjectsByType(zelda3::RoomLayoutObject::Type::kFloor);
|
||||
auto pits = layout.GetObjectsByType(zelda3::RoomLayoutObject::Type::kPit);
|
||||
auto water = layout.GetObjectsByType(zelda3::RoomLayoutObject::Type::kWater);
|
||||
auto doors = layout.GetObjectsByType(zelda3::RoomLayoutObject::Type::kDoor);
|
||||
|
||||
LOG_DEBUG("[DrawRoomLayout]", "Room layout: %dx%d tiles", width_tiles, height_tiles);
|
||||
LOG_DEBUG("[DrawRoomLayout]", "Layout elements: %zu walls, %zu floors, %zu pits, %zu water, %zu doors",
|
||||
walls.size(), floors.size(), pits.size(), water.size(), doors.size());
|
||||
|
||||
// Get canvas scale for proper sizing
|
||||
float scale = canvas_.global_scale();
|
||||
int tile_size = static_cast<int>(8 * scale); // 8x8 pixels scaled
|
||||
|
||||
// Draw walls (dark gray, semi-transparent) - immovable structure
|
||||
for (const auto& wall : walls) {
|
||||
auto [x, y] = RoomToCanvasCoordinates(wall.x(), wall.y());
|
||||
canvas_.DrawRect(x, y, tile_size, tile_size, ImVec4(0.2f, 0.2f, 0.2f, 0.5f));
|
||||
}
|
||||
|
||||
// Draw pits (orange warning) - damage zones
|
||||
for (const auto& pit : pits) {
|
||||
auto [x, y] = RoomToCanvasCoordinates(pit.x(), pit.y());
|
||||
canvas_.DrawRect(x, y, tile_size, tile_size, ImVec4(1.0f, 0.4f, 0.0f, 0.6f));
|
||||
}
|
||||
|
||||
// Draw water (blue, semi-transparent)
|
||||
for (const auto& water_tile : water) {
|
||||
auto [x, y] = RoomToCanvasCoordinates(water_tile.x(), water_tile.y());
|
||||
canvas_.DrawRect(x, y, tile_size, tile_size, ImVec4(0.2f, 0.4f, 0.8f, 0.5f));
|
||||
}
|
||||
|
||||
// Draw doors (purple) - connection points
|
||||
for (const auto& door : doors) {
|
||||
auto [x, y] = RoomToCanvasCoordinates(door.x(), door.y());
|
||||
canvas_.DrawRect(x, y, tile_size, tile_size, ImVec4(0.8f, 0.2f, 0.8f, 0.7f));
|
||||
if (scale >= 1.0f) { // Only show label if zoomed in enough
|
||||
canvas_.DrawText("DOOR", x + 2, y + 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Object visualization methods
|
||||
@@ -414,6 +501,9 @@ void DungeonCanvasViewer::DrawObjectPositionOutlines(const zelda3::Room& room) {
|
||||
|
||||
const auto& objects = room.GetTileObjects();
|
||||
|
||||
// Get canvas scale for proper sizing
|
||||
float scale = canvas_.global_scale();
|
||||
|
||||
for (const auto& obj : objects) {
|
||||
// Convert object position (tile coordinates) to canvas pixel coordinates
|
||||
auto [canvas_x, canvas_y] = RoomToCanvasCoordinates(obj.x(), obj.y());
|
||||
@@ -431,6 +521,10 @@ void DungeonCanvasViewer::DrawObjectPositionOutlines(const zelda3::Room& room) {
|
||||
width = (size_h + 1) * 8;
|
||||
height = (size_v + 1) * 8;
|
||||
|
||||
// Apply canvas scale
|
||||
width = static_cast<int>(width * scale);
|
||||
height = static_cast<int>(height * scale);
|
||||
|
||||
// Clamp to reasonable sizes
|
||||
width = std::min(width, 512);
|
||||
height = std::min(height, 512);
|
||||
|
||||
@@ -105,7 +105,7 @@ class DungeonCanvasViewer {
|
||||
void CalculateWallDimensions(const zelda3::RoomObject& object, int& width, int& height);
|
||||
|
||||
// Visualization
|
||||
void DrawRoomLayout(const zelda3::Room& room);
|
||||
void DrawRoomLayout(zelda3::Room& room); // Non-const to call LoadLayout
|
||||
void DrawObjectPositionOutlines(const zelda3::Room& room);
|
||||
|
||||
// Room graphics management
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
#include "app/gfx/graphics_optimizer.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "app/gfx/bpp_format_manager.h"
|
||||
#include "app/gfx/atlas_renderer.h"
|
||||
#include "util/log.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace gfx {
|
||||
@@ -20,181 +19,201 @@ void GraphicsOptimizer::Initialize() {
|
||||
max_quality_loss_ = 0.1f;
|
||||
min_memory_savings_ = 1024;
|
||||
performance_threshold_ = 0.05f;
|
||||
|
||||
|
||||
optimization_stats_.clear();
|
||||
optimization_cache_.clear();
|
||||
}
|
||||
|
||||
OptimizationResult GraphicsOptimizer::OptimizeSheet(const std::vector<uint8_t>& sheet_data,
|
||||
int sheet_id,
|
||||
const SnesPalette& palette,
|
||||
OptimizationStrategy strategy) {
|
||||
OptimizationResult GraphicsOptimizer::OptimizeSheet(
|
||||
const std::vector<uint8_t>& sheet_data, int sheet_id,
|
||||
const SnesPalette& palette, OptimizationStrategy strategy) {
|
||||
ScopedTimer timer("graphics_optimize_sheet");
|
||||
|
||||
|
||||
OptimizationResult result;
|
||||
|
||||
|
||||
try {
|
||||
// Analyze the sheet
|
||||
SheetOptimizationData data = AnalyzeSheet(sheet_data, sheet_id, palette);
|
||||
|
||||
|
||||
if (!data.is_convertible) {
|
||||
result.success = false;
|
||||
result.message = "Sheet is not suitable for optimization";
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// Check if optimization meets criteria
|
||||
if (!ShouldOptimize(data, strategy)) {
|
||||
result.success = false;
|
||||
result.message = "Optimization does not meet criteria";
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// Calculate potential savings
|
||||
result.memory_saved = data.current_size - data.optimized_size;
|
||||
result.performance_gain = CalculatePerformanceGain(data.current_format, data.recommended_format);
|
||||
result.quality_loss = CalculateQualityLoss(data.current_format, data.recommended_format, sheet_data);
|
||||
|
||||
result.performance_gain =
|
||||
CalculatePerformanceGain(data.current_format, data.recommended_format);
|
||||
result.quality_loss = CalculateQualityLoss(
|
||||
data.current_format, data.recommended_format, sheet_data);
|
||||
|
||||
// Check if optimization is worthwhile
|
||||
if (result.memory_saved < min_memory_savings_ &&
|
||||
if (result.memory_saved < min_memory_savings_ &&
|
||||
result.performance_gain < performance_threshold_ &&
|
||||
result.quality_loss > max_quality_loss_) {
|
||||
result.success = false;
|
||||
result.message = "Optimization benefits do not justify quality loss";
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
result.success = true;
|
||||
result.message = "Optimization recommended";
|
||||
result.recommended_formats.push_back(data.recommended_format);
|
||||
result.sheet_recommendations[sheet_id] = data.recommended_format;
|
||||
|
||||
|
||||
UpdateOptimizationStats("sheets_optimized", 1.0);
|
||||
UpdateOptimizationStats("memory_saved", static_cast<double>(result.memory_saved));
|
||||
UpdateOptimizationStats("performance_gain", static_cast<double>(result.performance_gain));
|
||||
|
||||
UpdateOptimizationStats("memory_saved",
|
||||
static_cast<double>(result.memory_saved));
|
||||
UpdateOptimizationStats("performance_gain",
|
||||
static_cast<double>(result.performance_gain));
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
result.success = false;
|
||||
result.message = "Optimization failed: " + std::string(e.what());
|
||||
SDL_Log("GraphicsOptimizer::OptimizeSheet failed: %s", e.what());
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
OptimizationResult GraphicsOptimizer::OptimizeSheets(const std::unordered_map<int, std::vector<uint8_t>>& sheets,
|
||||
const std::unordered_map<int, SnesPalette>& palettes,
|
||||
OptimizationStrategy strategy) {
|
||||
OptimizationResult GraphicsOptimizer::OptimizeSheets(
|
||||
const std::unordered_map<int, std::vector<uint8_t>>& sheets,
|
||||
const std::unordered_map<int, SnesPalette>& palettes,
|
||||
OptimizationStrategy strategy) {
|
||||
ScopedTimer timer("graphics_optimize_sheets");
|
||||
|
||||
|
||||
OptimizationResult result;
|
||||
result.success = true;
|
||||
|
||||
|
||||
size_t total_memory_saved = 0;
|
||||
float total_performance_gain = 0.0f;
|
||||
float total_quality_loss = 0.0f;
|
||||
int optimized_sheets = 0;
|
||||
|
||||
|
||||
for (const auto& [sheet_id, sheet_data] : sheets) {
|
||||
auto palette_it = palettes.find(sheet_id);
|
||||
if (palette_it == palettes.end()) {
|
||||
continue; // Skip sheets without palettes
|
||||
continue; // Skip sheets without palettes
|
||||
}
|
||||
|
||||
auto sheet_result = OptimizeSheet(sheet_data, sheet_id, palette_it->second, strategy);
|
||||
|
||||
|
||||
auto sheet_result =
|
||||
OptimizeSheet(sheet_data, sheet_id, palette_it->second, strategy);
|
||||
|
||||
if (sheet_result.success) {
|
||||
total_memory_saved += sheet_result.memory_saved;
|
||||
total_performance_gain += sheet_result.performance_gain;
|
||||
total_quality_loss += sheet_result.quality_loss;
|
||||
optimized_sheets++;
|
||||
|
||||
|
||||
// Merge recommendations
|
||||
result.recommended_formats.insert(result.recommended_formats.end(),
|
||||
sheet_result.recommended_formats.begin(),
|
||||
sheet_result.recommended_formats.end());
|
||||
result.sheet_recommendations.insert(sheet_result.sheet_recommendations.begin(),
|
||||
sheet_result.sheet_recommendations.end());
|
||||
result.recommended_formats.insert(
|
||||
result.recommended_formats.end(),
|
||||
sheet_result.recommended_formats.begin(),
|
||||
sheet_result.recommended_formats.end());
|
||||
result.sheet_recommendations.insert(
|
||||
sheet_result.sheet_recommendations.begin(),
|
||||
sheet_result.sheet_recommendations.end());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
result.memory_saved = total_memory_saved;
|
||||
result.performance_gain = optimized_sheets > 0 ? total_performance_gain / optimized_sheets : 0.0f;
|
||||
result.quality_loss = optimized_sheets > 0 ? total_quality_loss / optimized_sheets : 0.0f;
|
||||
|
||||
result.performance_gain =
|
||||
optimized_sheets > 0 ? total_performance_gain / optimized_sheets : 0.0f;
|
||||
result.quality_loss =
|
||||
optimized_sheets > 0 ? total_quality_loss / optimized_sheets : 0.0f;
|
||||
|
||||
if (optimized_sheets > 0) {
|
||||
result.message = "Optimized " + std::to_string(optimized_sheets) + " sheets";
|
||||
result.message =
|
||||
"Optimized " + std::to_string(optimized_sheets) + " sheets";
|
||||
} else {
|
||||
result.success = false;
|
||||
result.message = "No sheets could be optimized";
|
||||
}
|
||||
|
||||
|
||||
UpdateOptimizationStats("batch_optimizations", 1.0);
|
||||
UpdateOptimizationStats("total_sheets_processed", static_cast<double>(sheets.size()));
|
||||
|
||||
UpdateOptimizationStats("total_sheets_processed",
|
||||
static_cast<double>(sheets.size()));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
SheetOptimizationData GraphicsOptimizer::AnalyzeSheet(const std::vector<uint8_t>& sheet_data,
|
||||
int sheet_id,
|
||||
const SnesPalette& palette) {
|
||||
SheetOptimizationData GraphicsOptimizer::AnalyzeSheet(
|
||||
const std::vector<uint8_t>& sheet_data, int sheet_id,
|
||||
const SnesPalette& palette) {
|
||||
// Check cache first
|
||||
std::string cache_key = GenerateCacheKey(sheet_data, sheet_id);
|
||||
auto cache_it = optimization_cache_.find(cache_key);
|
||||
if (cache_it != optimization_cache_.end()) {
|
||||
return cache_it->second;
|
||||
}
|
||||
|
||||
|
||||
ScopedTimer timer("graphics_analyze_sheet");
|
||||
|
||||
|
||||
SheetOptimizationData data;
|
||||
data.sheet_id = sheet_id;
|
||||
data.current_size = sheet_data.size();
|
||||
|
||||
|
||||
// Detect current format
|
||||
data.current_format = BppFormatManager::Get().DetectFormat(sheet_data, 128, 32); // Standard sheet size
|
||||
|
||||
data.current_format = BppFormatManager::Get().DetectFormat(
|
||||
sheet_data, 128, 32); // Standard sheet size
|
||||
|
||||
// Analyze color usage
|
||||
data.colors_used = CountUsedColors(sheet_data, palette);
|
||||
|
||||
|
||||
// Determine optimal format
|
||||
data.recommended_format = DetermineOptimalFormat(sheet_data, palette, OptimizationStrategy::kBalanced);
|
||||
|
||||
data.recommended_format = DetermineOptimalFormat(
|
||||
sheet_data, palette, OptimizationStrategy::kBalanced);
|
||||
|
||||
// Calculate potential savings
|
||||
const auto& current_info = BppFormatManager::Get().GetFormatInfo(data.current_format);
|
||||
const auto& recommended_info = BppFormatManager::Get().GetFormatInfo(data.recommended_format);
|
||||
|
||||
data.optimized_size = (sheet_data.size() * recommended_info.bits_per_pixel) / current_info.bits_per_pixel;
|
||||
data.compression_ratio = static_cast<float>(data.current_size) / data.optimized_size;
|
||||
|
||||
const auto& current_info =
|
||||
BppFormatManager::Get().GetFormatInfo(data.current_format);
|
||||
const auto& recommended_info =
|
||||
BppFormatManager::Get().GetFormatInfo(data.recommended_format);
|
||||
|
||||
data.optimized_size = (sheet_data.size() * recommended_info.bits_per_pixel) /
|
||||
current_info.bits_per_pixel;
|
||||
data.compression_ratio =
|
||||
static_cast<float>(data.current_size) / data.optimized_size;
|
||||
|
||||
// Determine if conversion is beneficial
|
||||
data.is_convertible = (data.current_format != data.recommended_format) &&
|
||||
(data.colors_used <= recommended_info.max_colors) &&
|
||||
(data.compression_ratio > 1.1f); // At least 10% savings
|
||||
|
||||
data.is_convertible =
|
||||
(data.current_format != data.recommended_format) &&
|
||||
(data.colors_used <= recommended_info.max_colors) &&
|
||||
(data.compression_ratio > 1.1f); // At least 10% savings
|
||||
|
||||
data.optimization_reason = GenerateOptimizationReason(data);
|
||||
|
||||
|
||||
// Cache the result
|
||||
optimization_cache_[cache_key] = data;
|
||||
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
std::unordered_map<int, SheetOptimizationData> GraphicsOptimizer::GetOptimizationRecommendations(
|
||||
std::unordered_map<int, SheetOptimizationData>
|
||||
GraphicsOptimizer::GetOptimizationRecommendations(
|
||||
const std::unordered_map<int, std::vector<uint8_t>>& sheets,
|
||||
const std::unordered_map<int, SnesPalette>& palettes) {
|
||||
|
||||
|
||||
std::unordered_map<int, SheetOptimizationData> recommendations;
|
||||
|
||||
|
||||
for (const auto& [sheet_id, sheet_data] : sheets) {
|
||||
auto palette_it = palettes.find(sheet_id);
|
||||
if (palette_it == palettes.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
recommendations[sheet_id] = AnalyzeSheet(sheet_data, sheet_id, palette_it->second);
|
||||
|
||||
recommendations[sheet_id] =
|
||||
AnalyzeSheet(sheet_data, sheet_id, palette_it->second);
|
||||
}
|
||||
|
||||
|
||||
return recommendations;
|
||||
}
|
||||
|
||||
@@ -202,33 +221,34 @@ OptimizationResult GraphicsOptimizer::ApplyOptimizations(
|
||||
const std::unordered_map<int, SheetOptimizationData>& recommendations,
|
||||
std::unordered_map<int, std::vector<uint8_t>>& sheets,
|
||||
std::unordered_map<int, SnesPalette>& palettes) {
|
||||
|
||||
|
||||
ScopedTimer timer("graphics_apply_optimizations");
|
||||
|
||||
|
||||
OptimizationResult result;
|
||||
result.success = true;
|
||||
|
||||
|
||||
size_t total_memory_saved = 0;
|
||||
int optimized_sheets = 0;
|
||||
|
||||
|
||||
for (const auto& [sheet_id, data] : recommendations) {
|
||||
if (!data.is_convertible) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
auto sheet_it = sheets.find(sheet_id);
|
||||
if (sheet_it == sheets.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
// Convert the sheet data
|
||||
auto converted_data = BppFormatManager::Get().ConvertFormat(
|
||||
sheet_it->second, data.current_format, data.recommended_format, 128, 32);
|
||||
|
||||
sheet_it->second, data.current_format, data.recommended_format, 128,
|
||||
32);
|
||||
|
||||
// Update the sheet
|
||||
sheet_it->second = converted_data;
|
||||
|
||||
|
||||
// Optimize palette if needed
|
||||
auto palette_it = palettes.find(sheet_id);
|
||||
if (palette_it != palettes.end()) {
|
||||
@@ -236,31 +256,34 @@ OptimizationResult GraphicsOptimizer::ApplyOptimizations(
|
||||
for (int i = 0; i < data.colors_used; ++i) {
|
||||
used_colors.push_back(i);
|
||||
}
|
||||
|
||||
|
||||
palette_it->second = BppFormatManager::Get().OptimizePaletteForFormat(
|
||||
palette_it->second, data.recommended_format, used_colors);
|
||||
palette_it->second, data.recommended_format, used_colors);
|
||||
}
|
||||
|
||||
|
||||
total_memory_saved += data.current_size - data.optimized_size;
|
||||
optimized_sheets++;
|
||||
|
||||
|
||||
result.sheet_recommendations[sheet_id] = data.recommended_format;
|
||||
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
SDL_Log("Failed to optimize sheet %d: %s", sheet_id, e.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
result.memory_saved = total_memory_saved;
|
||||
result.message = "Optimized " + std::to_string(optimized_sheets) + " sheets";
|
||||
|
||||
UpdateOptimizationStats("optimizations_applied", static_cast<double>(optimized_sheets));
|
||||
UpdateOptimizationStats("total_memory_saved", static_cast<double>(total_memory_saved));
|
||||
|
||||
|
||||
UpdateOptimizationStats("optimizations_applied",
|
||||
static_cast<double>(optimized_sheets));
|
||||
UpdateOptimizationStats("total_memory_saved",
|
||||
static_cast<double>(total_memory_saved));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, double> GraphicsOptimizer::GetOptimizationStats() const {
|
||||
std::unordered_map<std::string, double>
|
||||
GraphicsOptimizer::GetOptimizationStats() const {
|
||||
return optimization_stats_;
|
||||
}
|
||||
|
||||
@@ -270,8 +293,8 @@ void GraphicsOptimizer::ClearCache() {
|
||||
}
|
||||
|
||||
void GraphicsOptimizer::SetOptimizationParameters(float max_quality_loss,
|
||||
size_t min_memory_savings,
|
||||
float performance_threshold) {
|
||||
size_t min_memory_savings,
|
||||
float performance_threshold) {
|
||||
max_quality_loss_ = max_quality_loss;
|
||||
min_memory_savings_ = min_memory_savings;
|
||||
performance_threshold_ = performance_threshold;
|
||||
@@ -279,166 +302,197 @@ void GraphicsOptimizer::SetOptimizationParameters(float max_quality_loss,
|
||||
|
||||
// Helper method implementations
|
||||
|
||||
BppFormat GraphicsOptimizer::DetermineOptimalFormat(const std::vector<uint8_t>& data,
|
||||
const SnesPalette& palette,
|
||||
OptimizationStrategy strategy) {
|
||||
BppFormat GraphicsOptimizer::DetermineOptimalFormat(
|
||||
const std::vector<uint8_t>& data, const SnesPalette& palette,
|
||||
OptimizationStrategy strategy) {
|
||||
int colors_used = CountUsedColors(data, palette);
|
||||
|
||||
|
||||
// Determine optimal format based on color usage and strategy
|
||||
switch (strategy) {
|
||||
case OptimizationStrategy::kMemoryOptimized:
|
||||
if (colors_used <= 4) return BppFormat::kBpp2;
|
||||
if (colors_used <= 8) return BppFormat::kBpp3;
|
||||
if (colors_used <= 16) return BppFormat::kBpp4;
|
||||
if (colors_used <= 4)
|
||||
return BppFormat::kBpp2;
|
||||
if (colors_used <= 8)
|
||||
return BppFormat::kBpp3;
|
||||
if (colors_used <= 16)
|
||||
return BppFormat::kBpp4;
|
||||
break;
|
||||
|
||||
|
||||
case OptimizationStrategy::kPerformanceOptimized:
|
||||
// Prefer formats that work well with atlas rendering
|
||||
if (colors_used <= 16) return BppFormat::kBpp4;
|
||||
if (colors_used <= 16)
|
||||
return BppFormat::kBpp4;
|
||||
break;
|
||||
|
||||
|
||||
case OptimizationStrategy::kQualityOptimized:
|
||||
// Only optimize if significant memory savings
|
||||
if (colors_used <= 4) return BppFormat::kBpp2;
|
||||
if (colors_used <= 4)
|
||||
return BppFormat::kBpp2;
|
||||
break;
|
||||
|
||||
|
||||
case OptimizationStrategy::kBalanced:
|
||||
if (colors_used <= 4) return BppFormat::kBpp2;
|
||||
if (colors_used <= 8) return BppFormat::kBpp3;
|
||||
if (colors_used <= 16) return BppFormat::kBpp4;
|
||||
if (colors_used <= 4)
|
||||
return BppFormat::kBpp2;
|
||||
if (colors_used <= 8)
|
||||
return BppFormat::kBpp3;
|
||||
if (colors_used <= 16)
|
||||
return BppFormat::kBpp4;
|
||||
break;
|
||||
}
|
||||
|
||||
return BppFormat::kBpp8; // Default to 8BPP
|
||||
|
||||
return BppFormat::kBpp8; // Default to 8BPP
|
||||
}
|
||||
|
||||
float GraphicsOptimizer::CalculateQualityLoss(BppFormat from_format, BppFormat to_format,
|
||||
const std::vector<uint8_t>& data) {
|
||||
if (from_format == to_format) return 0.0f;
|
||||
|
||||
float GraphicsOptimizer::CalculateQualityLoss(
|
||||
BppFormat from_format, BppFormat to_format,
|
||||
const std::vector<uint8_t>& data) {
|
||||
if (from_format == to_format)
|
||||
return 0.0f;
|
||||
|
||||
// Higher BPP to lower BPP conversions may lose quality
|
||||
if (static_cast<int>(from_format) > static_cast<int>(to_format)) {
|
||||
int bpp_diff = static_cast<int>(from_format) - static_cast<int>(to_format);
|
||||
return std::min(1.0f, static_cast<float>(bpp_diff) * 0.1f); // 10% loss per BPP level
|
||||
return std::min(
|
||||
1.0f, static_cast<float>(bpp_diff) * 0.1f); // 10% loss per BPP level
|
||||
}
|
||||
|
||||
return 0.0f; // Lower to higher BPP is lossless
|
||||
|
||||
return 0.0f; // Lower to higher BPP is lossless
|
||||
}
|
||||
|
||||
size_t GraphicsOptimizer::CalculateMemorySavings(BppFormat from_format, BppFormat to_format,
|
||||
const std::vector<uint8_t>& data) {
|
||||
if (from_format == to_format) return 0;
|
||||
|
||||
size_t GraphicsOptimizer::CalculateMemorySavings(
|
||||
BppFormat from_format, BppFormat to_format,
|
||||
const std::vector<uint8_t>& data) {
|
||||
if (from_format == to_format)
|
||||
return 0;
|
||||
|
||||
const auto& from_info = BppFormatManager::Get().GetFormatInfo(from_format);
|
||||
const auto& to_info = BppFormatManager::Get().GetFormatInfo(to_format);
|
||||
|
||||
|
||||
size_t from_size = data.size();
|
||||
size_t to_size = (from_size * to_info.bits_per_pixel) / from_info.bits_per_pixel;
|
||||
|
||||
size_t to_size =
|
||||
(from_size * to_info.bits_per_pixel) / from_info.bits_per_pixel;
|
||||
|
||||
return from_size - to_size;
|
||||
}
|
||||
|
||||
float GraphicsOptimizer::CalculatePerformanceGain(BppFormat from_format, BppFormat to_format) {
|
||||
if (from_format == to_format) return 0.0f;
|
||||
|
||||
float GraphicsOptimizer::CalculatePerformanceGain(BppFormat from_format,
|
||||
BppFormat to_format) {
|
||||
if (from_format == to_format)
|
||||
return 0.0f;
|
||||
|
||||
// Lower BPP formats generally render faster
|
||||
if (static_cast<int>(from_format) > static_cast<int>(to_format)) {
|
||||
int bpp_diff = static_cast<int>(from_format) - static_cast<int>(to_format);
|
||||
return std::min(0.5f, static_cast<float>(bpp_diff) * 0.1f); // 10% gain per BPP level
|
||||
return std::min(
|
||||
0.5f, static_cast<float>(bpp_diff) * 0.1f); // 10% gain per BPP level
|
||||
}
|
||||
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
bool GraphicsOptimizer::ShouldOptimize(const SheetOptimizationData& data, OptimizationStrategy strategy) {
|
||||
if (!data.is_convertible) return false;
|
||||
|
||||
bool GraphicsOptimizer::ShouldOptimize(const SheetOptimizationData& data,
|
||||
OptimizationStrategy strategy) {
|
||||
if (!data.is_convertible)
|
||||
return false;
|
||||
|
||||
switch (strategy) {
|
||||
case OptimizationStrategy::kMemoryOptimized:
|
||||
return data.compression_ratio > 1.2f; // At least 20% savings
|
||||
|
||||
return data.compression_ratio > 1.2f; // At least 20% savings
|
||||
|
||||
case OptimizationStrategy::kPerformanceOptimized:
|
||||
return data.compression_ratio > 1.1f; // At least 10% savings
|
||||
|
||||
return data.compression_ratio > 1.1f; // At least 10% savings
|
||||
|
||||
case OptimizationStrategy::kQualityOptimized:
|
||||
return data.compression_ratio > 1.5f; // At least 50% savings
|
||||
|
||||
return data.compression_ratio > 1.5f; // At least 50% savings
|
||||
|
||||
case OptimizationStrategy::kBalanced:
|
||||
return data.compression_ratio > 1.15f; // At least 15% savings
|
||||
return data.compression_ratio > 1.15f; // At least 15% savings
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string GraphicsOptimizer::GenerateOptimizationReason(const SheetOptimizationData& data) {
|
||||
std::string GraphicsOptimizer::GenerateOptimizationReason(
|
||||
const SheetOptimizationData& data) {
|
||||
std::ostringstream reason;
|
||||
|
||||
reason << "Convert from " << BppFormatManager::Get().GetFormatInfo(data.current_format).name
|
||||
<< " to " << BppFormatManager::Get().GetFormatInfo(data.recommended_format).name
|
||||
<< " (uses " << data.colors_used << " colors, "
|
||||
<< std::fixed << std::setprecision(1) << (data.compression_ratio - 1.0f) * 100.0f
|
||||
|
||||
reason << "Convert from "
|
||||
<< BppFormatManager::Get().GetFormatInfo(data.current_format).name
|
||||
<< " to "
|
||||
<< BppFormatManager::Get().GetFormatInfo(data.recommended_format).name
|
||||
<< " (uses " << data.colors_used << " colors, " << std::fixed
|
||||
<< std::setprecision(1) << (data.compression_ratio - 1.0f) * 100.0f
|
||||
<< "% memory savings)";
|
||||
|
||||
|
||||
return reason.str();
|
||||
}
|
||||
|
||||
int GraphicsOptimizer::CountUsedColors(const std::vector<uint8_t>& data, const SnesPalette& palette) {
|
||||
int GraphicsOptimizer::CountUsedColors(const std::vector<uint8_t>& data,
|
||||
const SnesPalette& palette) {
|
||||
std::vector<bool> used_colors(palette.size(), false);
|
||||
|
||||
|
||||
for (uint8_t pixel : data) {
|
||||
if (pixel < palette.size()) {
|
||||
used_colors[pixel] = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int count = 0;
|
||||
for (bool used : used_colors) {
|
||||
if (used) count++;
|
||||
if (used)
|
||||
count++;
|
||||
}
|
||||
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
float GraphicsOptimizer::CalculateColorEfficiency(const std::vector<uint8_t>& data, const SnesPalette& palette) {
|
||||
float GraphicsOptimizer::CalculateColorEfficiency(
|
||||
const std::vector<uint8_t>& data, const SnesPalette& palette) {
|
||||
int used_colors = CountUsedColors(data, palette);
|
||||
return static_cast<float>(used_colors) / palette.size();
|
||||
}
|
||||
|
||||
std::vector<int> GraphicsOptimizer::AnalyzeColorDistribution(const std::vector<uint8_t>& data) {
|
||||
std::vector<int> GraphicsOptimizer::AnalyzeColorDistribution(
|
||||
const std::vector<uint8_t>& data) {
|
||||
std::vector<int> distribution(256, 0);
|
||||
|
||||
|
||||
for (uint8_t pixel : data) {
|
||||
distribution[pixel]++;
|
||||
}
|
||||
|
||||
|
||||
return distribution;
|
||||
}
|
||||
|
||||
std::string GraphicsOptimizer::GenerateCacheKey(const std::vector<uint8_t>& data, int sheet_id) {
|
||||
std::string GraphicsOptimizer::GenerateCacheKey(
|
||||
const std::vector<uint8_t>& data, int sheet_id) {
|
||||
std::ostringstream key;
|
||||
key << "sheet_" << sheet_id << "_" << data.size();
|
||||
|
||||
|
||||
// Add hash of data for uniqueness
|
||||
size_t hash = 0;
|
||||
for (size_t i = 0; i < std::min(data.size(), size_t(1024)); ++i) {
|
||||
hash = hash * 31 + data[i];
|
||||
}
|
||||
key << "_" << hash;
|
||||
|
||||
|
||||
return key.str();
|
||||
}
|
||||
|
||||
void GraphicsOptimizer::UpdateOptimizationStats(const std::string& operation, double value) {
|
||||
void GraphicsOptimizer::UpdateOptimizationStats(const std::string& operation,
|
||||
double value) {
|
||||
optimization_stats_[operation] += value;
|
||||
}
|
||||
|
||||
// GraphicsOptimizationScope implementation
|
||||
|
||||
GraphicsOptimizationScope::GraphicsOptimizationScope(OptimizationStrategy strategy, int sheet_count)
|
||||
: strategy_(strategy), sheet_count_(sheet_count),
|
||||
GraphicsOptimizationScope::GraphicsOptimizationScope(
|
||||
OptimizationStrategy strategy, int sheet_count)
|
||||
: strategy_(strategy),
|
||||
sheet_count_(sheet_count),
|
||||
timer_("graphics_optimize_scope") {
|
||||
std::ostringstream op_name;
|
||||
op_name << "graphics_optimize_" << static_cast<int>(strategy) << "_" << sheet_count;
|
||||
op_name << "graphics_optimize_" << static_cast<int>(strategy) << "_"
|
||||
<< sheet_count;
|
||||
operation_name_ = op_name.str();
|
||||
}
|
||||
|
||||
@@ -446,7 +500,8 @@ GraphicsOptimizationScope::~GraphicsOptimizationScope() {
|
||||
// Timer automatically ends in destructor
|
||||
}
|
||||
|
||||
void GraphicsOptimizationScope::AddSheet(int sheet_id, size_t original_size, size_t optimized_size) {
|
||||
void GraphicsOptimizationScope::AddSheet(int sheet_id, size_t original_size,
|
||||
size_t optimized_size) {
|
||||
result_.memory_saved += (original_size - optimized_size);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,12 +3,9 @@
|
||||
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/bpp_format_manager.h"
|
||||
#include "app/gfx/atlas_renderer.h"
|
||||
#include "app/gfx/performance_profiler.h"
|
||||
|
||||
namespace yaze {
|
||||
|
||||
100
src/app/zelda3/dungeon/dungeon_rom_addresses.h
Normal file
100
src/app/zelda3/dungeon/dungeon_rom_addresses.h
Normal file
@@ -0,0 +1,100 @@
|
||||
#ifndef YAZE_APP_ZELDA3_DUNGEON_ROM_ADDRESSES_H
|
||||
#define YAZE_APP_ZELDA3_DUNGEON_ROM_ADDRESSES_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace yaze {
|
||||
namespace zelda3 {
|
||||
|
||||
// ============================================================================
|
||||
// Dungeon ROM Address Constants
|
||||
// ============================================================================
|
||||
// This file contains all ROM addresses for dungeon-related data in ALTTP.
|
||||
// Organized by category for readability and maintainability.
|
||||
|
||||
// === Room Data Pointers ===
|
||||
constexpr int kRoomObjectLayoutPointer = 0x882D; // Layout pointer table
|
||||
constexpr int kRoomObjectPointer = 0x874C; // Object data pointer (Long)
|
||||
constexpr int kRoomHeaderPointer = 0xB5DD; // Room header pointer (LONG)
|
||||
constexpr int kRoomHeaderPointerBank = 0xB5E7; // Room header bank byte
|
||||
|
||||
// === Palette Data ===
|
||||
constexpr int kDungeonsMainBgPalettePointers = 0xDEC4B; // JP Same
|
||||
constexpr int kDungeonsPalettes = 0xDD734; // Dungeon palette data
|
||||
|
||||
// === Item & Sprite Data ===
|
||||
constexpr int kRoomItemsPointers = 0xDB69; // JP 0xDB67
|
||||
constexpr int kRoomsSpritePointer = 0x4C298; // JP Same (2-byte bank 09D62E)
|
||||
constexpr int kSpriteBlocksetPointer = 0x5B57; // Sprite graphics pointer
|
||||
|
||||
// === Graphics Data ===
|
||||
constexpr int kGfxGroupsPointer = 0x6237; // Graphics group table
|
||||
constexpr int kTileAddress = 0x001B52; // Main tile graphics
|
||||
constexpr int kTileAddressFloor = 0x001B5A; // Floor tile graphics
|
||||
|
||||
// === Block Data ===
|
||||
constexpr int kBlocksLength = 0x8896; // Word value
|
||||
constexpr int kBlocksPointer1 = 0x15AFA; // Block data pointer 1
|
||||
constexpr int kBlocksPointer2 = 0x15B01; // Block data pointer 2
|
||||
constexpr int kBlocksPointer3 = 0x15B08; // Block data pointer 3
|
||||
constexpr int kBlocksPointer4 = 0x15B0F; // Block data pointer 4
|
||||
|
||||
// === Chests ===
|
||||
constexpr int kChestsLengthPointer = 0xEBF6; // Chest count pointer
|
||||
constexpr int kChestsDataPointer1 = 0xEBFB; // Chest data start
|
||||
|
||||
// === Torches ===
|
||||
constexpr int kTorchData = 0x2736A; // JP 0x2704A
|
||||
constexpr int kTorchesLengthPointer = 0x88C1; // Torch count pointer
|
||||
|
||||
// === Pits & Warps ===
|
||||
constexpr int kPitPointer = 0x394AB; // Pit/hole data
|
||||
constexpr int kPitCount = 0x394A6; // Number of pits
|
||||
|
||||
// === Doors ===
|
||||
constexpr int kDoorPointers = 0xF83C0; // Door data table
|
||||
constexpr int kDoorGfxUp = 0x4D9E; // Door graphics (up)
|
||||
constexpr int kDoorGfxDown = 0x4E06; // Door graphics (down)
|
||||
constexpr int kDoorGfxCaveExitDown = 0x4E06; // Cave exit door
|
||||
constexpr int kDoorGfxLeft = 0x4E66; // Door graphics (left)
|
||||
constexpr int kDoorGfxRight = 0x4EC6; // Door graphics (right)
|
||||
constexpr int kDoorPosUp = 0x197E; // Door position (up)
|
||||
constexpr int kDoorPosDown = 0x1996; // Door position (down)
|
||||
constexpr int kDoorPosLeft = 0x19AE; // Door position (left)
|
||||
constexpr int kDoorPosRight = 0x19C6; // Door position (right)
|
||||
|
||||
// === Sprites ===
|
||||
constexpr int kSpritesData = 0x4D8B0; // Sprite data start
|
||||
constexpr int kSpritesDataEmptyRoom = 0x4D8AE; // Empty room sprite marker
|
||||
constexpr int kSpritesEndData = 0x4EC9E; // Sprite data end
|
||||
constexpr int kDungeonSpritePointers = 0x090000; // Dungeon sprite pointer table
|
||||
|
||||
// === Messages ===
|
||||
constexpr int kMessagesIdDungeon = 0x3F61D; // Dungeon message IDs
|
||||
|
||||
// === Room Metadata ===
|
||||
constexpr int kNumberOfRooms = 296; // Total dungeon rooms (0x00-0x127)
|
||||
|
||||
// Stair objects (special handling)
|
||||
constexpr uint16_t kStairsObjects[] = {0x139, 0x138, 0x13B, 0x12E, 0x12D};
|
||||
|
||||
// === Layout Pointers (referenced in comments) ===
|
||||
// Layout00 ptr: 0x47EF04
|
||||
// Layout01 ptr: 0xAFEF04
|
||||
// Layout02 ptr: 0xF0EF04
|
||||
// Layout03 ptr: 0x4CF004
|
||||
// Layout04 ptr: 0xA8F004
|
||||
// Layout05 ptr: 0xECF004
|
||||
// Layout06 ptr: 0x48F104
|
||||
// Layout07 ptr: 0xA4F104
|
||||
|
||||
// === Notes ===
|
||||
// - Layout arrays are NOT exactly the same as rooms
|
||||
// - Object array is terminated by 0xFFFF (no layers)
|
||||
// - In normal room, 0xFFFF goes to next layer (layers 0, 1, 2)
|
||||
|
||||
} // namespace zelda3
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_ZELDA3_DUNGEON_ROM_ADDRESSES_H
|
||||
|
||||
@@ -331,17 +331,24 @@ void Room::RenderRoomGraphics() {
|
||||
// Update textures with all the data (floor + background + objects + palette)
|
||||
if (bg1_bmp.texture()) {
|
||||
// Texture exists - UPDATE it with new object data
|
||||
LOG_DEBUG("[RenderRoomGraphics]", "Queueing UPDATE for existing textures");
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, &bg1_bmp);
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, &bg2_bmp);
|
||||
} else {
|
||||
// No texture yet - CREATE it
|
||||
LOG_DEBUG("[RenderRoomGraphics]", "Queueing CREATE for new textures");
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, &bg1_bmp);
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, &bg2_bmp);
|
||||
}
|
||||
|
||||
// CRITICAL: Process texture queue immediately so objects appear!
|
||||
// Don't wait for next frame - update NOW!
|
||||
gfx::Arena::Get().ProcessTextureQueue(nullptr);
|
||||
LOG_DEBUG("[RenderRoomGraphics]", "Processed texture queue immediately");
|
||||
}
|
||||
|
||||
void Room::RenderObjectsToBackground() {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "app/rom.h"
|
||||
#include "app/gfx/background_buffer.h"
|
||||
#include "app/zelda3/dungeon/dungeon_rom_addresses.h"
|
||||
#include "app/zelda3/dungeon/room_layout.h"
|
||||
#include "app/zelda3/dungeon/room_object.h"
|
||||
#include "app/zelda3/sprite/sprite.h"
|
||||
@@ -16,72 +17,51 @@
|
||||
namespace yaze {
|
||||
namespace zelda3 {
|
||||
|
||||
// room_object_layout_pointer 0x882D
|
||||
// room_object_pointer 0x874C
|
||||
// 0x882D -> readlong() -> 2FEF04 (04EF2F -> toPC->026F2F) ->
|
||||
|
||||
// 47EF04 ; layout00 ptr
|
||||
// AFEF04 ; layout01 ptr
|
||||
// F0EF04 ; layout02 ptr
|
||||
// 4CF004 ; layout03 ptr
|
||||
// A8F004 ; layout04 ptr
|
||||
// ECF004 ; layout05 ptr
|
||||
// 48F104 ; layout06 ptr
|
||||
// A4F104 ; layout07 ptr
|
||||
// also they are not exactly the same as rooms
|
||||
// the object array is terminated by a 0xFFFF there's no layers
|
||||
// in normal room when you encounter a 0xFFFF it goes to the next layer
|
||||
|
||||
constexpr int room_object_layout_pointer = 0x882D;
|
||||
constexpr int room_object_pointer = 0x874C; // Long pointer
|
||||
|
||||
constexpr int dungeons_main_bg_palette_pointers = 0xDEC4B; // JP Same
|
||||
constexpr int dungeons_palettes = 0xDD734;
|
||||
constexpr int room_items_pointers = 0xDB69; // JP 0xDB67
|
||||
constexpr int rooms_sprite_pointer = 0x4C298; // JP Same //2byte bank 09D62E
|
||||
constexpr int kRoomHeaderPointer = 0xB5DD; // LONG
|
||||
constexpr int kRoomHeaderPointerBank = 0xB5E7; // JP Same
|
||||
constexpr int gfx_groups_pointer = 0x6237;
|
||||
constexpr int chests_length_pointer = 0xEBF6;
|
||||
constexpr int chests_data_pointer1 = 0xEBFB;
|
||||
|
||||
constexpr int messages_id_dungeon = 0x3F61D;
|
||||
|
||||
constexpr int blocks_length = 0x8896; // Word value
|
||||
constexpr int blocks_pointer1 = 0x15AFA;
|
||||
constexpr int blocks_pointer2 = 0x15B01;
|
||||
constexpr int blocks_pointer3 = 0x15B08;
|
||||
constexpr int blocks_pointer4 = 0x15B0F;
|
||||
constexpr int torch_data = 0x2736A; // JP 0x2704A
|
||||
constexpr int torches_length_pointer = 0x88C1;
|
||||
constexpr int sprite_blockset_pointer = 0x5B57;
|
||||
|
||||
constexpr int sprites_data = 0x4D8B0;
|
||||
constexpr int sprites_data_empty_room = 0x4D8AE;
|
||||
constexpr int sprites_end_data = 0x4EC9E;
|
||||
constexpr int pit_pointer = 0x394AB;
|
||||
constexpr int pit_count = 0x394A6;
|
||||
constexpr int doorPointers = 0xF83C0;
|
||||
|
||||
// doors
|
||||
constexpr int door_gfx_up = 0x4D9E;
|
||||
constexpr int door_gfx_down = 0x4E06;
|
||||
constexpr int door_gfx_cavexit_down = 0x4E06;
|
||||
constexpr int door_gfx_left = 0x4E66;
|
||||
constexpr int door_gfx_right = 0x4EC6;
|
||||
constexpr int door_pos_up = 0x197E;
|
||||
constexpr int door_pos_down = 0x1996;
|
||||
constexpr int door_pos_left = 0x19AE;
|
||||
constexpr int door_pos_right = 0x19C6;
|
||||
|
||||
constexpr int dungeon_spr_ptrs = 0x090000;
|
||||
|
||||
constexpr int NumberOfRooms = 296;
|
||||
// ROM addresses moved to dungeon_rom_addresses.h for better organization
|
||||
// Use kPrefixedNames for new code (clean naming convention)
|
||||
|
||||
// Legacy aliases for backward compatibility (gradual migration)
|
||||
constexpr int room_object_layout_pointer = kRoomObjectLayoutPointer;
|
||||
constexpr int room_object_pointer = kRoomObjectPointer;
|
||||
constexpr int dungeons_main_bg_palette_pointers = kDungeonsMainBgPalettePointers;
|
||||
constexpr int dungeons_palettes = kDungeonsPalettes;
|
||||
constexpr int room_items_pointers = kRoomItemsPointers;
|
||||
constexpr int rooms_sprite_pointer = kRoomsSpritePointer;
|
||||
constexpr int gfx_groups_pointer = kGfxGroupsPointer;
|
||||
constexpr int chests_length_pointer = kChestsLengthPointer;
|
||||
constexpr int chests_data_pointer1 = kChestsDataPointer1;
|
||||
constexpr int messages_id_dungeon = kMessagesIdDungeon;
|
||||
constexpr int blocks_length = kBlocksLength;
|
||||
constexpr int blocks_pointer1 = kBlocksPointer1;
|
||||
constexpr int blocks_pointer2 = kBlocksPointer2;
|
||||
constexpr int blocks_pointer3 = kBlocksPointer3;
|
||||
constexpr int blocks_pointer4 = kBlocksPointer4;
|
||||
constexpr int torch_data = kTorchData;
|
||||
constexpr int torches_length_pointer = kTorchesLengthPointer;
|
||||
constexpr int sprite_blockset_pointer = kSpriteBlocksetPointer;
|
||||
constexpr int sprites_data = kSpritesData;
|
||||
constexpr int sprites_data_empty_room = kSpritesDataEmptyRoom;
|
||||
constexpr int sprites_end_data = kSpritesEndData;
|
||||
constexpr int pit_pointer = kPitPointer;
|
||||
constexpr int pit_count = kPitCount;
|
||||
constexpr int doorPointers = kDoorPointers;
|
||||
constexpr int door_gfx_up = kDoorGfxUp;
|
||||
constexpr int door_gfx_down = kDoorGfxDown;
|
||||
constexpr int door_gfx_cavexit_down = kDoorGfxCaveExitDown;
|
||||
constexpr int door_gfx_left = kDoorGfxLeft;
|
||||
constexpr int door_gfx_right = kDoorGfxRight;
|
||||
constexpr int door_pos_up = kDoorPosUp;
|
||||
constexpr int door_pos_down = kDoorPosDown;
|
||||
constexpr int door_pos_left = kDoorPosLeft;
|
||||
constexpr int door_pos_right = kDoorPosRight;
|
||||
constexpr int dungeon_spr_ptrs = kDungeonSpritePointers;
|
||||
constexpr int tile_address = kTileAddress;
|
||||
constexpr int tile_address_floor = kTileAddressFloor;
|
||||
constexpr int NumberOfRooms = kNumberOfRooms;
|
||||
constexpr uint16_t stairsObjects[] = {0x139, 0x138, 0x13B, 0x12E, 0x12D};
|
||||
|
||||
constexpr int tile_address = 0x001B52;
|
||||
constexpr int tile_address_floor = 0x001B5A;
|
||||
// TODO: Gradually migrate all code to use kPrefixedNames directly
|
||||
// Then remove these legacy aliases
|
||||
|
||||
struct LayerMergeType {
|
||||
uint8_t ID;
|
||||
|
||||
Reference in New Issue
Block a user