From 7815f8cc8599f4770f88d394bccf35cb37770f75 Mon Sep 17 00:00:00 2001 From: scawful Date: Sat, 27 Sep 2025 19:42:32 -0400 Subject: [PATCH] Refactor map properties handling in Overworld Editor for improved clarity and performance - Rearranged include statements for better organization and reduced redundancy. - Enhanced the DrawSimplifiedMapSettings method by improving formatting and readability. - Streamlined the logic for handling world and map changes, ensuring more efficient updates. - Updated the DrawMapPropertiesPanel and related methods to improve layout and user interaction. - Refactored overlay handling logic to enhance maintainability and clarity in the Overworld class. --- src/app/editor/overworld/map_properties.cc | 758 +++++++++++-------- src/app/editor/overworld/overworld_editor.cc | 18 +- src/app/zelda3/overworld/overworld.cc | 541 ++++++++----- src/app/zelda3/overworld/overworld.h | 1 + src/app/zelda3/overworld/overworld_map.cc | 129 ++-- 5 files changed, 873 insertions(+), 574 deletions(-) diff --git a/src/app/editor/overworld/map_properties.cc b/src/app/editor/overworld/map_properties.cc index 110774a0..81b0c796 100644 --- a/src/app/editor/overworld/map_properties.cc +++ b/src/app/editor/overworld/map_properties.cc @@ -1,12 +1,12 @@ #include "app/editor/overworld/map_properties.h" +#include "app/editor/overworld/overworld_editor.h" +#include "app/editor/overworld/ui_constants.h" #include "app/gui/canvas.h" #include "app/gui/color.h" #include "app/gui/icons.h" #include "app/gui/input.h" #include "app/zelda3/overworld/overworld_map.h" -#include "app/editor/overworld/overworld_editor.h" -#include "app/editor/overworld/ui_constants.h" #include "imgui/imgui.h" namespace yaze { @@ -20,47 +20,51 @@ using ImGui::Text; // Using centralized UI constants -void MapPropertiesSystem::DrawSimplifiedMapSettings(int& current_world, int& current_map, - bool& current_map_lock, bool& show_map_properties_panel, - bool& show_custom_bg_color_editor, bool& show_overlay_editor, - bool& show_overlay_preview, int& game_state, int& current_mode) { +void MapPropertiesSystem::DrawSimplifiedMapSettings( + int& current_world, int& current_map, bool& current_map_lock, + bool& show_map_properties_panel, bool& show_custom_bg_color_editor, + bool& show_overlay_editor, bool& show_overlay_preview, int& game_state, + int& current_mode) { // Enhanced settings table with popup buttons for quick access and integrated toolset - if (BeginTable("SimplifiedMapSettings", 9, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit, ImVec2(0, 0), -1)) { - ImGui::TableSetupColumn("World", ImGuiTableColumnFlags_WidthFixed, kTableColumnWorld); - ImGui::TableSetupColumn("Map", ImGuiTableColumnFlags_WidthFixed, kTableColumnMap); - ImGui::TableSetupColumn("Area Size", ImGuiTableColumnFlags_WidthFixed, kTableColumnAreaSize); - ImGui::TableSetupColumn("Lock", ImGuiTableColumnFlags_WidthFixed, kTableColumnLock); - ImGui::TableSetupColumn("Graphics", ImGuiTableColumnFlags_WidthFixed, kTableColumnGraphics); - ImGui::TableSetupColumn("Palettes", ImGuiTableColumnFlags_WidthFixed, kTableColumnPalettes); - ImGui::TableSetupColumn("Properties", ImGuiTableColumnFlags_WidthFixed, kTableColumnProperties); - ImGui::TableSetupColumn("View", ImGuiTableColumnFlags_WidthFixed, kTableColumnView); - ImGui::TableSetupColumn("Quick", ImGuiTableColumnFlags_WidthFixed, kTableColumnQuick); + if (BeginTable("SimplifiedMapSettings", 9, + ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit, + ImVec2(0, 0), -1)) { + ImGui::TableSetupColumn("World", ImGuiTableColumnFlags_WidthFixed, + kTableColumnWorld); + ImGui::TableSetupColumn("Map", ImGuiTableColumnFlags_WidthFixed, + kTableColumnMap); + ImGui::TableSetupColumn("Area Size", ImGuiTableColumnFlags_WidthFixed, + kTableColumnAreaSize); + ImGui::TableSetupColumn("Lock", ImGuiTableColumnFlags_WidthFixed, + kTableColumnLock); + ImGui::TableSetupColumn("Graphics", ImGuiTableColumnFlags_WidthFixed, + kTableColumnGraphics); + ImGui::TableSetupColumn("Palettes", ImGuiTableColumnFlags_WidthFixed, + kTableColumnPalettes); + ImGui::TableSetupColumn("Properties", ImGuiTableColumnFlags_WidthFixed, + kTableColumnProperties); + ImGui::TableSetupColumn("View", ImGuiTableColumnFlags_WidthFixed, + kTableColumnView); + ImGui::TableSetupColumn("Quick", ImGuiTableColumnFlags_WidthFixed, + kTableColumnQuick); TableNextColumn(); ImGui::SetNextItemWidth(kComboWorldWidth); - if (ImGui::Combo("##world", ¤t_world, kWorldNames, 3)) { - // World changed, update current map if needed - if (current_map >= 0x40 && current_world == 0) { - current_map -= 0x40; - } else if (current_map < 0x40 && current_world == 1) { - current_map += 0x40; - } else if (current_map < 0x80 && current_world == 2) { - current_map += 0x80; - } else if (current_map >= 0x80 && current_world != 2) { - current_map -= 0x80; - } - } + ImGui::Combo("##world", ¤t_world, kWorldNames, 3); TableNextColumn(); ImGui::Text("%d (0x%02X)", current_map, current_map); TableNextColumn(); - static uint8_t asm_version = (*rom_)[zelda3::OverworldCustomASMHasBeenApplied]; + static uint8_t asm_version = + (*rom_)[zelda3::OverworldCustomASMHasBeenApplied]; if (asm_version != 0xFF) { - int current_area_size = static_cast(overworld_->overworld_map(current_map)->area_size()); + int current_area_size = + static_cast(overworld_->overworld_map(current_map)->area_size()); ImGui::SetNextItemWidth(kComboAreaSizeWidth); if (ImGui::Combo("##AreaSize", ¤t_area_size, kAreaSizeNames, 4)) { - overworld_->mutable_overworld_map(current_map)->SetAreaSize(static_cast(current_area_size)); + overworld_->mutable_overworld_map(current_map) + ->SetAreaSize(static_cast(current_area_size)); RefreshOverworldMap(); } } else { @@ -68,7 +72,8 @@ void MapPropertiesSystem::DrawSimplifiedMapSettings(int& current_world, int& cur } TableNextColumn(); - if (ImGui::Button(current_map_lock ? ICON_MD_LOCK : ICON_MD_LOCK_OPEN, ImVec2(40, 0))) { + if (ImGui::Button(current_map_lock ? ICON_MD_LOCK : ICON_MD_LOCK_OPEN, + ImVec2(40, 0))) { current_map_lock = !current_map_lock; } HOVER_HINT(current_map_lock ? "Unlock Map" : "Lock Map"); @@ -92,7 +97,8 @@ void MapPropertiesSystem::DrawSimplifiedMapSettings(int& current_world, int& cur ImGui::OpenPopup("PropertiesPopup"); } HOVER_HINT("Map Properties & Overlays"); - DrawPropertiesPopup(current_map, show_map_properties_panel, show_overlay_preview, game_state); + DrawPropertiesPopup(current_map, show_map_properties_panel, + show_overlay_preview, game_state); TableNextColumn(); // View Controls @@ -114,7 +120,8 @@ void MapPropertiesSystem::DrawSimplifiedMapSettings(int& current_world, int& cur } } -void MapPropertiesSystem::DrawMapPropertiesPanel(int current_map, bool& show_map_properties_panel) { +void MapPropertiesSystem::DrawMapPropertiesPanel( + int current_map, bool& show_map_properties_panel) { if (!overworld_->is_loaded()) { Text("No overworld loaded"); return; @@ -124,54 +131,58 @@ void MapPropertiesSystem::DrawMapPropertiesPanel(int current_map, bool& show_map ImGui::BeginGroup(); Text("Current Map: %d (0x%02X)", current_map, current_map); ImGui::EndGroup(); - + Separator(); - + // Create tabs for different property categories - if (ImGui::BeginTabBar("MapPropertiesTabs", ImGuiTabBarFlags_FittingPolicyScroll)) { - + if (ImGui::BeginTabBar("MapPropertiesTabs", + ImGuiTabBarFlags_FittingPolicyScroll)) { + // Basic Properties Tab if (ImGui::BeginTabItem("Basic Properties")) { DrawBasicPropertiesTab(current_map); ImGui::EndTabItem(); } - + // Sprite Properties Tab if (ImGui::BeginTabItem("Sprite Properties")) { DrawSpritePropertiesTab(current_map); ImGui::EndTabItem(); } - + // Custom Overworld Features Tab - static uint8_t asm_version = (*rom_)[zelda3::OverworldCustomASMHasBeenApplied]; + static uint8_t asm_version = + (*rom_)[zelda3::OverworldCustomASMHasBeenApplied]; if (asm_version != 0xFF && ImGui::BeginTabItem("Custom Features")) { DrawCustomFeaturesTab(current_map); ImGui::EndTabItem(); } - + // Tile Graphics Tab if (ImGui::BeginTabItem("Tile Graphics")) { DrawTileGraphicsTab(current_map); ImGui::EndTabItem(); } - + // Music Tab if (ImGui::BeginTabItem("Music")) { DrawMusicTab(current_map); ImGui::EndTabItem(); } - + ImGui::EndTabBar(); } } -void MapPropertiesSystem::DrawCustomBackgroundColorEditor(int current_map, bool& show_custom_bg_color_editor) { +void MapPropertiesSystem::DrawCustomBackgroundColorEditor( + int current_map, bool& show_custom_bg_color_editor) { if (!overworld_->is_loaded()) { Text("No overworld loaded"); return; } - static uint8_t asm_version = (*rom_)[zelda3::OverworldCustomASMHasBeenApplied]; + static uint8_t asm_version = + (*rom_)[zelda3::OverworldCustomASMHasBeenApplied]; if (asm_version < 2) { Text("Custom background colors require ZSCustomOverworld v2+"); return; @@ -182,42 +193,50 @@ void MapPropertiesSystem::DrawCustomBackgroundColorEditor(int current_map, bool& // Enable/disable area-specific background color static bool use_area_specific_bg_color = false; - if (ImGui::Checkbox("Use Area-Specific Background Color", &use_area_specific_bg_color)) { + if (ImGui::Checkbox("Use Area-Specific Background Color", + &use_area_specific_bg_color)) { // Update ROM data - (*rom_)[zelda3::OverworldCustomAreaSpecificBGEnabled] = use_area_specific_bg_color ? 1 : 0; + (*rom_)[zelda3::OverworldCustomAreaSpecificBGEnabled] = + use_area_specific_bg_color ? 1 : 0; } if (use_area_specific_bg_color) { // Get current color - uint16_t current_color = overworld_->overworld_map(current_map)->area_specific_bg_color(); + uint16_t current_color = + overworld_->overworld_map(current_map)->area_specific_bg_color(); gfx::SnesColor snes_color(current_color); - + // Convert to ImVec4 for color picker ImVec4 color_vec = gui::ConvertSnesColorToImVec4(snes_color); - - if (ImGui::ColorPicker4("Background Color", (float*)&color_vec, - ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHex)) { + + if (ImGui::ColorPicker4( + "Background Color", (float*)&color_vec, + ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHex)) { // Convert back to SNES color and update gfx::SnesColor new_snes_color = gui::ConvertImVec4ToSnesColor(color_vec); - overworld_->mutable_overworld_map(current_map)->set_area_specific_bg_color(new_snes_color.snes()); - + overworld_->mutable_overworld_map(current_map) + ->set_area_specific_bg_color(new_snes_color.snes()); + // Update ROM - int rom_address = zelda3::OverworldCustomAreaSpecificBGPalette + (current_map * 2); + int rom_address = + zelda3::OverworldCustomAreaSpecificBGPalette + (current_map * 2); (*rom_)[rom_address] = new_snes_color.snes() & 0xFF; (*rom_)[rom_address + 1] = (new_snes_color.snes() >> 8) & 0xFF; } - + Text("SNES Color: 0x%04X", current_color); } } -void MapPropertiesSystem::DrawOverlayEditor(int current_map, bool& show_overlay_editor) { +void MapPropertiesSystem::DrawOverlayEditor(int current_map, + bool& show_overlay_editor) { if (!overworld_->is_loaded()) { Text("No overworld loaded"); return; } - static uint8_t asm_version = (*rom_)[zelda3::OverworldCustomASMHasBeenApplied]; + static uint8_t asm_version = + (*rom_)[zelda3::OverworldCustomASMHasBeenApplied]; if (asm_version < 1) { Text("Subscreen overlays require ZSCustomOverworld v1+"); return; @@ -230,20 +249,25 @@ void MapPropertiesSystem::DrawOverlayEditor(int current_map, bool& show_overlay_ static bool use_subscreen_overlay = false; if (ImGui::Checkbox("Use Subscreen Overlay", &use_subscreen_overlay)) { // Update ROM data - (*rom_)[zelda3::OverworldCustomSubscreenOverlayEnabled] = use_subscreen_overlay ? 1 : 0; + (*rom_)[zelda3::OverworldCustomSubscreenOverlayEnabled] = + use_subscreen_overlay ? 1 : 0; } if (use_subscreen_overlay) { - uint16_t current_overlay = overworld_->overworld_map(current_map)->subscreen_overlay(); - if (gui::InputHexWord("Overlay ID", ¤t_overlay, kInputFieldSize + 20)) { - overworld_->mutable_overworld_map(current_map)->set_subscreen_overlay(current_overlay); - + uint16_t current_overlay = + overworld_->overworld_map(current_map)->subscreen_overlay(); + if (gui::InputHexWord("Overlay ID", ¤t_overlay, + kInputFieldSize + 20)) { + overworld_->mutable_overworld_map(current_map) + ->set_subscreen_overlay(current_overlay); + // Update ROM - int rom_address = zelda3::OverworldCustomSubscreenOverlayArray + (current_map * 2); + int rom_address = + zelda3::OverworldCustomSubscreenOverlayArray + (current_map * 2); (*rom_)[rom_address] = current_overlay & 0xFF; (*rom_)[rom_address + 1] = (current_overlay >> 8) & 0xFF; } - + Text("Common overlay IDs:"); Text("0x0000 = None"); Text("0x0001 = Map overlay"); @@ -251,12 +275,13 @@ void MapPropertiesSystem::DrawOverlayEditor(int current_map, bool& show_overlay_ } } -void MapPropertiesSystem::SetupCanvasContextMenu(gui::Canvas& canvas, int current_map, bool current_map_lock, - bool& show_map_properties_panel, bool& show_custom_bg_color_editor, - bool& show_overlay_editor) { +void MapPropertiesSystem::SetupCanvasContextMenu( + gui::Canvas& canvas, int current_map, bool current_map_lock, + bool& show_map_properties_panel, bool& show_custom_bg_color_editor, + bool& show_overlay_editor) { // Clear any existing context menu items canvas.ClearContextMenuItems(); - + // Add overworld-specific context menu items gui::Canvas::ContextMenuItem lock_item; lock_item.label = current_map_lock ? "Unlock Map" : "Lock to This Map"; @@ -264,7 +289,7 @@ void MapPropertiesSystem::SetupCanvasContextMenu(gui::Canvas& canvas, int curren current_map_lock = !current_map_lock; }; canvas.AddContextMenuItem(lock_item); - + // Map Properties gui::Canvas::ContextMenuItem properties_item; properties_item.label = "Map Properties"; @@ -272,9 +297,10 @@ void MapPropertiesSystem::SetupCanvasContextMenu(gui::Canvas& canvas, int curren show_map_properties_panel = true; }; canvas.AddContextMenuItem(properties_item); - + // Custom overworld features (only show if v3+) - static uint8_t asm_version = (*rom_)[zelda3::OverworldCustomASMHasBeenApplied]; + static uint8_t asm_version = + (*rom_)[zelda3::OverworldCustomASMHasBeenApplied]; if (asm_version >= 3 && asm_version != 0xFF) { // Custom Background Color gui::Canvas::ContextMenuItem bg_color_item; @@ -283,7 +309,7 @@ void MapPropertiesSystem::SetupCanvasContextMenu(gui::Canvas& canvas, int curren show_custom_bg_color_editor = true; }; canvas.AddContextMenuItem(bg_color_item); - + // Overlay Settings gui::Canvas::ContextMenuItem overlay_item; overlay_item.label = "Overlay Settings"; @@ -292,7 +318,7 @@ void MapPropertiesSystem::SetupCanvasContextMenu(gui::Canvas& canvas, int curren }; canvas.AddContextMenuItem(overlay_item); } - + // Canvas controls gui::Canvas::ContextMenuItem reset_pos_item; reset_pos_item.label = "Reset Canvas Position"; @@ -300,7 +326,7 @@ void MapPropertiesSystem::SetupCanvasContextMenu(gui::Canvas& canvas, int curren canvas.set_scrolling(ImVec2(0, 0)); }; canvas.AddContextMenuItem(reset_pos_item); - + gui::Canvas::ContextMenuItem zoom_fit_item; zoom_fit_item.label = "Zoom to Fit"; zoom_fit_item.callback = [&canvas]() { @@ -313,115 +339,137 @@ void MapPropertiesSystem::SetupCanvasContextMenu(gui::Canvas& canvas, int curren // Private method implementations void MapPropertiesSystem::DrawGraphicsPopup(int current_map, int game_state) { if (ImGui::BeginPopup("GraphicsPopup")) { - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(kCompactItemSpacing, kCompactFramePadding)); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(kCompactItemSpacing, kCompactFramePadding)); - + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, + ImVec2(kCompactItemSpacing, kCompactFramePadding)); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, + ImVec2(kCompactItemSpacing, kCompactFramePadding)); + ImGui::Text("Graphics Settings"); ImGui::Separator(); - - if (gui::InputHexByteCustom("Area Graphics", - overworld_->mutable_overworld_map(current_map)->mutable_area_graphics(), + + if (gui::InputHexByteCustom("Area Graphics", + overworld_->mutable_overworld_map(current_map) + ->mutable_area_graphics(), kHexByteInputWidth)) { RefreshMapProperties(); RefreshOverworldMap(); } - - if (gui::InputHexByteCustom(absl::StrFormat("Sprite GFX (%s)", kGameStateNames[game_state]).c_str(), - overworld_->mutable_overworld_map(current_map)->mutable_sprite_graphics(game_state), - kHexByteInputWidth)) { + + if (gui::InputHexByteCustom( + absl::StrFormat("Sprite GFX (%s)", kGameStateNames[game_state]) + .c_str(), + overworld_->mutable_overworld_map(current_map) + ->mutable_sprite_graphics(game_state), + kHexByteInputWidth)) { RefreshMapProperties(); RefreshOverworldMap(); } - - static uint8_t asm_version = (*rom_)[zelda3::OverworldCustomASMHasBeenApplied]; + + static uint8_t asm_version = + (*rom_)[zelda3::OverworldCustomASMHasBeenApplied]; if (asm_version >= 3) { - if (gui::InputHexByte("Animated GFX", - overworld_->mutable_overworld_map(current_map)->mutable_animated_gfx(), + if (gui::InputHexByte("Animated GFX", + overworld_->mutable_overworld_map(current_map) + ->mutable_animated_gfx(), kInputFieldSize)) { RefreshMapProperties(); RefreshOverworldMap(); } } - + ImGui::Separator(); ImGui::Text("Custom Tile Graphics (8 sheets):"); - + // Show the 8 custom graphics IDs in a more accessible way for (int i = 0; i < 8; i++) { std::string label = absl::StrFormat("Sheet %d", i); if (gui::InputHexByte(label.c_str(), - overworld_->mutable_overworld_map(current_map)->mutable_custom_tileset(i), + overworld_->mutable_overworld_map(current_map) + ->mutable_custom_tileset(i), 80.f)) { RefreshMapProperties(); RefreshOverworldMap(); } } - + ImGui::PopStyleVar(2); // Pop the 2 style variables we pushed ImGui::EndPopup(); } } -void MapPropertiesSystem::DrawPalettesPopup(int current_map, int game_state, bool& show_custom_bg_color_editor) { +void MapPropertiesSystem::DrawPalettesPopup(int current_map, int game_state, + bool& show_custom_bg_color_editor) { if (ImGui::BeginPopup("PalettesPopup")) { - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(kCompactItemSpacing, kCompactFramePadding)); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(kCompactItemSpacing, kCompactFramePadding)); - + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, + ImVec2(kCompactItemSpacing, kCompactFramePadding)); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, + ImVec2(kCompactItemSpacing, kCompactFramePadding)); + ImGui::Text("Palette Settings"); ImGui::Separator(); - - if (gui::InputHexByteCustom("Area Palette", - overworld_->mutable_overworld_map(current_map)->mutable_area_palette(), + + if (gui::InputHexByteCustom("Area Palette", + overworld_->mutable_overworld_map(current_map) + ->mutable_area_palette(), kHexByteInputWidth)) { RefreshMapProperties(); auto status = RefreshMapPalette(); RefreshOverworldMap(); } - - static uint8_t asm_version = (*rom_)[zelda3::OverworldCustomASMHasBeenApplied]; + + static uint8_t asm_version = + (*rom_)[zelda3::OverworldCustomASMHasBeenApplied]; if (asm_version >= 2) { - if (gui::InputHexByteCustom("Main Palette", - overworld_->mutable_overworld_map(current_map)->mutable_main_palette(), + if (gui::InputHexByteCustom("Main Palette", + overworld_->mutable_overworld_map(current_map) + ->mutable_main_palette(), kHexByteInputWidth)) { RefreshMapProperties(); auto status = RefreshMapPalette(); RefreshOverworldMap(); } } - - if (gui::InputHexByteCustom(absl::StrFormat("Sprite Palette (%s)", kGameStateNames[game_state]).c_str(), - overworld_->mutable_overworld_map(current_map)->mutable_sprite_palette(game_state), - kHexByteInputWidth)) { + + if (gui::InputHexByteCustom( + absl::StrFormat("Sprite Palette (%s)", kGameStateNames[game_state]) + .c_str(), + overworld_->mutable_overworld_map(current_map) + ->mutable_sprite_palette(game_state), + kHexByteInputWidth)) { RefreshMapProperties(); RefreshOverworldMap(); } - + ImGui::Separator(); if (ImGui::Button("Background Color")) { show_custom_bg_color_editor = !show_custom_bg_color_editor; } - + ImGui::PopStyleVar(2); // Pop the 2 style variables we pushed ImGui::EndPopup(); } } - -void MapPropertiesSystem::DrawPropertiesPopup(int current_map, bool& show_map_properties_panel, - bool& show_overlay_preview, int& game_state) { +void MapPropertiesSystem::DrawPropertiesPopup(int current_map, + bool& show_map_properties_panel, + bool& show_overlay_preview, + int& game_state) { if (ImGui::BeginPopup("PropertiesPopup")) { - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(kCompactItemSpacing, kCompactFramePadding)); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(kCompactItemSpacing, kCompactFramePadding)); - + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, + ImVec2(kCompactItemSpacing, kCompactFramePadding)); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, + ImVec2(kCompactItemSpacing, kCompactFramePadding)); + ImGui::Text("Map Properties"); ImGui::Separator(); // Basic Map Properties Section ImGui::Text("Basic Properties"); ImGui::Separator(); - + if (gui::InputHexWordCustom("Message ID", - overworld_->mutable_overworld_map(current_map)->mutable_message_id(), + overworld_->mutable_overworld_map(current_map) + ->mutable_message_id(), kHexWordInputWidth)) { RefreshMapProperties(); RefreshOverworldMap(); @@ -437,13 +485,16 @@ void MapPropertiesSystem::DrawPropertiesPopup(int current_map, bool& show_map_pr ImGui::Separator(); ImGui::Text("Area Configuration"); ImGui::Separator(); - - static uint8_t asm_version = (*rom_)[zelda3::OverworldCustomASMHasBeenApplied]; + + static uint8_t asm_version = + (*rom_)[zelda3::OverworldCustomASMHasBeenApplied]; if (asm_version != 0xFF) { - int current_area_size = static_cast(overworld_->overworld_map(current_map)->area_size()); + int current_area_size = + static_cast(overworld_->overworld_map(current_map)->area_size()); ImGui::SetNextItemWidth(kComboAreaSizeWidth); if (ImGui::Combo("Area Size", ¤t_area_size, kAreaSizeNames, 4)) { - overworld_->mutable_overworld_map(current_map)->SetAreaSize(static_cast(current_area_size)); + overworld_->mutable_overworld_map(current_map) + ->SetAreaSize(static_cast(current_area_size)); RefreshOverworldMap(); } } else { @@ -465,7 +516,7 @@ void MapPropertiesSystem::DrawPropertiesPopup(int current_map, bool& show_map_pr ImGui::Separator(); ImGui::Text("Visual Effects"); ImGui::Separator(); - + DrawMosaicControls(current_map); DrawOverlayControls(current_map, show_overlay_preview); @@ -473,8 +524,9 @@ void MapPropertiesSystem::DrawPropertiesPopup(int current_map, bool& show_map_pr ImGui::Separator(); ImGui::Text("Advanced Options"); ImGui::Separator(); - - if (ImGui::Button("Full Properties Panel", ImVec2(kLargeButtonWidth + 50, 0))) { + + if (ImGui::Button("Full Properties Panel", + ImVec2(kLargeButtonWidth + 50, 0))) { show_map_properties_panel = true; ImGui::CloseCurrentPopup(); } @@ -486,94 +538,113 @@ void MapPropertiesSystem::DrawPropertiesPopup(int current_map, bool& show_map_pr } void MapPropertiesSystem::DrawBasicPropertiesTab(int current_map) { - if (BeginTable("BasicProperties", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit)) { + if (BeginTable("BasicProperties", 2, + ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit)) { ImGui::TableSetupColumn("Property", ImGuiTableColumnFlags_WidthFixed, 150); ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch); - - TableNextColumn(); ImGui::Text("Area Graphics"); + TableNextColumn(); - if (gui::InputHexByte("##AreaGfx", - overworld_->mutable_overworld_map(current_map)->mutable_area_graphics(), + ImGui::Text("Area Graphics"); + TableNextColumn(); + if (gui::InputHexByte("##AreaGfx", + overworld_->mutable_overworld_map(current_map) + ->mutable_area_graphics(), kInputFieldSize)) { RefreshMapProperties(); RefreshOverworldMap(); } - - TableNextColumn(); ImGui::Text("Area Palette"); + TableNextColumn(); - if (gui::InputHexByte("##AreaPal", - overworld_->mutable_overworld_map(current_map)->mutable_area_palette(), + ImGui::Text("Area Palette"); + TableNextColumn(); + if (gui::InputHexByte("##AreaPal", + overworld_->mutable_overworld_map(current_map) + ->mutable_area_palette(), kInputFieldSize)) { RefreshMapProperties(); auto status = RefreshMapPalette(); RefreshOverworldMap(); } - - TableNextColumn(); ImGui::Text("Message ID"); + TableNextColumn(); - if (gui::InputHexWord("##MsgId", - overworld_->mutable_overworld_map(current_map)->mutable_message_id(), + ImGui::Text("Message ID"); + TableNextColumn(); + if (gui::InputHexWord("##MsgId", + overworld_->mutable_overworld_map(current_map) + ->mutable_message_id(), kInputFieldSize + 20)) { RefreshMapProperties(); RefreshOverworldMap(); } - - TableNextColumn(); ImGui::Text("Mosaic Effect"); + TableNextColumn(); - if (ImGui::Checkbox("##mosaic", - overworld_->mutable_overworld_map(current_map)->mutable_mosaic())) { + ImGui::Text("Mosaic Effect"); + TableNextColumn(); + if (ImGui::Checkbox( + "##mosaic", + overworld_->mutable_overworld_map(current_map)->mutable_mosaic())) { RefreshMapProperties(); RefreshOverworldMap(); } HOVER_HINT("Enable Mosaic effect for the current map"); - + // Add music editing controls - TableNextColumn(); ImGui::Text("Music (Beginning)"); TableNextColumn(); - if (gui::InputHexByte("##Music0", - overworld_->mutable_overworld_map(current_map)->mutable_area_music(0), + ImGui::Text("Music (Beginning)"); + TableNextColumn(); + if (gui::InputHexByte("##Music0", + overworld_->mutable_overworld_map(current_map) + ->mutable_area_music(0), kInputFieldSize)) { RefreshMapProperties(); } HOVER_HINT("Music track for game beginning state"); - - TableNextColumn(); ImGui::Text("Music (Zelda)"); + TableNextColumn(); - if (gui::InputHexByte("##Music1", - overworld_->mutable_overworld_map(current_map)->mutable_area_music(1), + ImGui::Text("Music (Zelda)"); + TableNextColumn(); + if (gui::InputHexByte("##Music1", + overworld_->mutable_overworld_map(current_map) + ->mutable_area_music(1), kInputFieldSize)) { RefreshMapProperties(); } HOVER_HINT("Music track for Zelda rescued state"); - - TableNextColumn(); ImGui::Text("Music (Master Sword)"); + TableNextColumn(); - if (gui::InputHexByte("##Music2", - overworld_->mutable_overworld_map(current_map)->mutable_area_music(2), + ImGui::Text("Music (Master Sword)"); + TableNextColumn(); + if (gui::InputHexByte("##Music2", + overworld_->mutable_overworld_map(current_map) + ->mutable_area_music(2), kInputFieldSize)) { RefreshMapProperties(); } HOVER_HINT("Music track for Master Sword obtained state"); - - TableNextColumn(); ImGui::Text("Music (Agahnim)"); + TableNextColumn(); - if (gui::InputHexByte("##Music3", - overworld_->mutable_overworld_map(current_map)->mutable_area_music(3), + ImGui::Text("Music (Agahnim)"); + TableNextColumn(); + if (gui::InputHexByte("##Music3", + overworld_->mutable_overworld_map(current_map) + ->mutable_area_music(3), kInputFieldSize)) { RefreshMapProperties(); } HOVER_HINT("Music track for Agahnim defeated state"); - + ImGui::EndTable(); } } void MapPropertiesSystem::DrawSpritePropertiesTab(int current_map) { - if (BeginTable("SpriteProperties", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit)) { + if (BeginTable("SpriteProperties", 2, + ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit)) { ImGui::TableSetupColumn("Property", ImGuiTableColumnFlags_WidthFixed, 150); ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch); - - TableNextColumn(); ImGui::Text("Game State"); + + TableNextColumn(); + ImGui::Text("Game State"); TableNextColumn(); static int game_state = 0; ImGui::SetNextItemWidth(100.f); @@ -581,95 +652,115 @@ void MapPropertiesSystem::DrawSpritePropertiesTab(int current_map) { RefreshMapProperties(); RefreshOverworldMap(); } - - TableNextColumn(); ImGui::Text("Sprite Graphics 1"); + TableNextColumn(); - if (gui::InputHexByte("##SprGfx1", - overworld_->mutable_overworld_map(current_map)->mutable_sprite_graphics(1), + ImGui::Text("Sprite Graphics 1"); + TableNextColumn(); + if (gui::InputHexByte("##SprGfx1", + overworld_->mutable_overworld_map(current_map) + ->mutable_sprite_graphics(1), kInputFieldSize)) { RefreshMapProperties(); RefreshOverworldMap(); } - - TableNextColumn(); ImGui::Text("Sprite Graphics 2"); + TableNextColumn(); - if (gui::InputHexByte("##SprGfx2", - overworld_->mutable_overworld_map(current_map)->mutable_sprite_graphics(2), + ImGui::Text("Sprite Graphics 2"); + TableNextColumn(); + if (gui::InputHexByte("##SprGfx2", + overworld_->mutable_overworld_map(current_map) + ->mutable_sprite_graphics(2), kInputFieldSize)) { RefreshMapProperties(); RefreshOverworldMap(); } - - TableNextColumn(); ImGui::Text("Sprite Palette 1"); + TableNextColumn(); - if (gui::InputHexByte("##SprPal1", - overworld_->mutable_overworld_map(current_map)->mutable_sprite_palette(1), + ImGui::Text("Sprite Palette 1"); + TableNextColumn(); + if (gui::InputHexByte("##SprPal1", + overworld_->mutable_overworld_map(current_map) + ->mutable_sprite_palette(1), kInputFieldSize)) { RefreshMapProperties(); RefreshOverworldMap(); } - - TableNextColumn(); ImGui::Text("Sprite Palette 2"); + TableNextColumn(); - if (gui::InputHexByte("##SprPal2", - overworld_->mutable_overworld_map(current_map)->mutable_sprite_palette(2), + ImGui::Text("Sprite Palette 2"); + TableNextColumn(); + if (gui::InputHexByte("##SprPal2", + overworld_->mutable_overworld_map(current_map) + ->mutable_sprite_palette(2), kInputFieldSize)) { RefreshMapProperties(); RefreshOverworldMap(); } - + ImGui::EndTable(); } } void MapPropertiesSystem::DrawCustomFeaturesTab(int current_map) { - if (BeginTable("CustomFeatures", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit)) { + if (BeginTable("CustomFeatures", 2, + ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit)) { ImGui::TableSetupColumn("Property", ImGuiTableColumnFlags_WidthFixed, 150); ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch); - - TableNextColumn(); ImGui::Text("Area Size"); + TableNextColumn(); - static const char *area_size_names[] = {"Small (1x1)", "Large (2x2)", "Wide (2x1)", "Tall (1x2)"}; - int current_area_size = static_cast(overworld_->overworld_map(current_map)->area_size()); + ImGui::Text("Area Size"); + TableNextColumn(); + static const char* area_size_names[] = {"Small (1x1)", "Large (2x2)", + "Wide (2x1)", "Tall (1x2)"}; + int current_area_size = + static_cast(overworld_->overworld_map(current_map)->area_size()); ImGui::SetNextItemWidth(120.f); if (ImGui::Combo("##AreaSize", ¤t_area_size, area_size_names, 4)) { - overworld_->mutable_overworld_map(current_map)->SetAreaSize(static_cast(current_area_size)); + overworld_->mutable_overworld_map(current_map) + ->SetAreaSize(static_cast(current_area_size)); RefreshOverworldMap(); } - - static uint8_t asm_version = (*rom_)[zelda3::OverworldCustomASMHasBeenApplied]; + + static uint8_t asm_version = + (*rom_)[zelda3::OverworldCustomASMHasBeenApplied]; if (asm_version >= 2) { - TableNextColumn(); ImGui::Text("Main Palette"); TableNextColumn(); - if (gui::InputHexByte("##MainPal", - overworld_->mutable_overworld_map(current_map)->mutable_main_palette(), + ImGui::Text("Main Palette"); + TableNextColumn(); + if (gui::InputHexByte("##MainPal", + overworld_->mutable_overworld_map(current_map) + ->mutable_main_palette(), kInputFieldSize)) { RefreshMapProperties(); auto status = RefreshMapPalette(); RefreshOverworldMap(); } } - + if (asm_version >= 3) { - TableNextColumn(); ImGui::Text("Animated GFX"); TableNextColumn(); - if (gui::InputHexByte("##AnimGfx", - overworld_->mutable_overworld_map(current_map)->mutable_animated_gfx(), + ImGui::Text("Animated GFX"); + TableNextColumn(); + if (gui::InputHexByte("##AnimGfx", + overworld_->mutable_overworld_map(current_map) + ->mutable_animated_gfx(), kInputFieldSize)) { RefreshMapProperties(); RefreshOverworldMap(); } - - TableNextColumn(); ImGui::Text("Subscreen Overlay"); + TableNextColumn(); - if (gui::InputHexWord("##SubOverlay", - overworld_->mutable_overworld_map(current_map)->mutable_subscreen_overlay(), + ImGui::Text("Subscreen Overlay"); + TableNextColumn(); + if (gui::InputHexWord("##SubOverlay", + overworld_->mutable_overworld_map(current_map) + ->mutable_subscreen_overlay(), kInputFieldSize + 20)) { RefreshMapProperties(); RefreshOverworldMap(); } } - + ImGui::EndTable(); } } @@ -677,35 +768,38 @@ void MapPropertiesSystem::DrawCustomFeaturesTab(int current_map) { void MapPropertiesSystem::DrawTileGraphicsTab(int current_map) { ImGui::Text("Custom Tile Graphics (8 sheets per map):"); Separator(); - - if (BeginTable("TileGraphics", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit)) { + + if (BeginTable("TileGraphics", 4, + ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit)) { ImGui::TableSetupColumn("Sheet", ImGuiTableColumnFlags_WidthFixed, 60); ImGui::TableSetupColumn("GFX ID", ImGuiTableColumnFlags_WidthFixed, 80); ImGui::TableSetupColumn("Sheet", ImGuiTableColumnFlags_WidthFixed, 60); ImGui::TableSetupColumn("GFX ID", ImGuiTableColumnFlags_WidthFixed, 80); - + for (int i = 0; i < 4; i++) { TableNextColumn(); ImGui::Text("Sheet %d", i); TableNextColumn(); if (gui::InputHexByte(absl::StrFormat("##TileGfx%d", i).c_str(), - overworld_->mutable_overworld_map(current_map)->mutable_custom_tileset(i), + overworld_->mutable_overworld_map(current_map) + ->mutable_custom_tileset(i), kInputFieldSize)) { RefreshMapProperties(); RefreshOverworldMap(); } - + TableNextColumn(); ImGui::Text("Sheet %d", i + 4); TableNextColumn(); if (gui::InputHexByte(absl::StrFormat("##TileGfx%d", i + 4).c_str(), - overworld_->mutable_overworld_map(current_map)->mutable_custom_tileset(i + 4), + overworld_->mutable_overworld_map(current_map) + ->mutable_custom_tileset(i + 4), kInputFieldSize)) { RefreshMapProperties(); RefreshOverworldMap(); } } - + ImGui::EndTable(); } } @@ -713,60 +807,70 @@ void MapPropertiesSystem::DrawTileGraphicsTab(int current_map) { void MapPropertiesSystem::DrawMusicTab(int current_map) { ImGui::Text("Music Settings for Different Game States:"); Separator(); - - if (BeginTable("MusicSettings", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit)) { - ImGui::TableSetupColumn("Game State", ImGuiTableColumnFlags_WidthFixed, 150); - ImGui::TableSetupColumn("Music Track ID", ImGuiTableColumnFlags_WidthStretch); - - const char* music_state_names[] = { - "Beginning (Pre-Zelda)", - "Zelda Rescued", - "Master Sword Obtained", - "Agahnim Defeated" - }; - + + if (BeginTable("MusicSettings", 2, + ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit)) { + ImGui::TableSetupColumn("Game State", ImGuiTableColumnFlags_WidthFixed, + 150); + ImGui::TableSetupColumn("Music Track ID", + ImGuiTableColumnFlags_WidthStretch); + + const char* music_state_names[] = {"Beginning (Pre-Zelda)", "Zelda Rescued", + "Master Sword Obtained", + "Agahnim Defeated"}; + const char* music_descriptions[] = { - "Music before rescuing Zelda", - "Music after rescuing Zelda from Hyrule Castle", - "Music after obtaining the Master Sword", - "Music after defeating Agahnim (Dark World)" - }; - + "Music before rescuing Zelda", + "Music after rescuing Zelda from Hyrule Castle", + "Music after obtaining the Master Sword", + "Music after defeating Agahnim (Dark World)"}; + for (int i = 0; i < 4; i++) { TableNextColumn(); ImGui::Text("%s", music_state_names[i]); - + TableNextColumn(); if (gui::InputHexByte(absl::StrFormat("##Music%d", i).c_str(), - overworld_->mutable_overworld_map(current_map)->mutable_area_music(i), + overworld_->mutable_overworld_map(current_map) + ->mutable_area_music(i), kInputFieldSize)) { RefreshMapProperties(); - + // Update the ROM directly since music is not automatically saved int music_address = 0; switch (i) { - case 0: music_address = zelda3::kOverworldMusicBeginning + current_map; break; - case 1: music_address = zelda3::kOverworldMusicZelda + current_map; break; - case 2: music_address = zelda3::kOverworldMusicMasterSword + current_map; break; - case 3: music_address = zelda3::kOverworldMusicAgahnim + current_map; break; + case 0: + music_address = zelda3::kOverworldMusicBeginning + current_map; + break; + case 1: + music_address = zelda3::kOverworldMusicZelda + current_map; + break; + case 2: + music_address = zelda3::kOverworldMusicMasterSword + current_map; + break; + case 3: + music_address = zelda3::kOverworldMusicAgahnim + current_map; + break; } - + if (music_address > 0) { - (*rom_)[music_address] = *overworld_->mutable_overworld_map(current_map)->mutable_area_music(i); + (*rom_)[music_address] = + *overworld_->mutable_overworld_map(current_map) + ->mutable_area_music(i); } } if (ImGui::IsItemHovered()) { ImGui::SetTooltip("%s", music_descriptions[i]); } } - + ImGui::EndTable(); } - + Separator(); ImGui::Text("Music tracks control the background music for different"); ImGui::Text("game progression states on this overworld map."); - + // Show common music track IDs for reference Separator(); ImGui::Text("Common Music Track IDs:"); @@ -792,15 +896,16 @@ absl::Status MapPropertiesSystem::RefreshMapPalette() { } void MapPropertiesSystem::DrawMosaicControls(int current_map) { - static uint8_t asm_version = (*rom_)[zelda3::OverworldCustomASMHasBeenApplied]; + static uint8_t asm_version = + (*rom_)[zelda3::OverworldCustomASMHasBeenApplied]; if (asm_version >= 2) { ImGui::Separator(); ImGui::Text("Mosaic Effects (per direction):"); - + auto* current_map_ptr = overworld_->mutable_overworld_map(current_map); std::array mosaic_expanded = current_map_ptr->mosaic_expanded(); const char* direction_names[] = {"North", "South", "East", "West"}; - + for (int i = 0; i < 4; i++) { if (ImGui::Checkbox(direction_names[i], &mosaic_expanded[i])) { current_map_ptr->set_mosaic_expanded(i, mosaic_expanded[i]); @@ -809,21 +914,23 @@ void MapPropertiesSystem::DrawMosaicControls(int current_map) { } } } else { - if (ImGui::Checkbox("Mosaic Effect", - overworld_->mutable_overworld_map(current_map) - ->mutable_mosaic())) { + if (ImGui::Checkbox( + "Mosaic Effect", + overworld_->mutable_overworld_map(current_map)->mutable_mosaic())) { RefreshMapProperties(); RefreshOverworldMap(); } } } -void MapPropertiesSystem::DrawOverlayControls(int current_map, bool& show_overlay_preview) { - static uint8_t asm_version = (*rom_)[zelda3::OverworldCustomASMHasBeenApplied]; - +void MapPropertiesSystem::DrawOverlayControls(int current_map, + bool& show_overlay_preview) { + static uint8_t asm_version = + (*rom_)[zelda3::OverworldCustomASMHasBeenApplied]; + // Determine if this is a special overworld map (0x80-0x9F) bool is_special_overworld_map = (current_map >= 0x80 && current_map < 0xA0); - + if (is_special_overworld_map) { // Special overworld maps (0x80-0x9F) do not support subscreen overlays ImGui::Text("Special overworld maps (0x80-0x9F) do not support"); @@ -831,7 +938,7 @@ void MapPropertiesSystem::DrawOverlayControls(int current_map, bool& show_overla ImGui::Text("Map 0x%02X is a special overworld map", current_map); } else { // Light World (0x00-0x3F) and Dark World (0x40-0x7F) maps support subscreen overlays for all versions - + // Subscreen Overlay Section ImGui::Text("Subscreen Overlay (Visual Effects)"); ImGui::SameLine(); @@ -839,32 +946,39 @@ void MapPropertiesSystem::DrawOverlayControls(int current_map, bool& show_overla ImGui::OpenPopup("SubscreenOverlayHelp"); } if (ImGui::BeginPopup("SubscreenOverlayHelp")) { - ImGui::Text("Subscreen overlays are visual effects like fog, rain, canopy,"); + ImGui::Text( + "Subscreen overlays are visual effects like fog, rain, canopy,"); ImGui::Text("and backgrounds that are displayed using tile16 graphics."); - ImGui::Text("They reference special area maps (0x80-0x9F) for their tile data."); + ImGui::Text( + "They reference special area maps (0x80-0x9F) for their tile data."); ImGui::EndPopup(); } - - uint16_t current_overlay = overworld_->mutable_overworld_map(current_map)->subscreen_overlay(); - if (gui::InputHexWord("Subscreen Overlay ID", ¤t_overlay, kInputFieldSize + 20)) { - overworld_->mutable_overworld_map(current_map)->set_subscreen_overlay(current_overlay); + + uint16_t current_overlay = + overworld_->mutable_overworld_map(current_map)->subscreen_overlay(); + if (gui::InputHexWord("Subscreen Overlay ID", ¤t_overlay, + kInputFieldSize + 20)) { + overworld_->mutable_overworld_map(current_map) + ->set_subscreen_overlay(current_overlay); RefreshMapProperties(); RefreshOverworldMap(); } - HOVER_HINT("Subscreen overlay ID - visual effects like fog, rain, backgrounds"); - + HOVER_HINT( + "Subscreen overlay ID - visual effects like fog, rain, backgrounds"); + // Show subscreen overlay description std::string overlay_desc = GetOverlayDescription(current_overlay); ImGui::Text("Description: %s", overlay_desc.c_str()); - + // Preview checkbox - if (ImGui::Checkbox("Preview Subscreen Overlay on Map", &show_overlay_preview)) { + if (ImGui::Checkbox("Preview Subscreen Overlay on Map", + &show_overlay_preview)) { // Toggle subscreen overlay preview } HOVER_HINT("Show semi-transparent preview of subscreen overlay on the map"); - + ImGui::Separator(); - + // Interactive Overlay Section (for vanilla ROMs) if (asm_version == 0xFF) { ImGui::Text("Interactive Overlay (Holes/Changes)"); @@ -873,26 +987,34 @@ void MapPropertiesSystem::DrawOverlayControls(int current_map, bool& show_overla ImGui::OpenPopup("InteractiveOverlayHelp"); } if (ImGui::BeginPopup("InteractiveOverlayHelp")) { - ImGui::Text("Interactive overlays reveal holes or change elements on top"); + ImGui::Text( + "Interactive overlays reveal holes or change elements on top"); ImGui::Text("of the map. They use tile16 graphics and are present in"); - ImGui::Text("vanilla ROMs. ZSCustomOverworld expands this functionality."); + ImGui::Text( + "vanilla ROMs. ZSCustomOverworld expands this functionality."); ImGui::EndPopup(); } - - auto *current_map_ptr = overworld_->overworld_map(current_map); + + auto* current_map_ptr = overworld_->overworld_map(current_map); if (current_map_ptr->has_overlay()) { - ImGui::Text("Interactive Overlay ID: 0x%04X", current_map_ptr->overlay_id()); - ImGui::Text("Overlay Data Size: %d bytes", static_cast(current_map_ptr->overlay_data().size())); + ImGui::Text("Interactive Overlay ID: 0x%04X", + current_map_ptr->overlay_id()); + ImGui::Text("Overlay Data Size: %d bytes", + static_cast(current_map_ptr->overlay_data().size())); } else { ImGui::Text("No interactive overlay data for this map"); } - HOVER_HINT("Interactive overlay for revealing holes/changing elements (read-only in vanilla)"); + HOVER_HINT( + "Interactive overlay for revealing holes/changing elements " + "(read-only in vanilla)"); } - + // Show version info if (asm_version == 0xFF) { - ImGui::Text("Vanilla ROM - subscreen overlays reference special area maps"); - ImGui::Text("(0x80-0x9F) for visual effects like fog, rain, backgrounds."); + ImGui::Text( + "Vanilla ROM - subscreen overlays reference special area maps"); + ImGui::Text( + "(0x80-0x9F) for visual effects like fog, rain, backgrounds."); } else { ImGui::Text("ZSCustomOverworld v%d", asm_version); } @@ -925,39 +1047,46 @@ std::string MapPropertiesSystem::GetOverlayDescription(uint16_t overlay_id) { } } -void MapPropertiesSystem::DrawOverlayPreviewOnMap(int current_map, int current_world, bool show_overlay_preview) { - if (!show_overlay_preview || !maps_bmp_ || !canvas_) return; - +void MapPropertiesSystem::DrawOverlayPreviewOnMap(int current_map, + int current_world, + bool show_overlay_preview) { + if (!show_overlay_preview || !maps_bmp_ || !canvas_) + return; + // Get subscreen overlay information based on ROM version and map type uint16_t overlay_id = 0x00FF; bool has_subscreen_overlay = false; - - static uint8_t asm_version = (*rom_)[zelda3::OverworldCustomASMHasBeenApplied]; + + static uint8_t asm_version = + (*rom_)[zelda3::OverworldCustomASMHasBeenApplied]; bool is_special_overworld_map = (current_map >= 0x80 && current_map < 0xA0); - + if (is_special_overworld_map) { // Special overworld maps (0x80-0x9F) do not support subscreen overlays return; } - + // Light World (0x00-0x3F) and Dark World (0x40-0x7F) maps support subscreen overlays for all versions overlay_id = overworld_->overworld_map(current_map)->subscreen_overlay(); has_subscreen_overlay = (overlay_id != 0x00FF); - - if (!has_subscreen_overlay) return; - + + if (!has_subscreen_overlay) + return; + // Map subscreen overlay ID to special area map for bitmap int overlay_map_index = -1; if (overlay_id >= 0x80 && overlay_id < 0xA0) { overlay_map_index = overlay_id; } - - if (overlay_map_index < 0 || overlay_map_index >= zelda3::kNumOverworldMaps) return; - + + if (overlay_map_index < 0 || overlay_map_index >= zelda3::kNumOverworldMaps) + return; + // Get the subscreen overlay map's bitmap - const auto &overlay_bitmap = (*maps_bmp_)[overlay_map_index]; - if (!overlay_bitmap.is_active()) return; - + const auto& overlay_bitmap = (*maps_bmp_)[overlay_map_index]; + if (!overlay_bitmap.is_active()) + return; + // Calculate position for subscreen overlay preview on the current map int current_map_x = current_map % 8; int current_map_y = current_map / 8; @@ -968,37 +1097,40 @@ void MapPropertiesSystem::DrawOverlayPreviewOnMap(int current_map, int current_w current_map_x = (current_map - 0x80) % 8; current_map_y = (current_map - 0x80) / 8; } - + int scale = static_cast(canvas_->global_scale()); int map_x = current_map_x * kOverworldMapSize * scale; int map_y = current_map_y * kOverworldMapSize * scale; - + // Determine if this is a background or foreground subscreen overlay - bool is_background_overlay = (overlay_id == 0x0095 || overlay_id == 0x0096 || overlay_id == 0x009C); - + bool is_background_overlay = + (overlay_id == 0x0095 || overlay_id == 0x0096 || overlay_id == 0x009C); + // Set alpha for semi-transparent preview - ImU32 overlay_color = is_background_overlay ? - IM_COL32(255, 255, 255, 128) : // Background subscreen overlays - lighter - IM_COL32(255, 255, 255, 180); // Foreground subscreen overlays - more opaque - + ImU32 overlay_color = + is_background_overlay ? IM_COL32(255, 255, 255, 128) + : // Background subscreen overlays - lighter + IM_COL32(255, 255, 255, + 180); // Foreground subscreen overlays - more opaque + // Draw the subscreen overlay bitmap with semi-transparency canvas_->draw_list()->AddImage( - (ImTextureID)(intptr_t)overlay_bitmap.texture(), - ImVec2(map_x, map_y), - ImVec2(map_x + kOverworldMapSize * scale, map_y + kOverworldMapSize * scale), - ImVec2(0, 0), - ImVec2(1, 1), - overlay_color); + (ImTextureID)(intptr_t)overlay_bitmap.texture(), ImVec2(map_x, map_y), + ImVec2(map_x + kOverworldMapSize * scale, + map_y + kOverworldMapSize * scale), + ImVec2(0, 0), ImVec2(1, 1), overlay_color); } void MapPropertiesSystem::DrawViewPopup() { if (ImGui::BeginPopup("ViewPopup")) { - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(kCompactItemSpacing, kCompactFramePadding)); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(kCompactItemSpacing, kCompactFramePadding)); - + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, + ImVec2(kCompactItemSpacing, kCompactFramePadding)); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, + ImVec2(kCompactItemSpacing, kCompactFramePadding)); + ImGui::Text("View Controls"); ImGui::Separator(); - + // Horizontal layout for view controls if (ImGui::Button(ICON_MD_ZOOM_OUT, ImVec2(kIconButtonWidth, 0))) { // This would need to be connected to the canvas zoom function @@ -1017,7 +1149,7 @@ void MapPropertiesSystem::DrawViewPopup() { // For now, just show the option } HOVER_HINT("Toggle fullscreen canvas (F11)"); - + ImGui::PopStyleVar(2); // Pop the 2 style variables we pushed ImGui::EndPopup(); } @@ -1025,12 +1157,14 @@ void MapPropertiesSystem::DrawViewPopup() { void MapPropertiesSystem::DrawQuickAccessPopup() { if (ImGui::BeginPopup("QuickPopup")) { - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(kCompactItemSpacing, kCompactFramePadding)); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(kCompactItemSpacing, kCompactFramePadding)); - + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, + ImVec2(kCompactItemSpacing, kCompactFramePadding)); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, + ImVec2(kCompactItemSpacing, kCompactFramePadding)); + ImGui::Text("Quick Access"); ImGui::Separator(); - + // Horizontal layout for quick access buttons if (ImGui::Button(ICON_MD_GRID_VIEW, ImVec2(kIconButtonWidth, 0))) { // This would need to be connected to the Tile16 editor toggle @@ -1038,20 +1172,20 @@ void MapPropertiesSystem::DrawQuickAccessPopup() { } HOVER_HINT("Open Tile16 Editor (Ctrl+T)"); ImGui::SameLine(); - + if (ImGui::Button(ICON_MD_CONTENT_COPY, ImVec2(kIconButtonWidth, 0))) { // This would need to be connected to the copy map function // For now, just show the option } HOVER_HINT("Copy current map to clipboard"); ImGui::SameLine(); - + if (ImGui::Button(ICON_MD_LOCK, ImVec2(kIconButtonWidth, 0))) { // This would need to be connected to the map lock toggle // For now, just show the option } HOVER_HINT("Lock/unlock current map (Ctrl+L)"); - + ImGui::PopStyleVar(2); // Pop the 2 style variables we pushed ImGui::EndPopup(); } diff --git a/src/app/editor/overworld/overworld_editor.cc b/src/app/editor/overworld/overworld_editor.cc index 7568502f..a01ae98d 100644 --- a/src/app/editor/overworld/overworld_editor.cc +++ b/src/app/editor/overworld/overworld_editor.cc @@ -444,10 +444,7 @@ void OverworldEditor::DrawOverworldMapSettings() { // World selector (always present) TableNextColumn(); ImGui::SetNextItemWidth(120.f); - if (ImGui::Combo("##world", ¤t_world_, kWorldList.data(), 3)) { - // Update current map when world changes - RefreshOverworldMap(); - } + ImGui::Combo("##world", ¤t_world_, kWorldList.data(), 3); // Area Graphics (always present) TableNextColumn(); @@ -2170,18 +2167,7 @@ void OverworldEditor::DrawMapPropertiesPanel() { Text("World"); TableNextColumn(); ImGui::SetNextItemWidth(100.f); - if (ImGui::Combo("##world", ¤t_world_, kWorldList.data(), 3)) { - // Update current map based on world change - if (current_map_ >= 0x40 && current_world_ == 0) { - current_map_ -= 0x40; - } else if (current_map_ < 0x40 && current_world_ == 1) { - current_map_ += 0x40; - } else if (current_map_ < 0x80 && current_world_ == 2) { - current_map_ += 0x80; - } else if (current_map_ >= 0x80 && current_world_ != 2) { - current_map_ -= 0x80; - } - } + ImGui::Combo("##world", ¤t_world_, kWorldList.data(), 3); TableNextColumn(); Text("Area Graphics"); diff --git a/src/app/zelda3/overworld/overworld.cc b/src/app/zelda3/overworld/overworld.cc index 2ea33b4d..cdb63413 100644 --- a/src/app/zelda3/overworld/overworld.cc +++ b/src/app/zelda3/overworld/overworld.cc @@ -39,7 +39,12 @@ absl::Status Overworld::Load(Rom* rom) { map_parent_[map_index] = overworld_maps_[map_index].parent(); } - FetchLargeMaps(); + uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied]; + if (asm_version >= 3) { + AssignMapSizes(overworld_maps_); + } else { + FetchLargeMaps(); + } LoadTileTypes(); RETURN_IF_ERROR(LoadEntrances()); RETURN_IF_ERROR(LoadHoles()); @@ -105,6 +110,90 @@ void Overworld::FetchLargeMaps() { } } +/** + * @brief Loads all maps from ROM to see what size they are. + * @param maps The maps to update (passed by reference) + */ +void Overworld::AssignMapSizes(std::vector& maps) { + std::vector map_checked(kNumOverworldMaps, false); + + int xx = 0; + int yy = 0; + int world = 0; + + while (true) { + int i = world + xx + (yy * 8); + + if (i >= static_cast(map_checked.size())) { + break; + } + + if (!map_checked[i]) { + switch (maps[i].area_size()) { + case AreaSizeEnum::SmallArea: + map_checked[i] = true; + maps[i].SetAreaSize(AreaSizeEnum::SmallArea); + break; + + case AreaSizeEnum::LargeArea: + map_checked[i] = true; + maps[i].SetAsLargeMap(i, 0); + + if (i + 1 < static_cast(maps.size())) { + map_checked[i + 1] = true; + maps[i + 1].SetAsLargeMap(i, 1); + } + + if (i + 8 < static_cast(maps.size())) { + map_checked[i + 8] = true; + maps[i + 8].SetAsLargeMap(i, 2); + } + + if (i + 9 < static_cast(maps.size())) { + map_checked[i + 9] = true; + maps[i + 9].SetAsLargeMap(i, 3); + } + + xx++; + break; + + case AreaSizeEnum::WideArea: + map_checked[i] = true; + maps[i].SetAreaSize(AreaSizeEnum::WideArea); + + if (i + 1 < static_cast(maps.size())) { + map_checked[i + 1] = true; + maps[i + 1].SetAreaSize(AreaSizeEnum::WideArea); + } + + xx++; + break; + + case AreaSizeEnum::TallArea: + map_checked[i] = true; + maps[i].SetAreaSize(AreaSizeEnum::TallArea); + + if (i + 8 < static_cast(maps.size())) { + map_checked[i + 8] = true; + maps[i + 8].SetAreaSize(AreaSizeEnum::TallArea); + } + break; + } + } + + xx++; + if (xx >= 8) { + xx = 0; + yy += 1; + + if (yy >= 8) { + yy = 0; + world += 0x40; + } + } + } +} + absl::StatusOr Overworld::GetTile16ForTile32( int index, int quadrant, int dimension, const uint32_t* map32address) { ASSIGN_OR_RETURN( @@ -443,7 +532,8 @@ absl::Status Overworld::LoadItems() { // Determine max number of overworld maps based on actual ASM version // Only use expanded maps (0xA0) if v3+ ASM is actually applied - int max_ow = (asm_version >= 0x03 && asm_version != 0xFF) ? kNumOverworldMaps : 0x80; + int max_ow = + (asm_version >= 0x03 && asm_version != 0xFF) ? kNumOverworldMaps : 0x80; ASSIGN_OR_RETURN(uint32_t pointer_snes, rom()->ReadLong(zelda3::overworldItemsAddress)); @@ -745,16 +835,16 @@ absl::Status Overworld::SaveOverworldMaps() { absl::Status Overworld::SaveLargeMaps() { util::logf("Saving Large Maps"); - + // Check if this is a v3+ ROM to use expanded transition system uint8_t asm_version = (*rom_)[zelda3::OverworldCustomASMHasBeenApplied]; bool use_expanded_transitions = (asm_version >= 3 && asm_version != 0xFF); - + if (use_expanded_transitions) { // Use new v3+ complex transition system with neighbor awareness return SaveLargeMapsExpanded(); } - + // Original vanilla/v2 logic preserved std::vector checked_map; @@ -1073,123 +1163,143 @@ absl::Status Overworld::SaveLargeMaps() { return absl::OkStatus(); } -absl::Status Overworld::SaveSmallAreaTransitions(int i, int parent_x_pos, int parent_y_pos, - int transition_target_north, int transition_target_west, - int transition_pos_x, int transition_pos_y, - int screen_change_1, int screen_change_2, - int screen_change_3, int screen_change_4) { +absl::Status Overworld::SaveSmallAreaTransitions( + int i, int parent_x_pos, int parent_y_pos, int transition_target_north, + int transition_target_west, int transition_pos_x, int transition_pos_y, + int screen_change_1, int screen_change_2, int screen_change_3, + int screen_change_4) { // Set basic transition targets - RETURN_IF_ERROR(rom()->WriteShort(transition_target_north + (i * 2), - (uint16_t)((parent_y_pos * 0x0200) - 0x00E0))); - RETURN_IF_ERROR(rom()->WriteShort(transition_target_west + (i * 2), - (uint16_t)((parent_x_pos * 0x0200) - 0x0100))); + RETURN_IF_ERROR( + rom()->WriteShort(transition_target_north + (i * 2), + (uint16_t)((parent_y_pos * 0x0200) - 0x00E0))); + RETURN_IF_ERROR( + rom()->WriteShort(transition_target_west + (i * 2), + (uint16_t)((parent_x_pos * 0x0200) - 0x0100))); - RETURN_IF_ERROR(rom()->WriteShort(transition_pos_x + (i * 2), parent_x_pos * 0x0200)); - RETURN_IF_ERROR(rom()->WriteShort(transition_pos_y + (i * 2), parent_y_pos * 0x0200)); + RETURN_IF_ERROR( + rom()->WriteShort(transition_pos_x + (i * 2), parent_x_pos * 0x0200)); + RETURN_IF_ERROR( + rom()->WriteShort(transition_pos_y + (i * 2), parent_y_pos * 0x0200)); // byScreen1 = Transitioning right uint16_t by_screen1_small = 0x0060; - + // Check west neighbor for transition adjustments if ((i % 0x40) - 1 >= 0) { auto& west_neighbor = overworld_maps_[i - 1]; - + // Transition from bottom right quadrant of large area to small area - if (west_neighbor.area_size() == AreaSizeEnum::LargeArea && west_neighbor.large_index() == 3) { + if (west_neighbor.area_size() == AreaSizeEnum::LargeArea && + west_neighbor.large_index() == 3) { by_screen1_small = 0xF060; } - // Transition from bottom quadrant of tall area to small area - else if (west_neighbor.area_size() == AreaSizeEnum::TallArea && west_neighbor.large_index() == 2) { + // Transition from bottom quadrant of tall area to small area + else if (west_neighbor.area_size() == AreaSizeEnum::TallArea && + west_neighbor.large_index() == 2) { by_screen1_small = 0xF060; } } - - RETURN_IF_ERROR(rom()->WriteShort(screen_change_1 + (i * 2), by_screen1_small)); + + RETURN_IF_ERROR( + rom()->WriteShort(screen_change_1 + (i * 2), by_screen1_small)); // byScreen2 = Transitioning left uint16_t by_screen2_small = 0x0040; - + // Check east neighbor for transition adjustments if ((i % 0x40) + 1 < 0x40 && i + 1 < kNumOverworldMaps) { auto& east_neighbor = overworld_maps_[i + 1]; - + // Transition from bottom left quadrant of large area to small area - if (east_neighbor.area_size() == AreaSizeEnum::LargeArea && east_neighbor.large_index() == 2) { + if (east_neighbor.area_size() == AreaSizeEnum::LargeArea && + east_neighbor.large_index() == 2) { by_screen2_small = 0xF040; } // Transition from bottom quadrant of tall area to small area - else if (east_neighbor.area_size() == AreaSizeEnum::TallArea && east_neighbor.large_index() == 2) { + else if (east_neighbor.area_size() == AreaSizeEnum::TallArea && + east_neighbor.large_index() == 2) { by_screen2_small = 0xF040; } } - - RETURN_IF_ERROR(rom()->WriteShort(screen_change_2 + (i * 2), by_screen2_small)); + + RETURN_IF_ERROR( + rom()->WriteShort(screen_change_2 + (i * 2), by_screen2_small)); // byScreen3 = Transitioning down uint16_t by_screen3_small = 0x1800; - + // Check north neighbor for transition adjustments if ((i % 0x40) - 8 >= 0) { auto& north_neighbor = overworld_maps_[i - 8]; - + // Transition from bottom right quadrant of large area to small area - if (north_neighbor.area_size() == AreaSizeEnum::LargeArea && north_neighbor.large_index() == 3) { + if (north_neighbor.area_size() == AreaSizeEnum::LargeArea && + north_neighbor.large_index() == 3) { by_screen3_small = 0x17C0; } // Transition from right quadrant of wide area to small area - else if (north_neighbor.area_size() == AreaSizeEnum::WideArea && north_neighbor.large_index() == 1) { + else if (north_neighbor.area_size() == AreaSizeEnum::WideArea && + north_neighbor.large_index() == 1) { by_screen3_small = 0x17C0; } } - - RETURN_IF_ERROR(rom()->WriteShort(screen_change_3 + (i * 2), by_screen3_small)); + + RETURN_IF_ERROR( + rom()->WriteShort(screen_change_3 + (i * 2), by_screen3_small)); // byScreen4 = Transitioning up uint16_t by_screen4_small = 0x1000; - + // Check south neighbor for transition adjustments if ((i % 0x40) + 8 < 0x40 && i + 8 < kNumOverworldMaps) { auto& south_neighbor = overworld_maps_[i + 8]; - + // Transition from top right quadrant of large area to small area - if (south_neighbor.area_size() == AreaSizeEnum::LargeArea && south_neighbor.large_index() == 1) { + if (south_neighbor.area_size() == AreaSizeEnum::LargeArea && + south_neighbor.large_index() == 1) { by_screen4_small = 0x0FC0; } // Transition from right quadrant of wide area to small area - else if (south_neighbor.area_size() == AreaSizeEnum::WideArea && south_neighbor.large_index() == 1) { + else if (south_neighbor.area_size() == AreaSizeEnum::WideArea && + south_neighbor.large_index() == 1) { by_screen4_small = 0x0FC0; } } - - RETURN_IF_ERROR(rom()->WriteShort(screen_change_4 + (i * 2), by_screen4_small)); + + RETURN_IF_ERROR( + rom()->WriteShort(screen_change_4 + (i * 2), by_screen4_small)); return absl::OkStatus(); } -absl::Status Overworld::SaveLargeAreaTransitions(int i, int parent_x_pos, int parent_y_pos, - int transition_target_north, int transition_target_west, - int transition_pos_x, int transition_pos_y, - int screen_change_1, int screen_change_2, - int screen_change_3, int screen_change_4) { +absl::Status Overworld::SaveLargeAreaTransitions( + int i, int parent_x_pos, int parent_y_pos, int transition_target_north, + int transition_target_west, int transition_pos_x, int transition_pos_y, + int screen_change_1, int screen_change_2, int screen_change_3, + int screen_change_4) { // Set transition targets for all 4 quadrants const uint16_t offsets[] = {0, 2, 16, 18}; for (auto offset : offsets) { - RETURN_IF_ERROR(rom()->WriteShort(transition_target_north + (i * 2) + offset, - (uint16_t)((parent_y_pos * 0x0200) - 0x00E0))); - RETURN_IF_ERROR(rom()->WriteShort(transition_target_west + (i * 2) + offset, - (uint16_t)((parent_x_pos * 0x0200) - 0x0100))); - RETURN_IF_ERROR(rom()->WriteShort(transition_pos_x + (i * 2) + offset, parent_x_pos * 0x0200)); - RETURN_IF_ERROR(rom()->WriteShort(transition_pos_y + (i * 2) + offset, parent_y_pos * 0x0200)); + RETURN_IF_ERROR( + rom()->WriteShort(transition_target_north + (i * 2) + offset, + (uint16_t)((parent_y_pos * 0x0200) - 0x00E0))); + RETURN_IF_ERROR( + rom()->WriteShort(transition_target_west + (i * 2) + offset, + (uint16_t)((parent_x_pos * 0x0200) - 0x0100))); + RETURN_IF_ERROR(rom()->WriteShort(transition_pos_x + (i * 2) + offset, + parent_x_pos * 0x0200)); + RETURN_IF_ERROR(rom()->WriteShort(transition_pos_y + (i * 2) + offset, + parent_y_pos * 0x0200)); } // Complex neighbor-aware transition calculations for large areas // byScreen1 = Transitioning right std::array by_screen1_large = {0x0060, 0x0060, 0x1060, 0x1060}; - + // Check west neighbor if ((i % 0x40) - 1 >= 0) { auto& west_neighbor = overworld_maps_[i - 1]; - + if (west_neighbor.area_size() == AreaSizeEnum::LargeArea) { switch (west_neighbor.large_index()) { case 1: // From bottom right to bottom left of large area @@ -1210,18 +1320,19 @@ absl::Status Overworld::SaveLargeAreaTransitions(int i, int parent_x_pos, int pa } } } - + for (int j = 0; j < 4; j++) { - RETURN_IF_ERROR(rom()->WriteShort(screen_change_1 + (i * 2) + offsets[j], by_screen1_large[j])); + RETURN_IF_ERROR(rom()->WriteShort(screen_change_1 + (i * 2) + offsets[j], + by_screen1_large[j])); } // byScreen2 = Transitioning left std::array by_screen2_large = {0x0080, 0x0080, 0x1080, 0x1080}; - + // Check east neighbor if ((i % 0x40) + 2 < 0x40 && i + 2 < kNumOverworldMaps) { auto& east_neighbor = overworld_maps_[i + 2]; - + if (east_neighbor.area_size() == AreaSizeEnum::LargeArea) { switch (east_neighbor.large_index()) { case 0: // From bottom left to bottom right of large area @@ -1242,18 +1353,19 @@ absl::Status Overworld::SaveLargeAreaTransitions(int i, int parent_x_pos, int pa } } } - + for (int j = 0; j < 4; j++) { - RETURN_IF_ERROR(rom()->WriteShort(screen_change_2 + (i * 2) + offsets[j], by_screen2_large[j])); + RETURN_IF_ERROR(rom()->WriteShort(screen_change_2 + (i * 2) + offsets[j], + by_screen2_large[j])); } // byScreen3 = Transitioning down std::array by_screen3_large = {0x1800, 0x1840, 0x1800, 0x1840}; - + // Check north neighbor if ((i % 0x40) - 8 >= 0) { auto& north_neighbor = overworld_maps_[i - 8]; - + if (north_neighbor.area_size() == AreaSizeEnum::LargeArea) { switch (north_neighbor.large_index()) { case 2: // From bottom right to top right of large area @@ -1274,18 +1386,19 @@ absl::Status Overworld::SaveLargeAreaTransitions(int i, int parent_x_pos, int pa } } } - + for (int j = 0; j < 4; j++) { - RETURN_IF_ERROR(rom()->WriteShort(screen_change_3 + (i * 2) + offsets[j], by_screen3_large[j])); + RETURN_IF_ERROR(rom()->WriteShort(screen_change_3 + (i * 2) + offsets[j], + by_screen3_large[j])); } // byScreen4 = Transitioning up std::array by_screen4_large = {0x2000, 0x2040, 0x2000, 0x2040}; - + // Check south neighbor if ((i % 0x40) + 16 < 0x40 && i + 16 < kNumOverworldMaps) { auto& south_neighbor = overworld_maps_[i + 16]; - + if (south_neighbor.area_size() == AreaSizeEnum::LargeArea) { switch (south_neighbor.large_index()) { case 0: // From top right to bottom right of large area @@ -1306,79 +1419,90 @@ absl::Status Overworld::SaveLargeAreaTransitions(int i, int parent_x_pos, int pa } } } - + for (int j = 0; j < 4; j++) { - RETURN_IF_ERROR(rom()->WriteShort(screen_change_4 + (i * 2) + offsets[j], by_screen4_large[j])); + RETURN_IF_ERROR(rom()->WriteShort(screen_change_4 + (i * 2) + offsets[j], + by_screen4_large[j])); } return absl::OkStatus(); } -absl::Status Overworld::SaveWideAreaTransitions(int i, int parent_x_pos, int parent_y_pos, - int transition_target_north, int transition_target_west, - int transition_pos_x, int transition_pos_y, - int screen_change_1, int screen_change_2, - int screen_change_3, int screen_change_4) { +absl::Status Overworld::SaveWideAreaTransitions( + int i, int parent_x_pos, int parent_y_pos, int transition_target_north, + int transition_target_west, int transition_pos_x, int transition_pos_y, + int screen_change_1, int screen_change_2, int screen_change_3, + int screen_change_4) { // Set transition targets for both quadrants const uint16_t offsets[] = {0, 2}; for (auto offset : offsets) { - RETURN_IF_ERROR(rom()->WriteShort(transition_target_north + (i * 2) + offset, - (uint16_t)((parent_y_pos * 0x0200) - 0x00E0))); - RETURN_IF_ERROR(rom()->WriteShort(transition_target_west + (i * 2) + offset, - (uint16_t)((parent_x_pos * 0x0200) - 0x0100))); - RETURN_IF_ERROR(rom()->WriteShort(transition_pos_x + (i * 2) + offset, parent_x_pos * 0x0200)); - RETURN_IF_ERROR(rom()->WriteShort(transition_pos_y + (i * 2) + offset, parent_y_pos * 0x0200)); + RETURN_IF_ERROR( + rom()->WriteShort(transition_target_north + (i * 2) + offset, + (uint16_t)((parent_y_pos * 0x0200) - 0x00E0))); + RETURN_IF_ERROR( + rom()->WriteShort(transition_target_west + (i * 2) + offset, + (uint16_t)((parent_x_pos * 0x0200) - 0x0100))); + RETURN_IF_ERROR(rom()->WriteShort(transition_pos_x + (i * 2) + offset, + parent_x_pos * 0x0200)); + RETURN_IF_ERROR(rom()->WriteShort(transition_pos_y + (i * 2) + offset, + parent_y_pos * 0x0200)); } // byScreen1 = Transitioning right std::array by_screen1_wide = {0x0060, 0x0060}; - + // Check west neighbor if ((i % 0x40) - 1 >= 0) { auto& west_neighbor = overworld_maps_[i - 1]; - + // From bottom right of large to left of wide - if (west_neighbor.area_size() == AreaSizeEnum::LargeArea && west_neighbor.large_index() == 3) { + if (west_neighbor.area_size() == AreaSizeEnum::LargeArea && + west_neighbor.large_index() == 3) { by_screen1_wide[0] = 0xF060; } // From bottom of tall to left of wide - else if (west_neighbor.area_size() == AreaSizeEnum::TallArea && west_neighbor.large_index() == 2) { + else if (west_neighbor.area_size() == AreaSizeEnum::TallArea && + west_neighbor.large_index() == 2) { by_screen1_wide[0] = 0xF060; } } - + for (int j = 0; j < 2; j++) { - RETURN_IF_ERROR(rom()->WriteShort(screen_change_1 + (i * 2) + offsets[j], by_screen1_wide[j])); + RETURN_IF_ERROR(rom()->WriteShort(screen_change_1 + (i * 2) + offsets[j], + by_screen1_wide[j])); } // byScreen2 = Transitioning left std::array by_screen2_wide = {0x0080, 0x0080}; - + // Check east neighbor if ((i % 0x40) + 2 < 0x40 && i + 2 < kNumOverworldMaps) { auto& east_neighbor = overworld_maps_[i + 2]; - + // From bottom left of large to right of wide - if (east_neighbor.area_size() == AreaSizeEnum::LargeArea && east_neighbor.large_index() == 2) { + if (east_neighbor.area_size() == AreaSizeEnum::LargeArea && + east_neighbor.large_index() == 2) { by_screen2_wide[1] = 0xF080; } // From bottom of tall to right of wide - else if (east_neighbor.area_size() == AreaSizeEnum::TallArea && east_neighbor.large_index() == 2) { + else if (east_neighbor.area_size() == AreaSizeEnum::TallArea && + east_neighbor.large_index() == 2) { by_screen2_wide[1] = 0xF080; } } - + for (int j = 0; j < 2; j++) { - RETURN_IF_ERROR(rom()->WriteShort(screen_change_2 + (i * 2) + offsets[j], by_screen2_wide[j])); + RETURN_IF_ERROR(rom()->WriteShort(screen_change_2 + (i * 2) + offsets[j], + by_screen2_wide[j])); } - // byScreen3 = Transitioning down + // byScreen3 = Transitioning down std::array by_screen3_wide = {0x1800, 0x1840}; - + // Check north neighbor if ((i % 0x40) - 8 >= 0) { auto& north_neighbor = overworld_maps_[i - 8]; - + if (north_neighbor.area_size() == AreaSizeEnum::LargeArea) { switch (north_neighbor.large_index()) { case 2: // From bottom right of large to right of wide @@ -1399,18 +1523,19 @@ absl::Status Overworld::SaveWideAreaTransitions(int i, int parent_x_pos, int par } } } - + for (int j = 0; j < 2; j++) { - RETURN_IF_ERROR(rom()->WriteShort(screen_change_3 + (i * 2) + offsets[j], by_screen3_wide[j])); + RETURN_IF_ERROR(rom()->WriteShort(screen_change_3 + (i * 2) + offsets[j], + by_screen3_wide[j])); } // byScreen4 = Transitioning up std::array by_screen4_wide = {0x1000, 0x1040}; - + // Check south neighbor if ((i % 0x40) + 8 < 0x40 && i + 8 < kNumOverworldMaps) { auto& south_neighbor = overworld_maps_[i + 8]; - + if (south_neighbor.area_size() == AreaSizeEnum::LargeArea) { switch (south_neighbor.large_index()) { case 0: // From top right of large to right of wide @@ -1434,37 +1559,42 @@ absl::Status Overworld::SaveWideAreaTransitions(int i, int parent_x_pos, int par } } } - + for (int j = 0; j < 2; j++) { - RETURN_IF_ERROR(rom()->WriteShort(screen_change_4 + (i * 2) + offsets[j], by_screen4_wide[j])); + RETURN_IF_ERROR(rom()->WriteShort(screen_change_4 + (i * 2) + offsets[j], + by_screen4_wide[j])); } return absl::OkStatus(); } -absl::Status Overworld::SaveTallAreaTransitions(int i, int parent_x_pos, int parent_y_pos, - int transition_target_north, int transition_target_west, - int transition_pos_x, int transition_pos_y, - int screen_change_1, int screen_change_2, - int screen_change_3, int screen_change_4) { +absl::Status Overworld::SaveTallAreaTransitions( + int i, int parent_x_pos, int parent_y_pos, int transition_target_north, + int transition_target_west, int transition_pos_x, int transition_pos_y, + int screen_change_1, int screen_change_2, int screen_change_3, + int screen_change_4) { // Set transition targets for both quadrants const uint16_t offsets[] = {0, 16}; for (auto offset : offsets) { - RETURN_IF_ERROR(rom()->WriteShort(transition_target_north + (i * 2) + offset, - (uint16_t)((parent_y_pos * 0x0200) - 0x00E0))); - RETURN_IF_ERROR(rom()->WriteShort(transition_target_west + (i * 2) + offset, - (uint16_t)((parent_x_pos * 0x0200) - 0x0100))); - RETURN_IF_ERROR(rom()->WriteShort(transition_pos_x + (i * 2) + offset, parent_x_pos * 0x0200)); - RETURN_IF_ERROR(rom()->WriteShort(transition_pos_y + (i * 2) + offset, parent_y_pos * 0x0200)); + RETURN_IF_ERROR( + rom()->WriteShort(transition_target_north + (i * 2) + offset, + (uint16_t)((parent_y_pos * 0x0200) - 0x00E0))); + RETURN_IF_ERROR( + rom()->WriteShort(transition_target_west + (i * 2) + offset, + (uint16_t)((parent_x_pos * 0x0200) - 0x0100))); + RETURN_IF_ERROR(rom()->WriteShort(transition_pos_x + (i * 2) + offset, + parent_x_pos * 0x0200)); + RETURN_IF_ERROR(rom()->WriteShort(transition_pos_y + (i * 2) + offset, + parent_y_pos * 0x0200)); } // byScreen1 = Transitioning right std::array by_screen1_tall = {0x0060, 0x1060}; - + // Check west neighbor if ((i % 0x40) - 1 >= 0) { auto& west_neighbor = overworld_maps_[i - 1]; - + if (west_neighbor.area_size() == AreaSizeEnum::LargeArea) { switch (west_neighbor.large_index()) { case 1: // From bottom right of large to bottom of tall @@ -1485,18 +1615,19 @@ absl::Status Overworld::SaveTallAreaTransitions(int i, int parent_x_pos, int par } } } - + for (int j = 0; j < 2; j++) { - RETURN_IF_ERROR(rom()->WriteShort(screen_change_1 + (i * 2) + offsets[j], by_screen1_tall[j])); + RETURN_IF_ERROR(rom()->WriteShort(screen_change_1 + (i * 2) + offsets[j], + by_screen1_tall[j])); } // byScreen2 = Transitioning left std::array by_screen2_tall = {0x0040, 0x1040}; - + // Check east neighbor if ((i % 0x40) + 1 < 0x40 && i + 1 < kNumOverworldMaps) { auto& east_neighbor = overworld_maps_[i + 1]; - + if (east_neighbor.area_size() == AreaSizeEnum::LargeArea) { switch (east_neighbor.large_index()) { case 0: // From bottom left of large to bottom of tall @@ -1517,51 +1648,58 @@ absl::Status Overworld::SaveTallAreaTransitions(int i, int parent_x_pos, int par } } } - + for (int j = 0; j < 2; j++) { - RETURN_IF_ERROR(rom()->WriteShort(screen_change_2 + (i * 2) + offsets[j], by_screen2_tall[j])); + RETURN_IF_ERROR(rom()->WriteShort(screen_change_2 + (i * 2) + offsets[j], + by_screen2_tall[j])); } // byScreen3 = Transitioning down std::array by_screen3_tall = {0x1800, 0x1800}; - + // Check north neighbor if ((i % 0x40) - 8 >= 0) { auto& north_neighbor = overworld_maps_[i - 8]; - + // From bottom right of large to top of tall - if (north_neighbor.area_size() == AreaSizeEnum::LargeArea && north_neighbor.large_index() == 3) { + if (north_neighbor.area_size() == AreaSizeEnum::LargeArea && + north_neighbor.large_index() == 3) { by_screen3_tall[0] = 0x17C0; } // From right of wide to top of tall - else if (north_neighbor.area_size() == AreaSizeEnum::WideArea && north_neighbor.large_index() == 1) { + else if (north_neighbor.area_size() == AreaSizeEnum::WideArea && + north_neighbor.large_index() == 1) { by_screen3_tall[0] = 0x17C0; } } - + for (int j = 0; j < 2; j++) { - RETURN_IF_ERROR(rom()->WriteShort(screen_change_3 + (i * 2) + offsets[j], by_screen3_tall[j])); + RETURN_IF_ERROR(rom()->WriteShort(screen_change_3 + (i * 2) + offsets[j], + by_screen3_tall[j])); } // byScreen4 = Transitioning up std::array by_screen4_tall = {0x2000, 0x2000}; - + // Check south neighbor if ((i % 0x40) + 16 < 0x40 && i + 16 < kNumOverworldMaps) { auto& south_neighbor = overworld_maps_[i + 16]; - + // From top right of large to bottom of tall - if (south_neighbor.area_size() == AreaSizeEnum::LargeArea && south_neighbor.large_index() == 1) { + if (south_neighbor.area_size() == AreaSizeEnum::LargeArea && + south_neighbor.large_index() == 1) { by_screen4_tall[1] = 0x1FC0; } // From right of wide to bottom of tall - else if (south_neighbor.area_size() == AreaSizeEnum::WideArea && south_neighbor.large_index() == 1) { + else if (south_neighbor.area_size() == AreaSizeEnum::WideArea && + south_neighbor.large_index() == 1) { by_screen4_tall[1] = 0x1FC0; } } - + for (int j = 0; j < 2; j++) { - RETURN_IF_ERROR(rom()->WriteShort(screen_change_4 + (i * 2) + offsets[j], by_screen4_tall[j])); + RETURN_IF_ERROR(rom()->WriteShort(screen_change_4 + (i * 2) + offsets[j], + by_screen4_tall[j])); } return absl::OkStatus(); @@ -1569,7 +1707,7 @@ absl::Status Overworld::SaveTallAreaTransitions(int i, int parent_x_pos, int par absl::Status Overworld::SaveLargeMapsExpanded() { util::logf("Saving Large Maps (v3+ Expanded)"); - + // Use expanded memory locations for v3+ int transition_target_north = zelda3::transition_target_northExpanded; int transition_target_west = zelda3::transition_target_westExpanded; @@ -1585,7 +1723,8 @@ absl::Status Overworld::SaveLargeMapsExpanded() { // Process all overworld maps (0xA0 for v3) for (int i = 0; i < kNumOverworldMaps; ++i) { // Skip if this map was already processed as part of a multi-area structure - if (std::find(checked_map.begin(), checked_map.end(), i) != checked_map.end()) { + if (std::find(checked_map.begin(), checked_map.end(), i) != + checked_map.end()) { continue; } @@ -1599,20 +1738,20 @@ absl::Status Overworld::SaveLargeMapsExpanded() { // Handle transitions based on area size switch (overworld_maps_[i].area_size()) { case AreaSizeEnum::SmallArea: - RETURN_IF_ERROR(SaveSmallAreaTransitions(i, parent_x_pos, parent_y_pos, - transition_target_north, transition_target_west, - transition_pos_x, transition_pos_y, - screen_change_1, screen_change_2, - screen_change_3, screen_change_4)); + RETURN_IF_ERROR(SaveSmallAreaTransitions( + i, parent_x_pos, parent_y_pos, transition_target_north, + transition_target_west, transition_pos_x, transition_pos_y, + screen_change_1, screen_change_2, screen_change_3, + screen_change_4)); checked_map.emplace_back(i); break; case AreaSizeEnum::LargeArea: - RETURN_IF_ERROR(SaveLargeAreaTransitions(i, parent_x_pos, parent_y_pos, - transition_target_north, transition_target_west, - transition_pos_x, transition_pos_y, - screen_change_1, screen_change_2, - screen_change_3, screen_change_4)); + RETURN_IF_ERROR(SaveLargeAreaTransitions( + i, parent_x_pos, parent_y_pos, transition_target_north, + transition_target_west, transition_pos_x, transition_pos_y, + screen_change_1, screen_change_2, screen_change_3, + screen_change_4)); // Mark all 4 quadrants as processed checked_map.emplace_back(i); checked_map.emplace_back(i + 1); @@ -1621,22 +1760,22 @@ absl::Status Overworld::SaveLargeMapsExpanded() { break; case AreaSizeEnum::WideArea: - RETURN_IF_ERROR(SaveWideAreaTransitions(i, parent_x_pos, parent_y_pos, - transition_target_north, transition_target_west, - transition_pos_x, transition_pos_y, - screen_change_1, screen_change_2, - screen_change_3, screen_change_4)); + RETURN_IF_ERROR(SaveWideAreaTransitions( + i, parent_x_pos, parent_y_pos, transition_target_north, + transition_target_west, transition_pos_x, transition_pos_y, + screen_change_1, screen_change_2, screen_change_3, + screen_change_4)); // Mark both horizontal quadrants as processed checked_map.emplace_back(i); checked_map.emplace_back(i + 1); break; case AreaSizeEnum::TallArea: - RETURN_IF_ERROR(SaveTallAreaTransitions(i, parent_x_pos, parent_y_pos, - transition_target_north, transition_target_west, - transition_pos_x, transition_pos_y, - screen_change_1, screen_change_2, - screen_change_3, screen_change_4)); + RETURN_IF_ERROR(SaveTallAreaTransitions( + i, parent_x_pos, parent_y_pos, transition_target_north, + transition_target_west, transition_pos_x, transition_pos_y, + screen_change_1, screen_change_2, screen_change_3, + screen_change_4)); // Mark both vertical quadrants as processed checked_map.emplace_back(i); checked_map.emplace_back(i + 8); @@ -2170,7 +2309,7 @@ absl::Status Overworld::SaveMap16Tiles() { absl::Status Overworld::SaveEntrances() { util::logf("Saving Entrances"); - + // Use expanded entrance tables if available if (expanded_entrances_) { for (int i = 0; i < kNumOverworldEntrances; i++) { @@ -2206,10 +2345,10 @@ absl::Status Overworld::SaveEntrances() { absl::Status Overworld::SaveExits() { util::logf("Saving Exits"); - - // ASM version 0x03 added SW support and the exit leading to Zora's Domain specifically + + // ASM version 0x03 added SW support and the exit leading to Zora's Domain specifically // needs to be updated because its camera values are incorrect. - // We only update it if it was a vanilla ROM though because we don't know if the + // We only update it if it was a vanilla ROM though because we don't know if the // user has already adjusted it or not. uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied]; if (asm_version == 0x00) { @@ -2219,7 +2358,7 @@ absl::Status Overworld::SaveExits() { // all_exits_[0x4D].SpecialUpdatePosition(); // } } - + for (int i = 0; i < kNumOverworldExits; i++) { RETURN_IF_ERROR( rom()->WriteShort(OWExitRoomId + (i * 2), all_exits_[i].room_id_)); @@ -2360,24 +2499,24 @@ absl::Status Overworld::SaveItems() { absl::Status Overworld::SaveMapOverlays() { util::logf("Saving Map Overlays"); - + // Generate the new overlay code that handles interactive overlays std::vector new_overlay_code = { - 0xC2, 0x30, // REP #$30 - 0xA5, 0x8A, // LDA $8A - 0x0A, 0x18, // ASL : CLC - 0x65, 0x8A, // ADC $8A - 0xAA, // TAX - 0xBF, 0x00, 0x00, 0x00, // LDA, X - 0x85, 0x00, // STA $00 - 0xBF, 0x00, 0x00, 0x00, // LDA, X +2 - 0x85, 0x02, // STA $02 - 0x4B, // PHK - 0xF4, 0x00, 0x00, // This position +3 ? - 0xDC, 0x00, 0x00, // JML [$00 00] - 0xE2, 0x30, // SEP #$30 - 0xAB, // PLB - 0x6B, // RTL + 0xC2, 0x30, // REP #$30 + 0xA5, 0x8A, // LDA $8A + 0x0A, 0x18, // ASL : CLC + 0x65, 0x8A, // ADC $8A + 0xAA, // TAX + 0xBF, 0x00, 0x00, 0x00, // LDA, X + 0x85, 0x00, // STA $00 + 0xBF, 0x00, 0x00, 0x00, // LDA, X +2 + 0x85, 0x02, // STA $02 + 0x4B, // PHK + 0xF4, 0x00, 0x00, // This position +3 ? + 0xDC, 0x00, 0x00, // JML [$00 00] + 0xE2, 0x30, // SEP #$30 + 0xAB, // PLB + 0x6B, // RTL }; // Write overlay code to ROM @@ -2399,7 +2538,7 @@ absl::Status Overworld::SaveMapOverlays() { constexpr int kExpandedOverlaySpace = 0x120000; int pos = kExpandedOverlaySpace; int ptr_pos = kOverlayCodeStart + 32; - + for (int i = 0; i < kNumOverworldMaps; i++) { int snes_addr = PcToSnes(pos); RETURN_IF_ERROR(rom()->WriteLong(ptr_pos, snes_addr & 0xFFFFFF)); @@ -2412,7 +2551,8 @@ absl::Status Overworld::SaveMapOverlays() { if (t + 2 < overlay_data.size()) { // Generate LDA/STA sequence for each overlay tile RETURN_IF_ERROR(rom()->WriteByte(pos, 0xA9)); // LDA #$ - RETURN_IF_ERROR(rom()->WriteShort(pos + 1, overlay_data[t] | (overlay_data[t + 1] << 8))); + RETURN_IF_ERROR(rom()->WriteShort( + pos + 1, overlay_data[t] | (overlay_data[t + 1] << 8))); pos += 3; RETURN_IF_ERROR(rom()->WriteByte(pos, 0x8D)); // STA $xxxx @@ -2431,37 +2571,46 @@ absl::Status Overworld::SaveMapOverlays() { absl::Status Overworld::SaveOverworldTilesType() { util::logf("Saving Overworld Tiles Types"); - + for (int i = 0; i < kNumTileTypes; i++) { - RETURN_IF_ERROR(rom()->WriteByte(overworldTilesType + i, all_tiles_types_[i])); + RETURN_IF_ERROR( + rom()->WriteByte(overworldTilesType + i, all_tiles_types_[i])); } return absl::OkStatus(); } -absl::Status Overworld::SaveCustomOverworldASM(bool enable_bg_color, bool enable_main_palette, - bool enable_mosaic, bool enable_gfx_groups, - bool enable_subscreen_overlay, bool enable_animated) { +absl::Status Overworld::SaveCustomOverworldASM(bool enable_bg_color, + bool enable_main_palette, + bool enable_mosaic, + bool enable_gfx_groups, + bool enable_subscreen_overlay, + bool enable_animated) { util::logf("Applying Custom Overworld ASM"); // Set the enable/disable settings uint8_t enable_value = enable_bg_color ? 0xFF : 0x00; - RETURN_IF_ERROR(rom()->WriteByte(OverworldCustomAreaSpecificBGEnabled, enable_value)); + RETURN_IF_ERROR( + rom()->WriteByte(OverworldCustomAreaSpecificBGEnabled, enable_value)); enable_value = enable_main_palette ? 0xFF : 0x00; - RETURN_IF_ERROR(rom()->WriteByte(OverworldCustomMainPaletteEnabled, enable_value)); + RETURN_IF_ERROR( + rom()->WriteByte(OverworldCustomMainPaletteEnabled, enable_value)); enable_value = enable_mosaic ? 0xFF : 0x00; RETURN_IF_ERROR(rom()->WriteByte(OverworldCustomMosaicEnabled, enable_value)); enable_value = enable_gfx_groups ? 0xFF : 0x00; - RETURN_IF_ERROR(rom()->WriteByte(OverworldCustomTileGFXGroupEnabled, enable_value)); + RETURN_IF_ERROR( + rom()->WriteByte(OverworldCustomTileGFXGroupEnabled, enable_value)); enable_value = enable_animated ? 0xFF : 0x00; - RETURN_IF_ERROR(rom()->WriteByte(OverworldCustomAnimatedGFXEnabled, enable_value)); + RETURN_IF_ERROR( + rom()->WriteByte(OverworldCustomAnimatedGFXEnabled, enable_value)); enable_value = enable_subscreen_overlay ? 0xFF : 0x00; - RETURN_IF_ERROR(rom()->WriteByte(OverworldCustomSubscreenOverlayEnabled, enable_value)); + RETURN_IF_ERROR( + rom()->WriteByte(OverworldCustomSubscreenOverlayEnabled, enable_value)); // Write the main palette table for (int i = 0; i < kNumOverworldMaps; i++) { @@ -2474,18 +2623,20 @@ absl::Status Overworld::SaveCustomOverworldASM(bool enable_bg_color, bool enable const auto& mosaic = overworld_maps_[i].mosaic_expanded(); // .... udlr bit format uint8_t mosaic_byte = (mosaic[0] ? 0x08 : 0x00) | // up - (mosaic[1] ? 0x04 : 0x00) | // down + (mosaic[1] ? 0x04 : 0x00) | // down (mosaic[2] ? 0x02 : 0x00) | // left (mosaic[3] ? 0x01 : 0x00); // right - RETURN_IF_ERROR(rom()->WriteByte(OverworldCustomMosaicArray + i, mosaic_byte)); + RETURN_IF_ERROR( + rom()->WriteByte(OverworldCustomMosaicArray + i, mosaic_byte)); } // Write the main and animated gfx tiles table for (int i = 0; i < kNumOverworldMaps; i++) { for (int j = 0; j < 8; j++) { - RETURN_IF_ERROR(rom()->WriteByte(OverworldCustomTileGFXGroupArray + (i * 8) + j, - overworld_maps_[i].custom_tileset(j))); + RETURN_IF_ERROR( + rom()->WriteByte(OverworldCustomTileGFXGroupArray + (i * 8) + j, + overworld_maps_[i].custom_tileset(j))); } RETURN_IF_ERROR(rom()->WriteByte(OverworldCustomAnimatedGFXArray + i, overworld_maps_[i].animated_gfx())); @@ -2493,8 +2644,9 @@ absl::Status Overworld::SaveCustomOverworldASM(bool enable_bg_color, bool enable // Write the subscreen overlay table for (int i = 0; i < kNumOverworldMaps; i++) { - RETURN_IF_ERROR(rom()->WriteShort(OverworldCustomSubscreenOverlayArray + (i * 2), - overworld_maps_[i].subscreen_overlay())); + RETURN_IF_ERROR( + rom()->WriteShort(OverworldCustomSubscreenOverlayArray + (i * 2), + overworld_maps_[i].subscreen_overlay())); } return absl::OkStatus(); @@ -2502,11 +2654,12 @@ absl::Status Overworld::SaveCustomOverworldASM(bool enable_bg_color, bool enable absl::Status Overworld::SaveAreaSpecificBGColors() { util::logf("Saving Area Specific Background Colors"); - + // Write area-specific background colors if enabled for (int i = 0; i < kNumOverworldMaps; i++) { uint16_t bg_color = overworld_maps_[i].area_specific_bg_color(); - RETURN_IF_ERROR(rom()->WriteShort(OverworldCustomAreaSpecificBGPalette + (i * 2), bg_color)); + RETURN_IF_ERROR(rom()->WriteShort( + OverworldCustomAreaSpecificBGPalette + (i * 2), bg_color)); } return absl::OkStatus(); diff --git a/src/app/zelda3/overworld/overworld.h b/src/app/zelda3/overworld/overworld.h index 0aea3fb4..ce4cd67c 100644 --- a/src/app/zelda3/overworld/overworld.h +++ b/src/app/zelda3/overworld/overworld.h @@ -190,6 +190,7 @@ class Overworld { absl::Status SaveMapProperties(); absl::Status SaveMusic(); absl::Status SaveAreaSizes(); + void AssignMapSizes(std::vector& maps); auto rom() const { return rom_; } auto mutable_rom() { return rom_; } diff --git a/src/app/zelda3/overworld/overworld_map.cc b/src/app/zelda3/overworld/overworld_map.cc index eb95652e..01ebde9a 100644 --- a/src/app/zelda3/overworld/overworld_map.cc +++ b/src/app/zelda3/overworld/overworld_map.cc @@ -14,20 +14,20 @@ namespace yaze { namespace zelda3 { -OverworldMap::OverworldMap(int index, Rom *rom) +OverworldMap::OverworldMap(int index, Rom* rom) : index_(index), parent_(index), rom_(rom) { LoadAreaInfo(); - - // Use ASM version byte as source of truth + // Load parent ID from ROM data if available (for custom ASM versions) uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied]; - + if (asm_version != 0xFF && asm_version >= 0x03) { + // For v3+, parent ID is stored in expanded table + parent_ = (*rom_)[kOverworldMapParentIdExpanded + index_]; + } + if (asm_version != 0xFF) { - // Custom overworld ASM is applied - use custom logic - if (asm_version == 0x00) { - // Special case: version 0 means flag-enabled vanilla mode + if (asm_version == 0x03) { LoadCustomOverworldData(); } else { - // Custom overworld ASM applied - set up custom tileset SetupCustomTileset(asm_version); } } else if (core::FeatureFlags::get().overworld.kLoadCustomOverworld) { @@ -38,21 +38,28 @@ OverworldMap::OverworldMap(int index, Rom *rom) } absl::Status OverworldMap::BuildMap(int count, int game_state, int world, - std::vector &tiles16, - OverworldBlockset &world_blockset) { + std::vector& tiles16, + OverworldBlockset& world_blockset) { game_state_ = game_state; world_ = world; - if (large_map_) { + uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied]; + + // For large maps in vanilla ROMs, we need to handle special world graphics + // This ensures proper rendering of special overworld areas like Zora's Domain + if (large_map_ && asm_version == 0xFF) { if (parent_ != index_ && !initialized_) { if (index_ >= kSpecialWorldMapIdStart && index_ <= 0x8A && index_ != 0x88) { + // Most special world areas use the special graphics group area_graphics_ = (*rom_)[kOverworldSpecialGfxGroup + (parent_ - kSpecialWorldMapIdStart)]; area_palette_ = (*rom_)[kOverworldSpecialPalGroup + 1]; } else if (index_ == 0x88) { + // Triforce room has special hardcoded values area_graphics_ = 0x51; area_palette_ = 0x00; } else { + // Fallback to standard area graphics area_graphics_ = (*rom_)[kAreaGfxIdPtr + parent_]; area_palette_ = (*rom_)[kOverworldMapPaletteIds + parent_]; } @@ -74,6 +81,11 @@ absl::Status OverworldMap::BuildMap(int count, int game_state, int world, void OverworldMap::LoadAreaInfo() { uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied]; + // ZSCustomOverworld ASM Version System: + // 0x00-0x02: Legacy versions with limited features + // 0x03: Current version with full area size expansion and custom data support + // 0xFF: Pure vanilla ROM (no ASM applied) + // Load message ID and area size based on ASM version if (asm_version < 3 || asm_version == 0xFF) { // v2 and vanilla: use original message table @@ -83,6 +95,7 @@ void OverworldMap::LoadAreaInfo() { // Load area size for v2/vanilla if (index_ < 0x80) { // For LW and DW, check the screen size byte + // Note: v2 had a bug where large/small values were swapped uint8_t size_byte = (*rom_)[kOverworldScreenSize + (index_ & 0x3F)]; switch (size_byte) { case 0: @@ -101,6 +114,7 @@ void OverworldMap::LoadAreaInfo() { } } else { // For SW, use hardcoded values for v2 compatibility + // Zora's Domain areas (0x81, 0x82, 0x89, 0x8A) are large areas area_size_ = (index_ == 0x81 || index_ == 0x82 || index_ == 0x89 || index_ == 0x8A) ? AreaSizeEnum::LargeArea @@ -108,6 +122,7 @@ void OverworldMap::LoadAreaInfo() { } } else { // v3: use expanded message table and area size table + // All area sizes are now stored in the expanded table, supporting all size types message_id_ = (*rom_)[kOverworldMessagesExpanded + (parent_ * 2)] | ((*rom_)[kOverworldMessagesExpanded + (parent_ * 2) + 1] << 8); @@ -172,10 +187,10 @@ void OverworldMap::LoadAreaInfo() { area_palette_ = (*rom_)[kOverworldMapPaletteIds + parent_]; } } else { - // Special World (SW) areas + // Special World (SW) areas (index >= 0x80) // Message ID already loaded above based on ASM version - // For v3, use expanded sprite tables + // For v3, use expanded sprite tables with full customization support if (asm_version >= 3 && asm_version != 0xFF) { sprite_graphics_[0] = (*rom_)[kOverworldSpecialSpriteGfxGroupExpandedTemp + parent_ - @@ -214,20 +229,24 @@ void OverworldMap::LoadAreaInfo() { area_palette_ = (*rom_)[kOverworldPalettesScreenToSetNew + parent_]; // For v2/vanilla, use original palette table and handle special cases + // These hardcoded cases are needed for vanilla compatibility if (asm_version < 3 || asm_version == 0xFF) { area_palette_ = (*rom_)[kOverworldMapPaletteIds + parent_]; - // Handle special world area cases + // Handle special world area cases based on ZScream documentation if (index_ == 0x88 || index_ == 0x93) { + // Triforce room - special graphics and palette area_graphics_ = 0x51; area_palette_ = 0x00; } else if (index_ == 0x80) { + // Master Sword area - use special graphics group area_graphics_ = (*rom_)[kOverworldSpecialGfxGroup + (parent_ - kSpecialWorldMapIdStart)]; area_palette_ = (*rom_)[kOverworldSpecialPalGroup + 1]; } else if (index_ == 0x81 || index_ == 0x82 || index_ == 0x89 || index_ == 0x8A) { - // Zora's Domain areas - use special sprite graphics + // Zora's Domain areas - use special sprite graphics and area graphics + // Note: These are the large area maps that were causing crashes sprite_graphics_[0] = 0x0E; sprite_graphics_[1] = 0x0E; sprite_graphics_[2] = 0x0E; @@ -253,7 +272,7 @@ void OverworldMap::LoadAreaInfo() { area_graphics_ = (*rom_)[kAreaGfxIdPtr + 0x43]; area_palette_ = (*rom_)[kOverworldMapPaletteIds + 0x43]; } else { - // Default case + // Default case - use basic graphics area_graphics_ = (*rom_)[kAreaGfxIdPtr + 0x00]; area_palette_ = (*rom_)[kOverworldMapPaletteIds + 0x00]; } @@ -578,7 +597,7 @@ void OverworldMap::LoadAreaGraphics() { namespace palette_internal { -absl::Status SetColorsPalette(Rom &rom, int index, gfx::SnesPalette ¤t, +absl::Status SetColorsPalette(Rom& rom, int index, gfx::SnesPalette& current, gfx::SnesPalette main, gfx::SnesPalette animated, gfx::SnesPalette aux1, gfx::SnesPalette aux2, gfx::SnesPalette hud, gfx::SnesColor bgrcolor, @@ -699,7 +718,7 @@ absl::Status SetColorsPalette(Rom &rom, int index, gfx::SnesPalette ¤t, } // namespace palette_internal absl::StatusOr OverworldMap::GetPalette( - const gfx::PaletteGroup &palette_group, int index, int previous_index, + const gfx::PaletteGroup& palette_group, int index, int previous_index, int limit) { if (index == 255) { index = (*rom_)[rom_->version_constants().kOverworldMapPaletteGroup + @@ -713,10 +732,10 @@ absl::StatusOr OverworldMap::GetPalette( absl::Status OverworldMap::LoadPalette() { uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied]; - + int previous_pal_id = 0; int previous_spr_pal_id = 0; - + if (index_ > 0) { // Load previous palette ID based on ASM version if (asm_version < 3 || asm_version == 0xFF) { @@ -725,7 +744,7 @@ absl::Status OverworldMap::LoadPalette() { // v3 uses expanded palette table previous_pal_id = (*rom_)[kOverworldPalettesScreenToSetNew + parent_ - 1]; } - + previous_spr_pal_id = (*rom_)[kOverworldSpritePaletteIds + parent_ - 1]; } @@ -751,12 +770,12 @@ absl::Status OverworldMap::LoadPalette() { pal1 = (*rom_)[rom_->version_constants().kOverworldMapPaletteGroup + (previous_pal_id * 4)]; } - + if (pal2 == 0xFF) { pal2 = (*rom_)[rom_->version_constants().kOverworldMapPaletteGroup + (previous_pal_id * 4) + 1]; } - + if (pal3 == 0xFF) { pal3 = (*rom_)[rom_->version_constants().kOverworldMapPaletteGroup + (previous_pal_id * 4) + 2]; @@ -769,11 +788,14 @@ absl::Status OverworldMap::LoadPalette() { GetPalette(ow_aux_pal_group, pal2, previous_pal_id, 20)); // Set background color based on world type and area-specific settings - bool use_area_specific_bg = (*rom_)[OverworldCustomAreaSpecificBGEnabled] != 0x00; + bool use_area_specific_bg = + (*rom_)[OverworldCustomAreaSpecificBGEnabled] != 0x00; if (use_area_specific_bg) { // Use area-specific background color from custom array - area_specific_bg_color_ = (*rom_)[OverworldCustomAreaSpecificBGPalette + (parent_ * 2)] | - ((*rom_)[OverworldCustomAreaSpecificBGPalette + (parent_ * 2) + 1] << 8); + area_specific_bg_color_ = + (*rom_)[OverworldCustomAreaSpecificBGPalette + (parent_ * 2)] | + ((*rom_)[OverworldCustomAreaSpecificBGPalette + (parent_ * 2) + 1] + << 8); // Convert 15-bit SNES color to palette color bgr = gfx::SnesColor(area_specific_bg_color_); } else { @@ -806,15 +828,16 @@ absl::Status OverworldMap::LoadPalette() { if (pal4 == 0xFF) { pal4 = (*rom_)[kOverworldSpritePaletteGroup + (previous_spr_pal_id * 2)]; } - + if (pal4 == 0xFF) { pal4 = 0; // Fallback to 0 if still 0xFF } - + if (pal5 == 0xFF) { - pal5 = (*rom_)[kOverworldSpritePaletteGroup + (previous_spr_pal_id * 2) + 1]; + pal5 = + (*rom_)[kOverworldSpritePaletteGroup + (previous_spr_pal_id * 2) + 1]; } - + if (pal5 == 0xFF) { pal5 = 0; // Fallback to 0 if still 0xFF } @@ -840,7 +863,7 @@ absl::Status OverworldMap::LoadPalette() { absl::Status OverworldMap::LoadOverlay() { uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied]; - + // Load overlays based on ROM version if (asm_version == 0xFF) { // Vanilla ROM - load overlay from overlay pointers @@ -855,15 +878,15 @@ absl::Status OverworldMap::LoadOverlay() { } absl::Status OverworldMap::LoadVanillaOverlayData() { - + // Load vanilla overlay for this map (interactive overlays for revealing holes/changing elements) int address = (kOverlayPointersBank << 16) + ((*rom_)[kOverlayPointers + (index_ * 2) + 1] << 8) + (*rom_)[kOverlayPointers + (index_ * 2)]; - + // Convert SNES address to PC address address = ((address & 0x7F0000) >> 1) | (address & 0x7FFF); - + // Check if custom overlay code is present if ((*rom_)[kOverlayData1] == 0x6B) { // Use custom overlay data pointer @@ -872,7 +895,7 @@ absl::Status OverworldMap::LoadVanillaOverlayData() { (*rom_)[kOverlayData2 + (index_ * 3)]; address = ((address & 0x7F0000) >> 1) | (address & 0x7FFF); } - + // Validate address if (address >= rom_->size()) { has_overlay_ = false; @@ -880,15 +903,15 @@ absl::Status OverworldMap::LoadVanillaOverlayData() { overlay_data_.clear(); return absl::OkStatus(); } - + // Parse overlay data (interactive overlays) overlay_data_.clear(); uint8_t b = (*rom_)[address]; - + // Parse overlay commands until we hit END (0x60) while (b != 0x60 && address < rom_->size()) { overlay_data_.push_back(b); - + // Handle different overlay commands switch (b) { case 0xA9: // LDA #$ @@ -957,28 +980,28 @@ absl::Status OverworldMap::LoadVanillaOverlayData() { address++; break; } - + if (address < rom_->size()) { b = (*rom_)[address]; } else { break; } } - + // Add the END command if we found it if (b == 0x60) { overlay_data_.push_back(0x60); } - + // Set overlay ID based on map index (simplified) overlay_id_ = index_; has_overlay_ = !overlay_data_.empty(); - + return absl::OkStatus(); } void OverworldMap::ProcessGraphicsBuffer(int index, int static_graphics_offset, - int size, uint8_t *all_gfx) { + int size, uint8_t* all_gfx) { // Ensure we don't go out of bounds int max_offset = static_graphics_offset * size + size; if (max_offset > rom_->graphics_buffer().size()) { @@ -988,7 +1011,7 @@ void OverworldMap::ProcessGraphicsBuffer(int index, int static_graphics_offset, } return; } - + for (int i = 0; i < size; i++) { auto byte = all_gfx[i + (static_graphics_offset * size)]; switch (index) { @@ -1004,8 +1027,9 @@ void OverworldMap::ProcessGraphicsBuffer(int index, int static_graphics_offset, } absl::Status OverworldMap::BuildTileset() { - if (current_gfx_.size() == 0) current_gfx_.resize(0x10000, 0x00); - + if (current_gfx_.size() == 0) + current_gfx_.resize(0x10000, 0x00); + // Process the 8 main graphics sheets (slots 0-7) for (int i = 0; i < 8; i++) { if (static_graphics_[i] != 0) { @@ -1013,7 +1037,7 @@ absl::Status OverworldMap::BuildTileset() { rom_->graphics_buffer().data()); } } - + // Process sprite graphics (slots 8-15) for (int i = 8; i < 16; i++) { if (static_graphics_[i] != 0) { @@ -1021,19 +1045,20 @@ absl::Status OverworldMap::BuildTileset() { rom_->graphics_buffer().data()); } } - + // Process animated graphics if available (slot 16) if (static_graphics_[16] != 0) { ProcessGraphicsBuffer(7, static_graphics_[16], 0x1000, rom_->graphics_buffer().data()); } - + return absl::OkStatus(); } -absl::Status OverworldMap::BuildTiles16Gfx(std::vector &tiles16, +absl::Status OverworldMap::BuildTiles16Gfx(std::vector& tiles16, int count) { - if (current_blockset_.size() == 0) current_blockset_.resize(0x100000, 0x00); + if (current_blockset_.size() == 0) + current_blockset_.resize(0x100000, 0x00); const int offsets[] = {0x00, 0x08, 0x400, 0x408}; auto yy = 0; @@ -1077,7 +1102,7 @@ absl::Status OverworldMap::BuildTiles16Gfx(std::vector &tiles16, return absl::OkStatus(); } -absl::Status OverworldMap::BuildBitmap(OverworldBlockset &world_blockset) { +absl::Status OverworldMap::BuildBitmap(OverworldBlockset& world_blockset) { if (bitmap_data_.size() != 0) { bitmap_data_.clear(); }