Update overworld map properties and overlay functionality

- Changed the included assembly file for custom overworld to version 3 for improved features.
- Enhanced documentation in the overworld loading guide to clarify overlay effects and configurations.
- Refactored MapPropertiesSystem to support overlay previews and improved mosaic controls.
- Added functionality for loading vanilla overlays and displaying overlay descriptions in the editor.
- Updated UI components in OverworldEditor to integrate new overlay settings and preview options.
This commit is contained in:
scawful
2025-09-24 20:16:53 -04:00
parent b633e57ae7
commit 912cc3fb56
8 changed files with 860 additions and 526 deletions

View File

@@ -15,7 +15,7 @@ endif
!ZS_CUSTOM_OVERWORLD = 1
if !ZS_CUSTOM_OVERWORLD != 0
incsrc "ZSCustomOverworld.asm"
incsrc "ZSCustomOverworld_v3.asm"
endif
}

View File

@@ -50,7 +50,7 @@ constexpr int OverworldCustomASMHasBeenApplied = 0x140145;
| Custom Tile Graphics | ❌ | ❌ | ✅ |
| Vanilla Overlays | ✅ | ✅ | ✅ |
**Note:** Subscreen overlays are a shared concept from vanilla ROMs. ZSCustomOverworld v2+ expands on this by adding support for special overworld areas and custom overlay configurations.
**Note:** Subscreen overlays are visual effects (fog, rain, backgrounds, etc.) that are shared between vanilla ROMs and ZSCustomOverworld. ZSCustomOverworld v2+ expands on this by adding support for custom overlay configurations and additional overlay types.
## Overworld Map Structure
@@ -96,31 +96,42 @@ class OverworldMap {
### Understanding Overlays
Overlays in Zelda 3 are **overworld maps from special areas** (maps 0x80-0x9F) that are displayed semi-transparently on top of other maps. This is a shared concept from vanilla ROMs that ZSCustomOverworld expands upon.
Overlays in Zelda 3 are **visual effects** that are displayed over or behind the main overworld map. They include effects like fog, rain, canopy, backgrounds, and other atmospheric elements. Overlays are collections of tile positions and tile IDs that specify where to place specific graphics on the map.
### Special Area Maps (0x80-0x9F)
Special area maps are the overworld maps past the dark world (maps 0x80-0x9F). These include:
The special area maps (0x80-0x9F) contain the actual tile data for overlays. These maps store the graphics that overlays reference and use to create visual effects:
- **0x80-0x8A**: Various special areas (Lost Woods, Skull Woods, etc.)
- **0x8B-0x8F**: Additional special areas
- **0x90-0x9F**: More special areas including Death Mountain variants
- **0x80-0x8F**: Various special area maps containing overlay graphics
- **0x90-0x9F**: Additional special area maps including more overlay graphics
### Overlay ID Mappings
Common overlay IDs reference specific special area maps:
Overlay IDs directly correspond to special area map indices. Common overlay mappings:
| Overlay ID | Special Area Map | Description |
|------------|------------------|-------------|
| 0x009D | 0x8D | Fog 2 (Lost Woods/Skull Woods) |
| 0x0095 | 0x85 | Sky Background (Death Mountain) |
| 0x009C | 0x8C | Lava (Dark World Death Mountain) |
| 0x0096 | 0x86 | Pyramid Background |
| 0x0097 | 0x87 | Fog 1 (Master Sword Area) |
| 0x0093 | 0x83 | Triforce Room Curtains |
| 0x0093 | 0x93 | Triforce Room Curtain |
| 0x0094 | 0x94 | Under the Bridge |
| 0x0095 | 0x95 | Sky Background (LW Death Mountain) |
| 0x0096 | 0x96 | Pyramid Background |
| 0x0097 | 0x97 | First Fog Overlay (Master Sword Area) |
| 0x009C | 0x9C | Lava Background (DW Death Mountain) |
| 0x009D | 0x9D | Second Fog Overlay (Lost Woods/Skull Woods) |
| 0x009E | 0x9E | Tree Canopy (Forest) |
| 0x009F | 0x9F | Rain Effect (Misery Mire) |
### Drawing Order
Overlays are drawn in a specific order based on their type:
- **Background Overlays** (0x95, 0x96, 0x9C): Drawn behind the main map tiles
- **Foreground Overlays** (0x9D, 0x97, 0x93, 0x94, 0x9E, 0x9F): Drawn on top of the main map tiles with transparency
### Vanilla Overlay Loading
In vanilla ROMs, overlays are loaded by parsing SNES assembly-like commands that specify tile positions and IDs:
```cpp
absl::Status LoadVanillaOverlay() {
uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
@@ -136,9 +147,14 @@ absl::Status LoadVanillaOverlay() {
((*rom_)[kOverlayPointers + (index_ * 2) + 1] << 8) +
(*rom_)[kOverlayPointers + (index_ * 2)];
// Parse overlay commands (SNES assembly-like)
// Commands like LDA, LDX, STA, JMP, END (0x60)
// These commands configure which special area map to use as overlay
// Parse overlay commands:
// LDA #$xxxx - Load tile ID into accumulator
// LDX #$xxxx - Load position into X register
// STA $xxxx - Store tile at position
// STA $xxxx,x - Store tile at position + X
// INC A - Increment accumulator (for sequential tiles)
// JMP $xxxx - Jump to another overlay routine
// END (0x60) - End of overlay data
return absl::OkStatus();
}
@@ -387,11 +403,11 @@ class OverworldMap {
**Solution:**
- Verify overlay pointer addresses and SNES-to-PC conversion
- Ensure special area maps (0x80-0x9F) are properly loaded with correct graphics
- Check that overlay ID mappings are correct (e.g., 0x009D → map 0x8D)
- Check that overlay ID mappings are correct (e.g., 0x009D → map 0x9D)
- Verify that overlay preview shows the actual bitmap of the referenced special area map
**Problem:** Overlay preview showing hex data instead of bitmap
**Solution:** Implement proper overlay preview that displays the bitmap of the referenced special area map, not just the overlay command data
**Problem:** Overlay preview showing incorrect information
**Solution:** Ensure overlay preview correctly maps overlay IDs to special area map indices and displays the appropriate bitmap from the special area maps (0x80-0x9F)
### 5. Large Map Problems

View File

@@ -1,9 +1,9 @@
#include "app/editor/overworld/map_properties.h"
#include "app/gui/canvas.h"
#include "app/gui/color.h"
#include "app/gui/icons.h"
#include "app/gui/input.h"
#include "app/gui/style.h"
#include "app/zelda3/overworld/overworld_map.h"
#include "imgui/imgui.h"
@@ -11,11 +11,7 @@ namespace yaze {
namespace editor {
using ImGui::BeginTable;
using ImGui::Button;
using ImGui::Checkbox;
using ImGui::EndTable;
// HOVER_HINT is defined in util/macro.h
using ImGui::SameLine;
using ImGui::Separator;
using ImGui::TableNextColumn;
using ImGui::Text;
@@ -26,7 +22,8 @@ constexpr const char* kGamePartComboString[] = {"Light World", "Dark World", "Sp
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_custom_bg_color_editor, bool& show_overlay_editor,
bool& show_overlay_preview, int& game_state) {
// Enhanced settings table with popup buttons for quick access
if (BeginTable("SimplifiedMapSettings", 8, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit, ImVec2(0, 0), -1)) {
ImGui::TableSetupColumn("World", ImGuiTableColumnFlags_WidthFixed, 100);
@@ -90,19 +87,14 @@ void MapPropertiesSystem::DrawSimplifiedMapSettings(int& current_world, int& cur
HOVER_HINT("Palette Settings");
DrawPalettesPopup(current_map, show_custom_bg_color_editor);
TableNextColumn();
if (ImGui::Button("Overlays", ImVec2(80, 0))) {
ImGui::OpenPopup("OverlaysPopup");
}
HOVER_HINT("Overlay Settings");
DrawOverlaysPopup(current_map, show_overlay_editor);
// Overlays are now integrated into Properties popup
TableNextColumn();
if (ImGui::Button("Properties", ImVec2(90, 0))) {
ImGui::OpenPopup("PropertiesPopup");
}
HOVER_HINT("Map Properties");
DrawPropertiesPopup(current_map, show_map_properties_panel);
DrawPropertiesPopup(current_map, show_map_properties_panel, show_overlay_preview, game_state);
ImGui::EndTable();
}
@@ -336,8 +328,17 @@ void MapPropertiesSystem::DrawGraphicsPopup(int current_map) {
}
ImGui::Separator();
if (ImGui::Button("Tile Graphics (8 sheets)")) {
// This would open the full properties panel
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),
80.f)) {
RefreshMapProperties();
RefreshOverworldMap();
}
}
ImGui::EndPopup();
@@ -353,7 +354,7 @@ void MapPropertiesSystem::DrawPalettesPopup(int current_map, bool& show_custom_b
overworld_->mutable_overworld_map(current_map)->mutable_area_palette(),
kInputFieldSize)) {
RefreshMapProperties();
RefreshMapPalette();
auto status = RefreshMapPalette();
RefreshOverworldMap();
}
@@ -363,7 +364,7 @@ void MapPropertiesSystem::DrawPalettesPopup(int current_map, bool& show_custom_b
overworld_->mutable_overworld_map(current_map)->mutable_main_palette(),
kInputFieldSize)) {
RefreshMapProperties();
RefreshMapPalette();
auto status = RefreshMapPalette();
RefreshOverworldMap();
}
}
@@ -391,73 +392,56 @@ void MapPropertiesSystem::DrawPalettesPopup(int current_map, bool& show_custom_b
}
}
void MapPropertiesSystem::DrawOverlaysPopup(int current_map, bool& show_overlay_editor) {
if (ImGui::BeginPopup("OverlaysPopup")) {
ImGui::Text("Overlay Settings");
ImGui::Separator();
static uint8_t asm_version = (*rom_)[zelda3::OverworldCustomASMHasBeenApplied];
if (asm_version >= 3) {
if (gui::InputHexWord("Subscreen Overlay",
overworld_->mutable_overworld_map(current_map)->mutable_subscreen_overlay(),
kInputFieldSize + 20)) {
RefreshMapProperties();
RefreshOverworldMap();
}
ImGui::Separator();
if (ImGui::Button("Overlay Settings")) {
show_overlay_editor = !show_overlay_editor;
}
} else {
ImGui::Text("Overlays require ZSCustomOverworld v3+");
}
ImGui::EndPopup();
}
}
void MapPropertiesSystem::DrawPropertiesPopup(int current_map, bool& show_map_properties_panel) {
void MapPropertiesSystem::DrawPropertiesPopup(int current_map, bool& show_map_properties_panel,
bool& show_overlay_preview, int& game_state) {
if (ImGui::BeginPopup("PropertiesPopup")) {
ImGui::Text("Map Properties");
ImGui::Text("Map Properties & Overlays");
ImGui::Separator();
if (gui::InputHexWord("Message ID",
overworld_->mutable_overworld_map(current_map)->mutable_message_id(),
// Basic properties
if (gui::InputHexWord("Message ID",
overworld_->mutable_overworld_map(current_map)
->mutable_message_id(),
kInputFieldSize + 20)) {
RefreshMapProperties();
RefreshOverworldMap();
}
if (ImGui::Checkbox("Mosaic Effect",
overworld_->mutable_overworld_map(current_map)->mutable_mosaic())) {
RefreshMapProperties();
RefreshOverworldMap();
}
static int game_state = 0; // This should be passed in or stored
// Mosaic controls with directional support for v2+
DrawMosaicControls(current_map);
// Overlay controls - always accessible, behavior changes by version
ImGui::Separator();
ImGui::Text("Overlay Settings:");
DrawOverlayControls(current_map, show_overlay_preview);
ImGui::SetNextItemWidth(100.f);
if (ImGui::Combo("Game State", &game_state, kGamePartComboString, 3)) {
RefreshMapProperties();
RefreshOverworldMap();
}
static uint8_t asm_version = (*rom_)[zelda3::OverworldCustomASMHasBeenApplied];
if (asm_version != 0xFF) {
static const char *area_size_names[] = {"Small (1x1)", "Large (2x2)", "Wide (2x1)", "Tall (1x2)"};
int current_area_size = static_cast<int>(overworld_->overworld_map(current_map)->area_size());
static const char *area_size_names[] = {"Small (1x1)", "Large (2x2)",
"Wide (2x1)", "Tall (1x2)"};
int current_area_size = static_cast<int>(
overworld_->overworld_map(current_map)->area_size());
ImGui::SetNextItemWidth(120.f);
if (ImGui::Combo("Area Size", &current_area_size, area_size_names, 4)) {
overworld_->mutable_overworld_map(current_map)->SetAreaSize(static_cast<zelda3::AreaSizeEnum>(current_area_size));
overworld_->mutable_overworld_map(current_map)
->SetAreaSize(
static_cast<zelda3::AreaSizeEnum>(current_area_size));
RefreshOverworldMap();
}
}
ImGui::Separator();
if (ImGui::Button("Full Properties Panel")) {
show_map_properties_panel = true;
}
ImGui::EndPopup();
}
}
@@ -482,7 +466,7 @@ void MapPropertiesSystem::DrawBasicPropertiesTab(int current_map) {
overworld_->mutable_overworld_map(current_map)->mutable_area_palette(),
kInputFieldSize)) {
RefreshMapProperties();
RefreshMapPalette();
auto status = RefreshMapPalette();
RefreshOverworldMap();
}
@@ -585,7 +569,7 @@ void MapPropertiesSystem::DrawCustomFeaturesTab(int current_map) {
overworld_->mutable_overworld_map(current_map)->mutable_main_palette(),
kInputFieldSize)) {
RefreshMapProperties();
RefreshMapPalette();
auto status = RefreshMapPalette();
RefreshOverworldMap();
}
}
@@ -663,5 +647,187 @@ absl::Status MapPropertiesSystem::RefreshMapPalette() {
return absl::OkStatus();
}
void MapPropertiesSystem::DrawMosaicControls(int current_map) {
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<bool, 4> 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]);
RefreshMapProperties();
RefreshOverworldMap();
}
}
} else {
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];
// Determine if this is a special overworld map (0x80-0x9F)
bool is_special_overworld_map = (current_map >= 0x80 && current_map < 0xA0);
if (asm_version == 0xFF) {
// Vanilla ROM - read-only overlay info
auto *current_map_ptr = overworld_->overworld_map(current_map);
if (current_map_ptr->has_vanilla_overlay()) {
ImGui::Text("Vanilla Overlay: 0x%04X", current_map_ptr->vanilla_overlay_id());
// Show overlay description
uint16_t overlay_id = current_map_ptr->vanilla_overlay_id();
std::string overlay_desc = GetOverlayDescription(overlay_id);
ImGui::Text("Description: %s", overlay_desc.c_str());
// Preview checkbox for vanilla
if (ImGui::Checkbox("Preview Overlay on Map", &show_overlay_preview)) {
// Toggle overlay preview
}
} else {
ImGui::Text("No vanilla overlay for this map");
}
} else if (is_special_overworld_map && asm_version < 3) {
// Special overworld maps (0x80-0x9F) require ZSCustomOverworld v3+
ImGui::Text("Special overworld maps require ZSCustomOverworld v3+");
ImGui::Text("Current version: v%d", asm_version);
ImGui::Text("Map 0x%02X is a special overworld map", current_map);
} else {
// Light World (0x00-0x3F) and Dark World (0x40-0x7F) maps always support overlays
// Special overworld maps (0x80-0x9F) support overlays with v3+
uint16_t current_overlay = overworld_->mutable_overworld_map(current_map)->subscreen_overlay();
if (gui::InputHexWord("Subscreen Overlay", &current_overlay, kInputFieldSize + 20)) {
overworld_->mutable_overworld_map(current_map)->set_subscreen_overlay(current_overlay);
RefreshMapProperties();
RefreshOverworldMap();
}
// Show overlay description
std::string overlay_desc = GetOverlayDescription(current_overlay);
ImGui::Text("Description: %s", overlay_desc.c_str());
// Preview checkbox
if (ImGui::Checkbox("Preview Overlay on Map", &show_overlay_preview)) {
// Toggle overlay preview
}
// Show version info for special maps
if (is_special_overworld_map) {
ImGui::Text("Special overworld map (v%d)", asm_version);
}
}
}
std::string MapPropertiesSystem::GetOverlayDescription(uint16_t overlay_id) {
if (overlay_id == 0x0093) {
return "Triforce Room Curtain";
} else if (overlay_id == 0x0094) {
return "Under the Bridge";
} else if (overlay_id == 0x0095) {
return "Sky Background (LW Death Mountain)";
} else if (overlay_id == 0x0096) {
return "Pyramid Background";
} else if (overlay_id == 0x0097) {
return "First Fog Overlay (Master Sword Area)";
} else if (overlay_id == 0x009C) {
return "Lava Background (DW Death Mountain)";
} else if (overlay_id == 0x009D) {
return "Second Fog Overlay (Lost Woods/Skull Woods)";
} else if (overlay_id == 0x009E) {
return "Tree Canopy (Forest)";
} else if (overlay_id == 0x009F) {
return "Rain Effect (Misery Mire)";
} else if (overlay_id == 0x00FF) {
return "No Overlay";
} else {
return "Custom overlay";
}
}
void MapPropertiesSystem::DrawOverlayPreviewOnMap(int current_map, int current_world, bool show_overlay_preview) {
if (!show_overlay_preview || !maps_bmp_ || !canvas_) return;
// Get overlay information based on ROM version and map type
uint16_t overlay_id = 0x00FF;
bool has_overlay = false;
static uint8_t asm_version = (*rom_)[zelda3::OverworldCustomASMHasBeenApplied];
bool is_special_overworld_map = (current_map >= 0x80 && current_map < 0xA0);
if (asm_version == 0xFF) {
// Vanilla ROM - use vanilla overlay
auto *current_map_ptr = overworld_->overworld_map(current_map);
if (current_map_ptr->has_vanilla_overlay()) {
overlay_id = current_map_ptr->vanilla_overlay_id();
has_overlay = true;
}
} else if (is_special_overworld_map && asm_version < 3) {
// Special overworld maps require v3+ - no overlay support
return;
} else {
// Light World (0x00-0x3F) and Dark World (0x40-0x7F) maps always support overlays
// Special overworld maps (0x80-0x9F) support overlays with v3+
overlay_id = overworld_->overworld_map(current_map)->subscreen_overlay();
has_overlay = (overlay_id != 0x00FF);
}
if (!has_overlay) return;
// Map 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;
// Get the overlay map's bitmap
const auto &overlay_bitmap = (*maps_bmp_)[overlay_map_index];
if (!overlay_bitmap.is_active()) return;
// Calculate position for overlay preview on the current map
int current_map_x = current_map % 8;
int current_map_y = current_map / 8;
if (current_world == 1) {
current_map_x = (current_map - 0x40) % 8;
current_map_y = (current_map - 0x40) / 8;
} else if (current_world == 2) {
current_map_x = (current_map - 0x80) % 8;
current_map_y = (current_map - 0x80) / 8;
}
int scale = static_cast<int>(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 overlay
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 overlays - lighter
IM_COL32(255, 255, 255, 180); // Foreground overlays - more opaque
// Draw the 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);
}
} // namespace editor
} // namespace yaze

View File

@@ -4,20 +4,22 @@
#include "app/zelda3/overworld/overworld.h"
#include "app/rom.h"
#include "app/gui/canvas.h"
#include "imgui/imgui.h"
namespace yaze {
namespace editor {
class MapPropertiesSystem {
public:
explicit MapPropertiesSystem(zelda3::Overworld* overworld, Rom* rom)
: overworld_(overworld), rom_(rom) {}
explicit MapPropertiesSystem(zelda3::Overworld* overworld, Rom* rom,
std::array<gfx::Bitmap, zelda3::kNumOverworldMaps>* maps_bmp = nullptr,
gui::Canvas* canvas = nullptr)
: overworld_(overworld), rom_(rom), maps_bmp_(maps_bmp), canvas_(canvas) {}
// Main interface methods
void 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_custom_bg_color_editor, bool& show_overlay_editor,
bool& show_overlay_preview, int& game_state);
void DrawMapPropertiesPanel(int current_map, bool& show_map_properties_panel);
@@ -25,6 +27,9 @@ class MapPropertiesSystem {
void DrawOverlayEditor(int current_map, bool& show_overlay_editor);
// Overlay preview functionality
void DrawOverlayPreviewOnMap(int current_map, int current_world, bool show_overlay_preview);
// Context menu integration
void SetupCanvasContextMenu(gui::Canvas& canvas, int current_map, bool current_map_lock,
bool& show_map_properties_panel, bool& show_custom_bg_color_editor,
@@ -34,8 +39,13 @@ class MapPropertiesSystem {
// Property category drawers
void DrawGraphicsPopup(int current_map);
void DrawPalettesPopup(int current_map, bool& show_custom_bg_color_editor);
void DrawOverlaysPopup(int current_map, bool& show_overlay_editor);
void DrawPropertiesPopup(int current_map, bool& show_map_properties_panel);
void DrawPropertiesPopup(int current_map, bool& show_map_properties_panel,
bool& show_overlay_preview, int& game_state);
// Overlay and mosaic functionality
void DrawMosaicControls(int current_map);
void DrawOverlayControls(int current_map, bool& show_overlay_preview);
std::string GetOverlayDescription(uint16_t overlay_id);
// Tab content drawers
void DrawBasicPropertiesTab(int current_map);
@@ -50,6 +60,8 @@ class MapPropertiesSystem {
zelda3::Overworld* overworld_;
Rom* rom_;
std::array<gfx::Bitmap, zelda3::kNumOverworldMaps>* maps_bmp_;
gui::Canvas* canvas_;
// Static constants
static constexpr float kInputFieldSize = 30.f;

File diff suppressed because it is too large Load Diff

View File

@@ -6,6 +6,7 @@
#include "app/editor/graphics/gfx_group_editor.h"
#include "app/editor/graphics/palette_editor.h"
#include "app/editor/overworld/tile16_editor.h"
#include "app/editor/overworld/map_properties.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/tilemap.h"
@@ -77,6 +78,7 @@ class OverworldEditor : public Editor, public gfx::GfxContext {
explicit OverworldEditor(Rom* rom) : rom_(rom) {
type_ = EditorType::kOverworld;
gfx_group_editor_.set_rom(rom);
// MapPropertiesSystem will be initialized after maps_bmp_ and canvas are ready
}
void Initialize() override;
@@ -161,6 +163,8 @@ class OverworldEditor : public Editor, public gfx::GfxContext {
void DrawCustomBackgroundColorEditor();
void DrawOverlayEditor();
void DrawMapLockControls();
void DrawOverlayPreview();
void DrawOverlayPreviewOnMap();
void DrawOverworldContextMenu();
void DrawSimplifiedMapSettings();
void DrawMapPropertiesPanel();
@@ -227,6 +231,10 @@ class OverworldEditor : public Editor, public gfx::GfxContext {
bool show_overlay_editor_ = false;
bool use_area_specific_bg_color_ = false;
bool show_map_properties_panel_ = false;
bool show_overlay_preview_ = false;
// Map properties system for UI organization
std::unique_ptr<MapPropertiesSystem> map_properties_system_;
gfx::Tilemap tile16_blockset_;

View File

@@ -58,6 +58,7 @@ absl::Status OverworldMap::BuildMap(int count, int game_state, int world,
RETURN_IF_ERROR(BuildTileset())
RETURN_IF_ERROR(BuildTiles16Gfx(tiles16, count))
RETURN_IF_ERROR(LoadPalette());
RETURN_IF_ERROR(LoadVanillaOverlay());
RETURN_IF_ERROR(BuildBitmap(world_blockset))
built_ = true;
return absl::OkStatus();
@@ -824,6 +825,138 @@ absl::Status OverworldMap::LoadPalette() {
return absl::OkStatus();
}
absl::Status OverworldMap::LoadVanillaOverlay() {
uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
// Only load vanilla overlays if this is a vanilla ROM (asm_version == 0xFF)
if (asm_version != 0xFF) {
has_vanilla_overlay_ = false;
vanilla_overlay_id_ = 0;
vanilla_overlay_data_.clear();
return absl::OkStatus();
}
// Load vanilla overlay for this map
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
address = ((*rom_)[kOverlayData2 + 2 + (index_ * 3)] << 16) +
((*rom_)[kOverlayData2 + 1 + (index_ * 3)] << 8) +
(*rom_)[kOverlayData2 + (index_ * 3)];
address = ((address & 0x7F0000) >> 1) | (address & 0x7FFF);
}
// Validate address
if (address >= rom_->size()) {
has_vanilla_overlay_ = false;
vanilla_overlay_id_ = 0;
vanilla_overlay_data_.clear();
return absl::OkStatus();
}
// Parse overlay data
vanilla_overlay_data_.clear();
uint8_t b = (*rom_)[address];
// Parse overlay commands until we hit END (0x60)
while (b != 0x60 && address < rom_->size()) {
vanilla_overlay_data_.push_back(b);
// Handle different overlay commands
switch (b) {
case 0xA9: // LDA #$
if (address + 2 < rom_->size()) {
vanilla_overlay_data_.push_back((*rom_)[address + 1]);
vanilla_overlay_data_.push_back((*rom_)[address + 2]);
address += 3;
} else {
address++;
}
break;
case 0xA2: // LDX #$
if (address + 2 < rom_->size()) {
vanilla_overlay_data_.push_back((*rom_)[address + 1]);
vanilla_overlay_data_.push_back((*rom_)[address + 2]);
address += 3;
} else {
address++;
}
break;
case 0x8D: // STA $xxxx
if (address + 3 < rom_->size()) {
vanilla_overlay_data_.push_back((*rom_)[address + 1]);
vanilla_overlay_data_.push_back((*rom_)[address + 2]);
vanilla_overlay_data_.push_back((*rom_)[address + 3]);
address += 4;
} else {
address++;
}
break;
case 0x9D: // STA $xxxx,x
if (address + 3 < rom_->size()) {
vanilla_overlay_data_.push_back((*rom_)[address + 1]);
vanilla_overlay_data_.push_back((*rom_)[address + 2]);
vanilla_overlay_data_.push_back((*rom_)[address + 3]);
address += 4;
} else {
address++;
}
break;
case 0x8F: // STA $xxxxxx
if (address + 4 < rom_->size()) {
vanilla_overlay_data_.push_back((*rom_)[address + 1]);
vanilla_overlay_data_.push_back((*rom_)[address + 2]);
vanilla_overlay_data_.push_back((*rom_)[address + 3]);
vanilla_overlay_data_.push_back((*rom_)[address + 4]);
address += 5;
} else {
address++;
}
break;
case 0x1A: // INC A
address++;
break;
case 0x4C: // JMP
if (address + 3 < rom_->size()) {
vanilla_overlay_data_.push_back((*rom_)[address + 1]);
vanilla_overlay_data_.push_back((*rom_)[address + 2]);
vanilla_overlay_data_.push_back((*rom_)[address + 3]);
address += 4;
} else {
address++;
}
break;
default:
address++;
break;
}
if (address < rom_->size()) {
b = (*rom_)[address];
} else {
break;
}
}
// Add the END command if we found it
if (b == 0x60) {
vanilla_overlay_data_.push_back(0x60);
}
// Set overlay ID based on map index (simplified)
vanilla_overlay_id_ = index_;
has_vanilla_overlay_ = !vanilla_overlay_data_.empty();
return absl::OkStatus();
}
void OverworldMap::ProcessGraphicsBuffer(int index, int static_graphics_offset,
int size, uint8_t *all_gfx) {
// Ensure we don't go out of bounds

View File

@@ -36,6 +36,13 @@ constexpr int OverworldCustomTileGFXGroupEnabled = 0x140148; // 1 byte, not 0 i
constexpr int OverworldCustomMosaicArray = 0x140200; // 1 byte for each overworld area (0xA0)
constexpr int OverworldCustomMosaicEnabled = 0x140142; // 1 byte, not 0 if enabled
// Vanilla overlay constants
constexpr int kOverlayPointers = 0x77664; // 2 bytes for each overworld area (0x100)
constexpr int kOverlayPointersBank = 0x0E; // Bank for overlay pointers
constexpr int kOverlayData1 = 0x77676; // Check for custom overlay code
constexpr int kOverlayData2 = 0x77677; // Custom overlay data pointer
constexpr int kOverlayCodeStart = 0x77657; // Start of overlay code
// 1 byte for each overworld area (0xA0)
constexpr int OverworldCustomMainPaletteArray = 0x140160;
// 1 byte, not 0 if enabled
@@ -98,6 +105,7 @@ class OverworldMap : public gfx::GfxContext {
void LoadAreaGraphics();
absl::Status LoadPalette();
absl::Status LoadVanillaOverlay();
absl::Status BuildTileset();
absl::Status BuildTiles16Gfx(std::vector<gfx::Tile16>& tiles16, int count);
absl::Status BuildBitmap(OverworldBlockset& world_blockset);
@@ -139,6 +147,15 @@ class OverworldMap : public gfx::GfxContext {
void set_animated_gfx(uint8_t gfx) { animated_gfx_ = gfx; }
auto custom_tileset(int index) const { return custom_gfx_ids_[index]; }
// Vanilla overlay accessors
auto vanilla_overlay_id() const { return vanilla_overlay_id_; }
auto has_vanilla_overlay() const { return has_vanilla_overlay_; }
const auto& vanilla_overlay_data() const { return vanilla_overlay_data_; }
// Mosaic expanded accessors
const std::array<bool, 4>& mosaic_expanded() const { return mosaic_expanded_; }
void set_mosaic_expanded(int index, bool value) { mosaic_expanded_[index] = value; }
void set_custom_tileset(int index, uint8_t value) { custom_gfx_ids_[index] = value; }
auto mutable_current_graphics() { return &current_gfx_; }
@@ -216,6 +233,9 @@ class OverworldMap : public gfx::GfxContext {
static_graphics_.fill(0);
mosaic_expanded_.fill(false);
area_size_ = AreaSizeEnum::SmallArea;
vanilla_overlay_id_ = 0;
has_vanilla_overlay_ = false;
vanilla_overlay_data_.clear();
}
private:
@@ -267,6 +287,11 @@ class OverworldMap : public gfx::GfxContext {
std::array<bool, 4> mosaic_expanded_;
// Vanilla overlay support
uint16_t vanilla_overlay_id_ = 0;
bool has_vanilla_overlay_ = false;
std::vector<uint8_t> vanilla_overlay_data_;
std::vector<uint8_t> current_blockset_;
std::vector<uint8_t> current_gfx_;
std::vector<uint8_t> bitmap_data_;