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:
@@ -15,7 +15,7 @@ endif
|
||||
!ZS_CUSTOM_OVERWORLD = 1
|
||||
|
||||
if !ZS_CUSTOM_OVERWORLD != 0
|
||||
incsrc "ZSCustomOverworld.asm"
|
||||
incsrc "ZSCustomOverworld_v3.asm"
|
||||
endif
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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", ¤t_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", ¤t_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
|
||||
|
||||
@@ -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
@@ -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_;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ¤t_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_;
|
||||
|
||||
Reference in New Issue
Block a user