refactor: Remove OverworldEditorManager and GraphicsManager for Simplification
- Deleted OverworldEditorManager and OverworldGraphicsManager classes to streamline the codebase and reduce complexity in the overworld editor. - Updated CMake configuration to remove references to the deleted files, ensuring a clean build environment. - Adjusted OverworldEditor to handle graphics management directly, improving maintainability and reducing the number of dependencies. - Enhanced the initialization and update methods in OverworldEditor to accommodate the removal of the manager classes, ensuring continued functionality. - Cleaned up related header files to reflect the removal of obsolete classes and methods, enhancing clarity and organization.
This commit is contained in:
@@ -18,7 +18,6 @@ set(
|
||||
app/editor/dungeon/dungeon_room_loader.cc
|
||||
app/editor/dungeon/dungeon_usage_tracker.cc
|
||||
app/editor/overworld/overworld_editor.cc
|
||||
app/editor/overworld/overworld_editor_manager.cc
|
||||
app/editor/overworld/scratch_space.cc
|
||||
app/editor/sprite/sprite_editor.cc
|
||||
app/editor/music/music_editor.cc
|
||||
@@ -36,7 +35,6 @@ set(
|
||||
app/editor/graphics/gfx_group_editor.cc
|
||||
app/editor/overworld/entity.cc
|
||||
app/editor/overworld/overworld_entity_renderer.cc
|
||||
app/editor/overworld/overworld_graphics_manager.cc
|
||||
app/editor/system/settings_editor.cc
|
||||
app/editor/system/command_manager.cc
|
||||
app/editor/system/extension_manager.cc
|
||||
|
||||
@@ -59,10 +59,6 @@ void OverworldEditor::Initialize() {
|
||||
[this](int map_index) { this->ForceRefreshGraphics(map_index); }
|
||||
);
|
||||
|
||||
// Initialize OverworldEditorManager for v3 features
|
||||
overworld_manager_ =
|
||||
std::make_unique<OverworldEditorManager>(&overworld_, rom_, this);
|
||||
|
||||
// Initialize OverworldEntityRenderer for entity visualization
|
||||
entity_renderer_ = std::make_unique<OverworldEntityRenderer>(
|
||||
&overworld_, &ow_map_canvas_, &sprite_previews_);
|
||||
@@ -220,14 +216,6 @@ absl::Status OverworldEditor::Update() {
|
||||
usage_stats_card.End();
|
||||
}
|
||||
|
||||
// v3 Settings popup
|
||||
if (show_v3_settings_ && v3_settings_card.Begin(&show_v3_settings_)) {
|
||||
if (rom_->is_loaded()) {
|
||||
status_ = overworld_manager_->DrawV3SettingsPanel();
|
||||
}
|
||||
v3_settings_card.End();
|
||||
}
|
||||
|
||||
// Area Configuration Panel (detailed editing)
|
||||
if (show_map_properties_panel_) {
|
||||
ImGui::SetNextWindowSize(ImVec2(650, 750), ImGuiCond_FirstUseEver);
|
||||
@@ -1397,9 +1385,26 @@ absl::Status OverworldEditor::LoadGraphics() {
|
||||
}
|
||||
}
|
||||
|
||||
// Store remaining maps for lazy texture creation
|
||||
deferred_map_textures_.assign(maps_to_texture.begin() + initial_texture_count,
|
||||
maps_to_texture.end());
|
||||
// Queue remaining maps for progressive loading via Arena
|
||||
// Priority based on current world (0 = current world, 11+ = other worlds)
|
||||
for (size_t i = initial_texture_count; i < maps_to_texture.size(); ++i) {
|
||||
// Determine priority based on which world this map belongs to
|
||||
int map_index = -1;
|
||||
for (int j = 0; j < zelda3::kNumOverworldMaps; ++j) {
|
||||
if (&maps_bmp_[j] == maps_to_texture[i]) {
|
||||
map_index = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int priority = 15; // Default low priority
|
||||
if (map_index >= 0) {
|
||||
int map_world = map_index / 0x40;
|
||||
priority = (map_world == current_world_) ? 5 : 15; // Current world = priority 5, others = 15
|
||||
}
|
||||
|
||||
gfx::Arena::Get().QueueDeferredTexture(maps_to_texture[i], priority);
|
||||
}
|
||||
|
||||
if (core::FeatureFlags::get().overworld.kDrawOverworldSprites) {
|
||||
{
|
||||
@@ -1433,75 +1438,29 @@ absl::Status OverworldEditor::LoadSpriteGraphics() {
|
||||
}
|
||||
|
||||
void OverworldEditor::ProcessDeferredTextures() {
|
||||
std::lock_guard<std::mutex> lock(deferred_textures_mutex_);
|
||||
|
||||
if (deferred_map_textures_.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Priority-based loading: process more textures for visible maps
|
||||
const int textures_per_frame = 8; // Increased from 2 to 8 for faster loading
|
||||
int processed = 0;
|
||||
|
||||
// First pass: prioritize textures for the current world
|
||||
auto it = deferred_map_textures_.begin();
|
||||
while (it != deferred_map_textures_.end() && processed < textures_per_frame) {
|
||||
if (*it && !(*it)->texture()) {
|
||||
// Check if this texture belongs to the current world
|
||||
int map_index = -1;
|
||||
for (int i = 0; i < zelda3::kNumOverworldMaps; ++i) {
|
||||
if (&maps_bmp_[i] == *it) {
|
||||
map_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_current_world = false;
|
||||
if (map_index >= 0) {
|
||||
int map_world = map_index / 0x40; // 64 maps per world
|
||||
is_current_world = (map_world == current_world_);
|
||||
}
|
||||
|
||||
// Prioritize current world maps, but also process others if we have capacity
|
||||
if (is_current_world || processed < textures_per_frame / 2) {
|
||||
Renderer::Get().RenderBitmap(*it);
|
||||
processed++;
|
||||
it = deferred_map_textures_.erase(
|
||||
it); // Remove immediately after processing
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
} else {
|
||||
++it;
|
||||
// Use Arena's centralized progressive loading system
|
||||
// This makes progressive loading available to all editors
|
||||
auto batch = gfx::Arena::Get().GetNextDeferredTextureBatch(4, 2);
|
||||
|
||||
for (auto* bitmap : batch) {
|
||||
if (bitmap && !bitmap->texture()) {
|
||||
Renderer::Get().RenderBitmap(bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass: process remaining textures if we still have capacity
|
||||
if (processed < textures_per_frame) {
|
||||
it = deferred_map_textures_.begin();
|
||||
while (it != deferred_map_textures_.end() &&
|
||||
processed < textures_per_frame) {
|
||||
if (*it && !(*it)->texture()) {
|
||||
Renderer::Get().RenderBitmap(*it);
|
||||
processed++;
|
||||
it = deferred_map_textures_.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Third pass: process deferred map refreshes for visible maps
|
||||
if (processed < textures_per_frame) {
|
||||
for (int i = 0;
|
||||
i < zelda3::kNumOverworldMaps && processed < textures_per_frame; ++i) {
|
||||
if (maps_bmp_[i].modified() && maps_bmp_[i].is_active()) {
|
||||
// Check if this map is visible
|
||||
bool is_visible = (i == current_map_) || (i / 0x40 == current_world_);
|
||||
if (is_visible) {
|
||||
RefreshOverworldMapOnDemand(i);
|
||||
processed++;
|
||||
}
|
||||
|
||||
// Also process deferred map refreshes for modified maps
|
||||
int refresh_count = 0;
|
||||
const int max_refreshes_per_frame = 2;
|
||||
|
||||
for (int i = 0; i < zelda3::kNumOverworldMaps && refresh_count < max_refreshes_per_frame; ++i) {
|
||||
if (maps_bmp_[i].modified() && maps_bmp_[i].is_active()) {
|
||||
// Check if this map is in current world (prioritize)
|
||||
bool is_current_world = (i / 0x40 == current_world_);
|
||||
bool is_current_map = (i == current_map_);
|
||||
|
||||
if (is_current_map || is_current_world) {
|
||||
RefreshOverworldMapOnDemand(i);
|
||||
refresh_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1539,14 +1498,7 @@ void OverworldEditor::EnsureMapTexture(int map_index) {
|
||||
|
||||
if (!bitmap.texture() && bitmap.is_active()) {
|
||||
Renderer::Get().RenderBitmap(&bitmap);
|
||||
|
||||
// Remove from deferred list if it was there
|
||||
std::lock_guard<std::mutex> lock(deferred_textures_mutex_);
|
||||
auto it = std::find(deferred_map_textures_.begin(),
|
||||
deferred_map_textures_.end(), &bitmap);
|
||||
if (it != deferred_map_textures_.end()) {
|
||||
deferred_map_textures_.erase(it);
|
||||
}
|
||||
// Note: Arena automatically removes from deferred queue when textures are created
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include "app/gui/input.h"
|
||||
#include "app/rom.h"
|
||||
#include "app/zelda3/overworld/overworld.h"
|
||||
#include "app/editor/overworld/overworld_editor_manager.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include <mutex>
|
||||
|
||||
@@ -273,7 +272,6 @@ class OverworldEditor : public Editor, public gfx::GfxContext {
|
||||
|
||||
// Map properties system for UI organization
|
||||
std::unique_ptr<MapPropertiesSystem> map_properties_system_;
|
||||
std::unique_ptr<OverworldEditorManager> overworld_manager_;
|
||||
std::unique_ptr<OverworldEntityRenderer> entity_renderer_;
|
||||
|
||||
// Scratch space for large layouts
|
||||
@@ -313,8 +311,7 @@ class OverworldEditor : public Editor, public gfx::GfxContext {
|
||||
std::vector<gfx::Bitmap> sprite_previews_;
|
||||
|
||||
// Deferred texture creation for performance optimization
|
||||
std::vector<gfx::Bitmap*> deferred_map_textures_;
|
||||
std::mutex deferred_textures_mutex_;
|
||||
// Deferred texture management now handled by gfx::Arena::Get()
|
||||
|
||||
zelda3::Overworld overworld_{rom_};
|
||||
zelda3::OverworldBlockset refresh_blockset_;
|
||||
|
||||
@@ -1,423 +0,0 @@
|
||||
#include "overworld_editor_manager.h"
|
||||
|
||||
#include "app/gfx/snes_color.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/input.h"
|
||||
#include "app/gui/style.h"
|
||||
#include "app/zelda3/overworld/overworld_map.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
|
||||
using namespace ImGui;
|
||||
|
||||
absl::Status OverworldEditorManager::DrawV3SettingsPanel() {
|
||||
if (BeginTabItem("v3 Settings")) {
|
||||
Text("ZSCustomOverworld v3 Settings");
|
||||
Separator();
|
||||
|
||||
// Check if custom ASM is applied
|
||||
uint8_t asm_version = GetCustomASMVersion();
|
||||
if (asm_version >= 3 && asm_version != 0xFF) {
|
||||
TextColored(ImVec4(0, 1, 0, 1), "Custom Overworld ASM v%d Applied", asm_version);
|
||||
} else if (asm_version == 0x00) {
|
||||
TextColored(ImVec4(1, 1, 0, 1), "Vanilla ROM - Custom features available via flag");
|
||||
} else {
|
||||
TextColored(ImVec4(1, 0, 0, 1), "Custom ASM v%d - Consider upgrading to v3", asm_version);
|
||||
}
|
||||
|
||||
Separator();
|
||||
|
||||
RETURN_IF_ERROR(DrawCustomOverworldSettings());
|
||||
RETURN_IF_ERROR(DrawAreaSpecificSettings());
|
||||
RETURN_IF_ERROR(DrawTransitionSettings());
|
||||
RETURN_IF_ERROR(DrawOverlaySettings());
|
||||
|
||||
EndTabItem();
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status OverworldEditorManager::DrawCustomOverworldSettings() {
|
||||
if (TreeNode("Custom Overworld Features")) {
|
||||
RETURN_IF_ERROR(DrawBooleanSetting("Enable Area-Specific Background Colors",
|
||||
&enable_area_specific_bg_,
|
||||
"Allows each overworld area to have its own background color"));
|
||||
|
||||
RETURN_IF_ERROR(DrawBooleanSetting("Enable Main Palette Override",
|
||||
&enable_main_palette_,
|
||||
"Allows each area to override the main palette"));
|
||||
|
||||
RETURN_IF_ERROR(DrawBooleanSetting("Enable Mosaic Transitions",
|
||||
&enable_mosaic_,
|
||||
"Enables mosaic screen transitions between areas"));
|
||||
|
||||
RETURN_IF_ERROR(DrawBooleanSetting("Enable Custom GFX Groups",
|
||||
&enable_gfx_groups_,
|
||||
"Allows each area to have custom tile GFX groups"));
|
||||
|
||||
RETURN_IF_ERROR(DrawBooleanSetting("Enable Subscreen Overlays",
|
||||
&enable_subscreen_overlay_,
|
||||
"Enables custom subscreen overlays (fog, sky, etc.)"));
|
||||
|
||||
RETURN_IF_ERROR(DrawBooleanSetting("Enable Animated GFX Override",
|
||||
&enable_animated_gfx_,
|
||||
"Allows each area to have custom animated tiles"));
|
||||
|
||||
Separator();
|
||||
|
||||
if (Button("Apply Custom Overworld ASM")) {
|
||||
RETURN_IF_ERROR(ApplyCustomOverworldASM());
|
||||
}
|
||||
SameLine();
|
||||
HOVER_HINT("Writes the custom overworld settings to ROM");
|
||||
|
||||
TreePop();
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status OverworldEditorManager::DrawAreaSpecificSettings() {
|
||||
if (TreeNode("Area-Specific Settings")) {
|
||||
// Map selection
|
||||
int map_count = zelda3::kNumOverworldMaps;
|
||||
SliderInt("Map Index", ¤t_map_index_, 0, map_count - 1);
|
||||
|
||||
auto* current_map = overworld_->mutable_overworld_map(current_map_index_);
|
||||
|
||||
// Area size controls
|
||||
RETURN_IF_ERROR(DrawAreaSizeControls());
|
||||
|
||||
// Background color
|
||||
if (enable_area_specific_bg_) {
|
||||
uint16_t bg_color = current_map->area_specific_bg_color();
|
||||
RETURN_IF_ERROR(DrawColorPicker("Background Color", &bg_color));
|
||||
current_map->set_area_specific_bg_color(bg_color);
|
||||
}
|
||||
|
||||
// Main palette
|
||||
if (enable_main_palette_) {
|
||||
uint8_t main_palette = current_map->main_palette();
|
||||
SliderInt("Main Palette", (int*)&main_palette, 0, 5);
|
||||
current_map->set_main_palette(main_palette);
|
||||
}
|
||||
|
||||
// Mosaic settings
|
||||
if (enable_mosaic_) {
|
||||
RETURN_IF_ERROR(DrawMosaicControls());
|
||||
}
|
||||
|
||||
// GFX groups
|
||||
if (enable_gfx_groups_) {
|
||||
RETURN_IF_ERROR(DrawGfxGroupControls());
|
||||
}
|
||||
|
||||
// Subscreen overlay
|
||||
if (enable_subscreen_overlay_) {
|
||||
uint16_t overlay = current_map->subscreen_overlay();
|
||||
RETURN_IF_ERROR(DrawOverlaySetting("Subscreen Overlay", &overlay));
|
||||
current_map->set_subscreen_overlay(overlay);
|
||||
}
|
||||
|
||||
// Animated GFX
|
||||
if (enable_animated_gfx_) {
|
||||
uint8_t animated_gfx = current_map->animated_gfx();
|
||||
RETURN_IF_ERROR(DrawGfxGroupSetting("Animated GFX", &animated_gfx));
|
||||
current_map->set_animated_gfx(animated_gfx);
|
||||
}
|
||||
|
||||
TreePop();
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status OverworldEditorManager::DrawAreaSizeControls() {
|
||||
auto* current_map = overworld_->mutable_overworld_map(current_map_index_);
|
||||
|
||||
const char* area_size_names[] = {"Small", "Large", "Wide", "Tall"};
|
||||
int current_size = static_cast<int>(current_map->area_size());
|
||||
|
||||
if (Combo("Area Size", ¤t_size, area_size_names, 4)) {
|
||||
current_map->SetAreaSize(static_cast<zelda3::AreaSizeEnum>(current_size));
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status OverworldEditorManager::DrawMosaicControls() {
|
||||
auto* current_map = overworld_->mutable_overworld_map(current_map_index_);
|
||||
const auto& mosaic = current_map->mosaic_expanded();
|
||||
|
||||
bool mosaic_up = mosaic[0];
|
||||
bool mosaic_down = mosaic[1];
|
||||
bool mosaic_left = mosaic[2];
|
||||
bool mosaic_right = mosaic[3];
|
||||
|
||||
if (Checkbox("Mosaic Up", &mosaic_up)) {
|
||||
current_map->set_mosaic_expanded(0, mosaic_up);
|
||||
}
|
||||
SameLine();
|
||||
if (Checkbox("Mosaic Down", &mosaic_down)) {
|
||||
current_map->set_mosaic_expanded(1, mosaic_down);
|
||||
}
|
||||
if (Checkbox("Mosaic Left", &mosaic_left)) {
|
||||
current_map->set_mosaic_expanded(2, mosaic_left);
|
||||
}
|
||||
SameLine();
|
||||
if (Checkbox("Mosaic Right", &mosaic_right)) {
|
||||
current_map->set_mosaic_expanded(3, mosaic_right);
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status OverworldEditorManager::DrawGfxGroupControls() {
|
||||
auto* current_map = overworld_->mutable_overworld_map(current_map_index_);
|
||||
|
||||
Text("Custom Tile GFX Groups:");
|
||||
for (int i = 0; i < 8; i++) {
|
||||
uint8_t gfx_id = current_map->custom_tileset(i);
|
||||
std::string label = "GFX " + std::to_string(i);
|
||||
RETURN_IF_ERROR(DrawGfxGroupSetting(label.c_str(), &gfx_id));
|
||||
current_map->set_custom_tileset(i, gfx_id);
|
||||
if (i < 7) SameLine();
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status OverworldEditorManager::DrawTransitionSettings() {
|
||||
if (TreeNode("Transition Settings")) {
|
||||
Text("Complex area transition calculations are automatically handled");
|
||||
Text("based on neighboring area sizes (Large, Wide, Tall, Small).");
|
||||
|
||||
if (GetCustomASMVersion() >= 3) {
|
||||
TextColored(ImVec4(0, 1, 0, 1), "Using v3+ enhanced transitions");
|
||||
} else {
|
||||
TextColored(ImVec4(1, 1, 0, 1), "Using vanilla/v2 transitions");
|
||||
}
|
||||
|
||||
TreePop();
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status OverworldEditorManager::DrawOverlaySettings() {
|
||||
if (TreeNode("Interactive Overlay Settings")) {
|
||||
Text("Interactive overlays reveal holes and change map elements.");
|
||||
|
||||
auto* current_map = overworld_->mutable_overworld_map(current_map_index_);
|
||||
|
||||
Text("Map %d has %s", current_map_index_,
|
||||
current_map->has_overlay() ? "interactive overlay" : "no overlay");
|
||||
|
||||
if (current_map->has_overlay()) {
|
||||
Text("Overlay ID: 0x%04X", current_map->overlay_id());
|
||||
Text("Overlay data size: %zu bytes", current_map->overlay_data().size());
|
||||
}
|
||||
|
||||
TreePop();
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status OverworldEditorManager::ApplyCustomOverworldASM() {
|
||||
return overworld_->SaveCustomOverworldASM(
|
||||
enable_area_specific_bg_, enable_main_palette_, enable_mosaic_,
|
||||
enable_gfx_groups_, enable_subscreen_overlay_, enable_animated_gfx_);
|
||||
}
|
||||
|
||||
bool OverworldEditorManager::ValidateV3Compatibility() {
|
||||
uint8_t asm_version = GetCustomASMVersion();
|
||||
return (asm_version >= 3 && asm_version != 0xFF);
|
||||
}
|
||||
|
||||
bool OverworldEditorManager::CheckCustomASMApplied() {
|
||||
uint8_t asm_version = GetCustomASMVersion();
|
||||
return (asm_version != 0xFF && asm_version != 0x00);
|
||||
}
|
||||
|
||||
uint8_t OverworldEditorManager::GetCustomASMVersion() {
|
||||
return (*rom_)[zelda3::OverworldCustomASMHasBeenApplied];
|
||||
}
|
||||
|
||||
absl::Status OverworldEditorManager::DrawBooleanSetting(const char* label, bool* setting,
|
||||
const char* help_text) {
|
||||
Checkbox(label, setting);
|
||||
if (help_text && IsItemHovered()) {
|
||||
SetTooltip("%s", help_text);
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status OverworldEditorManager::DrawColorPicker(const char* label, uint16_t* color) {
|
||||
gfx::SnesColor snes_color(*color);
|
||||
ImVec4 imgui_color = snes_color.rgb();
|
||||
|
||||
if (ColorEdit3(label, &imgui_color.x)) {
|
||||
gfx::SnesColor new_color;
|
||||
new_color.set_rgb(imgui_color);
|
||||
*color = new_color.snes();
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status OverworldEditorManager::DrawOverlaySetting(const char* label, uint16_t* overlay) {
|
||||
int overlay_int = *overlay;
|
||||
if (InputInt(label, &overlay_int, 1, 16, ImGuiInputTextFlags_CharsHexadecimal)) {
|
||||
*overlay = static_cast<uint16_t>(overlay_int & 0xFFFF);
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status OverworldEditorManager::DrawGfxGroupSetting(const char* label, uint8_t* gfx_id,
|
||||
int max_value) {
|
||||
int gfx_int = *gfx_id;
|
||||
if (SliderInt(label, &gfx_int, 0, max_value)) {
|
||||
*gfx_id = static_cast<uint8_t>(gfx_int);
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status OverworldEditorManager::DrawUnifiedSettingsTable() {
|
||||
// Create a comprehensive settings table that combines toolset and properties
|
||||
if (BeginTable("##UnifiedOverworldSettings", 6,
|
||||
ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter |
|
||||
ImGuiTableFlags_BordersV | ImGuiTableFlags_SizingFixedFit)) {
|
||||
|
||||
// Setup columns with proper widths
|
||||
TableSetupColumn(ICON_MD_BUILD " Tools", ImGuiTableColumnFlags_WidthFixed, 120);
|
||||
TableSetupColumn(ICON_MD_MAP " World", ImGuiTableColumnFlags_WidthFixed, 100);
|
||||
TableSetupColumn(ICON_MD_IMAGE " Graphics", ImGuiTableColumnFlags_WidthFixed, 100);
|
||||
TableSetupColumn(ICON_MD_PALETTE " Palette", ImGuiTableColumnFlags_WidthFixed, 100);
|
||||
TableSetupColumn(ICON_MD_SETTINGS " Properties", ImGuiTableColumnFlags_WidthStretch);
|
||||
TableSetupColumn(ICON_MD_EXTENSION " v3 Features", ImGuiTableColumnFlags_WidthFixed, 120);
|
||||
TableHeadersRow();
|
||||
|
||||
TableNextRow();
|
||||
|
||||
// Tools column
|
||||
TableNextColumn();
|
||||
RETURN_IF_ERROR(DrawToolsetInSettings());
|
||||
|
||||
// World column
|
||||
TableNextColumn();
|
||||
Text(ICON_MD_PUBLIC " Current World");
|
||||
SetNextItemWidth(80.f);
|
||||
// if (Combo("##world", ¤t_world_, kWorldList.data(), 3)) {
|
||||
// // World change logic would go here
|
||||
// }
|
||||
|
||||
// Graphics column
|
||||
TableNextColumn();
|
||||
Text(ICON_MD_IMAGE " Area Graphics");
|
||||
// Graphics controls would go here
|
||||
|
||||
// Palette column
|
||||
TableNextColumn();
|
||||
Text(ICON_MD_PALETTE " Area Palette");
|
||||
// Palette controls would go here
|
||||
|
||||
// Properties column
|
||||
TableNextColumn();
|
||||
Text(ICON_MD_SETTINGS " Map Properties");
|
||||
// Map properties would go here
|
||||
|
||||
// v3 Features column
|
||||
TableNextColumn();
|
||||
uint8_t asm_version = GetCustomASMVersion();
|
||||
if (asm_version >= 3 && asm_version != 0xFF) {
|
||||
TextColored(ImVec4(0, 1, 0, 1), ICON_MD_NEW_RELEASES " v3 Active");
|
||||
if (Button(ICON_MD_TUNE " Settings")) {
|
||||
// Open v3 settings
|
||||
}
|
||||
} else {
|
||||
TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1), ICON_MD_UPGRADE " v3 Available");
|
||||
if (Button(ICON_MD_UPGRADE " Upgrade")) {
|
||||
// Trigger upgrade
|
||||
}
|
||||
}
|
||||
|
||||
EndTable();
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status OverworldEditorManager::DrawToolsetInSettings() {
|
||||
// Compact toolset layout within the settings table
|
||||
BeginGroup();
|
||||
|
||||
// Core editing tools in a compact grid
|
||||
if (Button(ICON_MD_PAN_TOOL_ALT, ImVec2(25, 25))) {
|
||||
// Set PAN mode
|
||||
}
|
||||
HOVER_HINT("Pan (1)");
|
||||
|
||||
SameLine();
|
||||
if (Button(ICON_MD_DRAW, ImVec2(25, 25))) {
|
||||
// Set DRAW_TILE mode
|
||||
}
|
||||
HOVER_HINT("Draw Tile (2)");
|
||||
|
||||
SameLine();
|
||||
if (Button(ICON_MD_DOOR_FRONT, ImVec2(25, 25))) {
|
||||
// Set ENTRANCES mode
|
||||
}
|
||||
HOVER_HINT("Entrances (3)");
|
||||
|
||||
SameLine();
|
||||
if (Button(ICON_MD_DOOR_BACK, ImVec2(25, 25))) {
|
||||
// Set EXITS mode
|
||||
}
|
||||
HOVER_HINT("Exits (4)");
|
||||
|
||||
// Second row
|
||||
if (Button(ICON_MD_GRASS, ImVec2(25, 25))) {
|
||||
// Set ITEMS mode
|
||||
}
|
||||
HOVER_HINT("Items (5)");
|
||||
|
||||
SameLine();
|
||||
if (Button(ICON_MD_PEST_CONTROL_RODENT, ImVec2(25, 25))) {
|
||||
// Set SPRITES mode
|
||||
}
|
||||
HOVER_HINT("Sprites (6)");
|
||||
|
||||
SameLine();
|
||||
if (Button(ICON_MD_ADD_LOCATION, ImVec2(25, 25))) {
|
||||
// Set TRANSPORTS mode
|
||||
}
|
||||
HOVER_HINT("Transports (7)");
|
||||
|
||||
SameLine();
|
||||
if (Button(ICON_MD_MUSIC_NOTE, ImVec2(25, 25))) {
|
||||
// Set MUSIC mode
|
||||
}
|
||||
HOVER_HINT("Music (8)");
|
||||
|
||||
EndGroup();
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status OverworldEditorManager::HandleCanvasSelectionTransfer() {
|
||||
// This could be called to manage bidirectional selection transfer
|
||||
// For now, it's a placeholder for future canvas interaction management
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status OverworldEditorManager::TransferOverworldSelectionToScratch() {
|
||||
// Transfer logic would go here to copy selections from overworld to scratch
|
||||
// This could be integrated with the editor's context system
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status OverworldEditorManager::TransferScratchSelectionToOverworld() {
|
||||
// Transfer logic would go here to copy selections from scratch to overworld
|
||||
// This could be integrated with the editor's context system
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace yaze
|
||||
@@ -1,108 +0,0 @@
|
||||
#ifndef YAZE_APP_EDITOR_OVERWORLD_OVERWORLD_EDITOR_MANAGER_H
|
||||
#define YAZE_APP_EDITOR_OVERWORLD_OVERWORLD_EDITOR_MANAGER_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/rom.h"
|
||||
#include "app/zelda3/overworld/overworld.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/input.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
|
||||
// Forward declarations
|
||||
enum class EditingMode;
|
||||
class OverworldEditor;
|
||||
|
||||
/**
|
||||
* @class OverworldEditorManager
|
||||
* @brief Manages the complex overworld editor functionality and v3 features
|
||||
*
|
||||
* This class separates the complex overworld editing functionality from the main
|
||||
* OverworldEditor class to improve maintainability and organization, especially
|
||||
* for ZSCustomOverworld v3 features.
|
||||
*/
|
||||
class OverworldEditorManager {
|
||||
public:
|
||||
OverworldEditorManager(zelda3::Overworld* overworld, Rom* rom,
|
||||
OverworldEditor* editor = nullptr)
|
||||
: overworld_(overworld), rom_(rom), editor_(editor) {}
|
||||
|
||||
// Set editor context for mode changes
|
||||
void SetEditorContext(OverworldEditor* editor) { editor_ = editor; }
|
||||
|
||||
// v3 Feature Management
|
||||
absl::Status DrawV3SettingsPanel();
|
||||
absl::Status DrawCustomOverworldSettings();
|
||||
absl::Status DrawAreaSpecificSettings();
|
||||
absl::Status DrawTransitionSettings();
|
||||
absl::Status DrawOverlaySettings();
|
||||
|
||||
// Map Properties Management
|
||||
absl::Status DrawMapPropertiesTable();
|
||||
absl::Status DrawUnifiedSettingsTable();
|
||||
absl::Status DrawToolsetInSettings();
|
||||
absl::Status DrawAreaSizeControls();
|
||||
absl::Status DrawMosaicControls();
|
||||
absl::Status DrawPaletteControls();
|
||||
absl::Status DrawGfxGroupControls();
|
||||
|
||||
// Save/Load Operations for v3
|
||||
absl::Status SaveCustomOverworldData();
|
||||
absl::Status LoadCustomOverworldData();
|
||||
absl::Status ApplyCustomOverworldASM();
|
||||
|
||||
// Canvas Interaction Management
|
||||
absl::Status HandleCanvasSelectionTransfer();
|
||||
absl::Status TransferOverworldSelectionToScratch();
|
||||
absl::Status TransferScratchSelectionToOverworld();
|
||||
|
||||
// Validation and Checks
|
||||
bool ValidateV3Compatibility();
|
||||
bool CheckCustomASMApplied();
|
||||
uint8_t GetCustomASMVersion();
|
||||
|
||||
// Getters/Setters for v3 settings
|
||||
auto enable_area_specific_bg() const { return enable_area_specific_bg_; }
|
||||
auto enable_main_palette() const { return enable_main_palette_; }
|
||||
auto enable_mosaic() const { return enable_mosaic_; }
|
||||
auto enable_gfx_groups() const { return enable_gfx_groups_; }
|
||||
auto enable_subscreen_overlay() const { return enable_subscreen_overlay_; }
|
||||
auto enable_animated_gfx() const { return enable_animated_gfx_; }
|
||||
|
||||
void set_enable_area_specific_bg(bool value) { enable_area_specific_bg_ = value; }
|
||||
void set_enable_main_palette(bool value) { enable_main_palette_ = value; }
|
||||
void set_enable_mosaic(bool value) { enable_mosaic_ = value; }
|
||||
void set_enable_gfx_groups(bool value) { enable_gfx_groups_ = value; }
|
||||
void set_enable_subscreen_overlay(bool value) { enable_subscreen_overlay_ = value; }
|
||||
void set_enable_animated_gfx(bool value) { enable_animated_gfx_ = value; }
|
||||
|
||||
private:
|
||||
zelda3::Overworld* overworld_;
|
||||
Rom* rom_;
|
||||
OverworldEditor* editor_;
|
||||
|
||||
// v3 Feature flags
|
||||
bool enable_area_specific_bg_ = false;
|
||||
bool enable_main_palette_ = false;
|
||||
bool enable_mosaic_ = false;
|
||||
bool enable_gfx_groups_ = false;
|
||||
bool enable_subscreen_overlay_ = false;
|
||||
bool enable_animated_gfx_ = false;
|
||||
|
||||
// Current editing state
|
||||
int current_map_index_ = 0;
|
||||
|
||||
// Helper methods
|
||||
absl::Status DrawBooleanSetting(const char* label, bool* setting, const char* help_text = nullptr);
|
||||
absl::Status DrawColorPicker(const char* label, uint16_t* color);
|
||||
absl::Status DrawOverlaySetting(const char* label, uint16_t* overlay);
|
||||
absl::Status DrawGfxGroupSetting(const char* label, uint8_t* gfx_id, int max_value = 255);
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_OVERWORLD_OVERWORLD_EDITOR_MANAGER_H
|
||||
@@ -1,714 +0,0 @@
|
||||
#include "overworld_graphics_manager.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "app/core/features.h"
|
||||
#include "app/core/window.h"
|
||||
#include "app/gfx/performance_profiler.h"
|
||||
#include "util/log.h"
|
||||
#include "util/macro.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
|
||||
using core::Renderer;
|
||||
using zelda3::kNumOverworldMaps;
|
||||
using zelda3::kOverworldMapSize;
|
||||
|
||||
constexpr int kTile16Size = 16;
|
||||
|
||||
// ============================================================================
|
||||
// Loading Operations
|
||||
// ============================================================================
|
||||
|
||||
absl::Status OverworldGraphicsManager::LoadGraphics() {
|
||||
gfx::ScopedTimer timer("LoadGraphics");
|
||||
|
||||
LOG_INFO("OverworldGraphicsManager", "Loading overworld.");
|
||||
// Load the Link to the Past overworld.
|
||||
{
|
||||
gfx::ScopedTimer load_timer("Overworld::Load");
|
||||
RETURN_IF_ERROR(overworld_->Load(rom_));
|
||||
}
|
||||
*palette_ = overworld_->current_area_palette();
|
||||
|
||||
LOG_INFO("OverworldGraphicsManager", "Loading overworld graphics (optimized).");
|
||||
|
||||
// Phase 1: Create bitmaps without textures for faster loading
|
||||
// This avoids blocking the main thread with GPU texture creation
|
||||
{
|
||||
gfx::ScopedTimer gfx_timer("CreateBitmapWithoutTexture_Graphics");
|
||||
Renderer::Get().CreateBitmapWithoutTexture(0x80, kOverworldMapSize, 0x40,
|
||||
overworld_->current_graphics(),
|
||||
*current_gfx_bmp_, *palette_);
|
||||
}
|
||||
|
||||
LOG_INFO("OverworldGraphicsManager", "Loading overworld tileset (deferred textures).");
|
||||
{
|
||||
gfx::ScopedTimer tileset_timer("CreateBitmapWithoutTexture_Tileset");
|
||||
Renderer::Get().CreateBitmapWithoutTexture(
|
||||
0x80, 0x2000, 0x08, overworld_->tile16_blockset_data(),
|
||||
*tile16_blockset_bmp_, *palette_);
|
||||
}
|
||||
map_blockset_loaded_ = true;
|
||||
|
||||
// Copy the tile16 data into individual tiles.
|
||||
auto tile16_blockset_data = overworld_->tile16_blockset_data();
|
||||
LOG_INFO("OverworldGraphicsManager", "Loading overworld tile16 graphics.");
|
||||
|
||||
{
|
||||
gfx::ScopedTimer tilemap_timer("CreateTilemap");
|
||||
*tile16_blockset_ =
|
||||
gfx::CreateTilemap(tile16_blockset_data, 0x80, 0x2000, kTile16Size,
|
||||
zelda3::kNumTile16Individual, *palette_);
|
||||
}
|
||||
|
||||
// Phase 2: Create bitmaps only for essential maps initially
|
||||
// Non-essential maps will be created on-demand when accessed
|
||||
constexpr int kEssentialMapsPerWorld = 8;
|
||||
constexpr int kLightWorldEssential = kEssentialMapsPerWorld;
|
||||
constexpr int kDarkWorldEssential =
|
||||
zelda3::kDarkWorldMapIdStart + kEssentialMapsPerWorld;
|
||||
constexpr int kSpecialWorldEssential =
|
||||
zelda3::kSpecialWorldMapIdStart + kEssentialMapsPerWorld;
|
||||
|
||||
LOG_INFO("OverworldGraphicsManager",
|
||||
"Creating bitmaps for essential maps only (first %d maps per world)",
|
||||
kEssentialMapsPerWorld);
|
||||
|
||||
std::vector<gfx::Bitmap*> maps_to_texture;
|
||||
maps_to_texture.reserve(kEssentialMapsPerWorld *
|
||||
3); // 8 maps per world * 3 worlds
|
||||
|
||||
{
|
||||
gfx::ScopedTimer maps_timer("CreateEssentialOverworldMaps");
|
||||
for (int i = 0; i < zelda3::kNumOverworldMaps; ++i) {
|
||||
bool is_essential = false;
|
||||
|
||||
// Check if this is an essential map
|
||||
if (i < kLightWorldEssential) {
|
||||
is_essential = true;
|
||||
} else if (i >= zelda3::kDarkWorldMapIdStart && i < kDarkWorldEssential) {
|
||||
is_essential = true;
|
||||
} else if (i >= zelda3::kSpecialWorldMapIdStart &&
|
||||
i < kSpecialWorldEssential) {
|
||||
is_essential = true;
|
||||
}
|
||||
|
||||
if (is_essential) {
|
||||
overworld_->set_current_map(i);
|
||||
auto palette = overworld_->current_area_palette();
|
||||
try {
|
||||
// Create bitmap data and surface but defer texture creation
|
||||
(*maps_bmp_)[i].Create(kOverworldMapSize, kOverworldMapSize, 0x80,
|
||||
overworld_->current_map_bitmap_data());
|
||||
(*maps_bmp_)[i].SetPalette(palette);
|
||||
maps_to_texture.push_back(&(*maps_bmp_)[i]);
|
||||
} catch (const std::bad_alloc& e) {
|
||||
LOG_ERROR("OverworldGraphicsManager", "Error allocating map %d: %s",
|
||||
i, e.what());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Non-essential maps will be created on-demand when accessed
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 3: Create textures only for currently visible maps
|
||||
// Only create textures for the first few maps initially
|
||||
const int initial_texture_count =
|
||||
std::min(4, static_cast<int>(maps_to_texture.size()));
|
||||
{
|
||||
gfx::ScopedTimer initial_textures_timer("CreateInitialTextures");
|
||||
for (int i = 0; i < initial_texture_count; ++i) {
|
||||
Renderer::Get().RenderBitmap(maps_to_texture[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Store remaining maps for lazy texture creation
|
||||
deferred_map_textures_.assign(maps_to_texture.begin() + initial_texture_count,
|
||||
maps_to_texture.end());
|
||||
|
||||
if (core::FeatureFlags::get().overworld.kDrawOverworldSprites) {
|
||||
{
|
||||
gfx::ScopedTimer sprites_timer("LoadSpriteGraphics");
|
||||
RETURN_IF_ERROR(LoadSpriteGraphics());
|
||||
}
|
||||
}
|
||||
|
||||
all_gfx_loaded_ = true;
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status OverworldGraphicsManager::LoadSpriteGraphics() {
|
||||
// Render the sprites for each Overworld map
|
||||
const int depth = 0x10;
|
||||
for (int i = 0; i < 3; i++)
|
||||
for (auto const& sprite : *overworld_->mutable_sprites(i)) {
|
||||
int width = sprite.width();
|
||||
int height = sprite.height();
|
||||
if (width == 0 || height == 0) {
|
||||
continue;
|
||||
}
|
||||
if (sprite_previews_->size() < sprite.id()) {
|
||||
sprite_previews_->resize(sprite.id() + 1);
|
||||
}
|
||||
(*sprite_previews_)[sprite.id()].Create(width, height, depth,
|
||||
*sprite.preview_graphics());
|
||||
(*sprite_previews_)[sprite.id()].SetPalette(*palette_);
|
||||
Renderer::Get().RenderBitmap(&(*sprite_previews_)[sprite.id()]);
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Texture Management
|
||||
// ============================================================================
|
||||
|
||||
void OverworldGraphicsManager::ProcessDeferredTextures() {
|
||||
std::lock_guard<std::mutex> lock(deferred_textures_mutex_);
|
||||
|
||||
// Always process deferred textures progressively, even if the list is "empty"
|
||||
// This allows for continuous background loading
|
||||
|
||||
// PHASE 1: Priority loading for current world
|
||||
const int high_priority_per_frame = 4; // Current world maps
|
||||
const int low_priority_per_frame = 2; // Other world maps
|
||||
const int refresh_per_frame = 2; // Modified map refreshes
|
||||
int processed = 0;
|
||||
|
||||
// Process high-priority deferred textures (current world)
|
||||
if (!deferred_map_textures_.empty()) {
|
||||
auto it = deferred_map_textures_.begin();
|
||||
while (it != deferred_map_textures_.end() && processed < high_priority_per_frame) {
|
||||
if (*it && !(*it)->texture()) {
|
||||
// Find map index for priority check
|
||||
int map_index = -1;
|
||||
for (int i = 0; i < zelda3::kNumOverworldMaps; ++i) {
|
||||
if (&(*maps_bmp_)[i] == *it) {
|
||||
map_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_current_world = false;
|
||||
if (map_index >= 0) {
|
||||
int map_world = map_index / 0x40; // 64 maps per world
|
||||
is_current_world = (map_world == current_world_);
|
||||
}
|
||||
|
||||
if (is_current_world) {
|
||||
Renderer::Get().RenderBitmap(*it);
|
||||
processed++;
|
||||
it = deferred_map_textures_.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PHASE 2: Background loading for other worlds (lower priority)
|
||||
if (!deferred_map_textures_.empty() && processed < high_priority_per_frame) {
|
||||
auto it = deferred_map_textures_.begin();
|
||||
int low_priority_processed = 0;
|
||||
while (it != deferred_map_textures_.end() && low_priority_processed < low_priority_per_frame) {
|
||||
if (*it && !(*it)->texture()) {
|
||||
Renderer::Get().RenderBitmap(*it);
|
||||
low_priority_processed++;
|
||||
processed++;
|
||||
it = deferred_map_textures_.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PHASE 3: Process modified maps that need refresh (highest priority)
|
||||
int refresh_processed = 0;
|
||||
for (int i = 0; i < zelda3::kNumOverworldMaps && refresh_processed < refresh_per_frame; ++i) {
|
||||
if ((*maps_bmp_)[i].modified() && (*maps_bmp_)[i].is_active()) {
|
||||
// Check if this map is in current world (high priority) or visible
|
||||
bool is_current_world = (i / 0x40 == current_world_);
|
||||
bool is_current_map = (i == current_map_);
|
||||
|
||||
if (is_current_map || is_current_world) {
|
||||
RefreshOverworldMapOnDemand(i);
|
||||
refresh_processed++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PHASE 4: Background refresh for modified maps in other worlds (very low priority)
|
||||
if (refresh_processed == 0) {
|
||||
for (int i = 0; i < zelda3::kNumOverworldMaps; ++i) {
|
||||
if ((*maps_bmp_)[i].modified() && (*maps_bmp_)[i].is_active()) {
|
||||
bool is_current_world = (i / 0x40 == current_world_);
|
||||
if (!is_current_world) {
|
||||
// Just mark for later, don't refresh now to avoid lag
|
||||
// These will be refreshed when the world is switched
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OverworldGraphicsManager::EnsureMapTexture(int map_index) {
|
||||
if (map_index < 0 || map_index >= zelda3::kNumOverworldMaps) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure the map is built first (on-demand loading)
|
||||
auto status = overworld_->EnsureMapBuilt(map_index);
|
||||
if (!status.ok()) {
|
||||
LOG_ERROR("OverworldGraphicsManager", "Failed to build map %d: %s", map_index,
|
||||
status.message().data());
|
||||
return;
|
||||
}
|
||||
|
||||
auto& bitmap = (*maps_bmp_)[map_index];
|
||||
|
||||
// If bitmap doesn't exist yet (non-essential map), create it now
|
||||
if (!bitmap.is_active()) {
|
||||
overworld_->set_current_map(map_index);
|
||||
auto palette = overworld_->current_area_palette();
|
||||
try {
|
||||
bitmap.Create(kOverworldMapSize, kOverworldMapSize, 0x80,
|
||||
overworld_->current_map_bitmap_data());
|
||||
bitmap.SetPalette(palette);
|
||||
} catch (const std::bad_alloc& e) {
|
||||
LOG_ERROR("OverworldGraphicsManager", "Error allocating bitmap for map %d: %s",
|
||||
map_index, e.what());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bitmap.texture() && bitmap.is_active()) {
|
||||
Renderer::Get().RenderBitmap(&bitmap);
|
||||
|
||||
// Remove from deferred list if it was there
|
||||
std::lock_guard<std::mutex> lock(deferred_textures_mutex_);
|
||||
auto it = std::find(deferred_map_textures_.begin(),
|
||||
deferred_map_textures_.end(), &bitmap);
|
||||
if (it != deferred_map_textures_.end()) {
|
||||
deferred_map_textures_.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Refresh Operations
|
||||
// ============================================================================
|
||||
|
||||
void OverworldGraphicsManager::RefreshOverworldMap() {
|
||||
// Use the new on-demand refresh system
|
||||
RefreshOverworldMapOnDemand(current_map_);
|
||||
}
|
||||
|
||||
void OverworldGraphicsManager::RefreshOverworldMapOnDemand(int map_index) {
|
||||
if (map_index < 0 || map_index >= zelda3::kNumOverworldMaps) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the map is actually visible or being edited
|
||||
bool is_current_map = (map_index == current_map_);
|
||||
bool is_current_world = (map_index / 0x40 == current_world_);
|
||||
|
||||
// For non-current maps in non-current worlds, defer the refresh
|
||||
if (!is_current_map && !is_current_world) {
|
||||
// Mark for deferred refresh - will be processed when the map becomes visible
|
||||
(*maps_bmp_)[map_index].set_modified(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// For visible maps, do immediate refresh
|
||||
RefreshChildMapOnDemand(map_index);
|
||||
}
|
||||
|
||||
void OverworldGraphicsManager::RefreshChildMap(int map_index) {
|
||||
overworld_->mutable_overworld_map(map_index)->LoadAreaGraphics();
|
||||
auto status = overworld_->mutable_overworld_map(map_index)->BuildTileset();
|
||||
PRINT_IF_ERROR(status);
|
||||
status = overworld_->mutable_overworld_map(map_index)->BuildTiles16Gfx(
|
||||
*overworld_->mutable_tiles16(), overworld_->tiles16().size());
|
||||
PRINT_IF_ERROR(status);
|
||||
status = overworld_->mutable_overworld_map(map_index)->BuildBitmap(
|
||||
overworld_->GetMapTiles(current_world_));
|
||||
(*maps_bmp_)[map_index].set_data(
|
||||
overworld_->mutable_overworld_map(map_index)->bitmap_data());
|
||||
(*maps_bmp_)[map_index].set_modified(true);
|
||||
PRINT_IF_ERROR(status);
|
||||
}
|
||||
|
||||
void OverworldGraphicsManager::RefreshChildMapOnDemand(int map_index) {
|
||||
auto* map = overworld_->mutable_overworld_map(map_index);
|
||||
|
||||
// Check what actually needs to be refreshed
|
||||
bool needs_graphics_rebuild = (*maps_bmp_)[map_index].modified();
|
||||
|
||||
if (needs_graphics_rebuild) {
|
||||
// Only rebuild what's actually changed
|
||||
map->LoadAreaGraphics();
|
||||
|
||||
// Rebuild tileset only if graphics changed
|
||||
auto status = map->BuildTileset();
|
||||
if (!status.ok()) {
|
||||
LOG_ERROR("OverworldGraphicsManager", "Failed to build tileset for map %d: %s",
|
||||
map_index, status.message().data());
|
||||
return;
|
||||
}
|
||||
|
||||
// Rebuild tiles16 graphics
|
||||
status = map->BuildTiles16Gfx(*overworld_->mutable_tiles16(),
|
||||
overworld_->tiles16().size());
|
||||
if (!status.ok()) {
|
||||
LOG_ERROR("OverworldGraphicsManager", "Failed to build tiles16 graphics for map %d: %s",
|
||||
map_index, status.message().data());
|
||||
return;
|
||||
}
|
||||
|
||||
// Rebuild bitmap
|
||||
status = map->BuildBitmap(overworld_->GetMapTiles(current_world_));
|
||||
if (!status.ok()) {
|
||||
LOG_ERROR("OverworldGraphicsManager", "Failed to build bitmap for map %d: %s",
|
||||
map_index, status.message().data());
|
||||
return;
|
||||
}
|
||||
|
||||
// Update bitmap data
|
||||
(*maps_bmp_)[map_index].set_data(map->bitmap_data());
|
||||
(*maps_bmp_)[map_index].set_modified(false);
|
||||
|
||||
// Validate surface synchronization to help debug crashes
|
||||
if (!(*maps_bmp_)[map_index].ValidateDataSurfaceSync()) {
|
||||
LOG_WARN("OverworldGraphicsManager", "Warning: Surface synchronization issue detected for map %d",
|
||||
map_index);
|
||||
}
|
||||
|
||||
// Update texture on main thread
|
||||
if ((*maps_bmp_)[map_index].texture()) {
|
||||
Renderer::Get().UpdateBitmap(&(*maps_bmp_)[map_index]);
|
||||
} else {
|
||||
// Create texture if it doesn't exist
|
||||
EnsureMapTexture(map_index);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle multi-area maps (large, wide, tall) with safe coordination
|
||||
// Check if ZSCustomOverworld v3 is present
|
||||
uint8_t asm_version = (*rom_)[zelda3::OverworldCustomASMHasBeenApplied];
|
||||
bool use_v3_area_sizes = (asm_version >= 3 && asm_version != 0xFF);
|
||||
|
||||
if (use_v3_area_sizes) {
|
||||
// Use v3 multi-area coordination
|
||||
RefreshMultiAreaMapsSafely(map_index, map);
|
||||
} else {
|
||||
// Legacy logic: only handle large maps for vanilla/v2
|
||||
if (map->is_large_map()) {
|
||||
RefreshMultiAreaMapsSafely(map_index, map);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OverworldGraphicsManager::RefreshMultiAreaMapsSafely(
|
||||
int map_index, zelda3::OverworldMap* map) {
|
||||
using zelda3::AreaSizeEnum;
|
||||
|
||||
// Skip if this is already a processed sibling to avoid double-processing
|
||||
static std::set<int> currently_processing;
|
||||
if (currently_processing.count(map_index)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto area_size = map->area_size();
|
||||
if (area_size == AreaSizeEnum::SmallArea) {
|
||||
return; // No siblings to coordinate
|
||||
}
|
||||
|
||||
LOG_DEBUG("OverworldGraphicsManager",
|
||||
"RefreshMultiAreaMapsSafely: Processing %s area map %d (parent: %d)",
|
||||
(area_size == AreaSizeEnum::LargeArea) ? "large"
|
||||
: (area_size == AreaSizeEnum::WideArea) ? "wide"
|
||||
: "tall",
|
||||
map_index, map->parent());
|
||||
|
||||
// Determine all maps that are part of this multi-area structure
|
||||
std::vector<int> sibling_maps;
|
||||
int parent_id = map->parent();
|
||||
|
||||
// Use the same logic as ZScream for area coordination
|
||||
switch (area_size) {
|
||||
case AreaSizeEnum::LargeArea: {
|
||||
// Large Area: 2x2 grid (4 maps total)
|
||||
sibling_maps = {parent_id, parent_id + 1, parent_id + 8, parent_id + 9};
|
||||
LOG_DEBUG("OverworldGraphicsManager",
|
||||
"RefreshMultiAreaMapsSafely: Large area siblings: %d, %d, %d, %d",
|
||||
parent_id, parent_id + 1, parent_id + 8, parent_id + 9);
|
||||
break;
|
||||
}
|
||||
|
||||
case AreaSizeEnum::WideArea: {
|
||||
// Wide Area: 2x1 grid (2 maps total, horizontally adjacent)
|
||||
sibling_maps = {parent_id, parent_id + 1};
|
||||
LOG_DEBUG("OverworldGraphicsManager",
|
||||
"RefreshMultiAreaMapsSafely: Wide area siblings: %d, %d",
|
||||
parent_id, parent_id + 1);
|
||||
break;
|
||||
}
|
||||
|
||||
case AreaSizeEnum::TallArea: {
|
||||
// Tall Area: 1x2 grid (2 maps total, vertically adjacent)
|
||||
sibling_maps = {parent_id, parent_id + 8};
|
||||
LOG_DEBUG("OverworldGraphicsManager",
|
||||
"RefreshMultiAreaMapsSafely: Tall area siblings: %d, %d",
|
||||
parent_id, parent_id + 8);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
LOG_WARN("OverworldGraphicsManager",
|
||||
"RefreshMultiAreaMapsSafely: Unknown area size %d for map %d",
|
||||
static_cast<int>(area_size), map_index);
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark all siblings as being processed to prevent recursion
|
||||
for (int sibling : sibling_maps) {
|
||||
currently_processing.insert(sibling);
|
||||
}
|
||||
|
||||
// Only refresh siblings that are visible/current and need updating
|
||||
for (int sibling : sibling_maps) {
|
||||
if (sibling == map_index) {
|
||||
continue; // Skip self (already processed above)
|
||||
}
|
||||
|
||||
// Bounds check
|
||||
if (sibling < 0 || sibling >= zelda3::kNumOverworldMaps) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only refresh if it's visible or current
|
||||
bool is_current_map = (sibling == current_map_);
|
||||
bool is_current_world = (sibling / 0x40 == current_world_);
|
||||
bool needs_refresh = (*maps_bmp_)[sibling].modified();
|
||||
|
||||
if ((is_current_map || is_current_world) && needs_refresh) {
|
||||
LOG_DEBUG("OverworldGraphicsManager",
|
||||
"RefreshMultiAreaMapsSafely: Refreshing %s area sibling map %d "
|
||||
"(parent: %d)",
|
||||
(area_size == AreaSizeEnum::LargeArea) ? "large"
|
||||
: (area_size == AreaSizeEnum::WideArea) ? "wide"
|
||||
: "tall",
|
||||
sibling, parent_id);
|
||||
|
||||
// Direct refresh without calling RefreshChildMapOnDemand to avoid recursion
|
||||
auto* sibling_map = overworld_->mutable_overworld_map(sibling);
|
||||
if (sibling_map && (*maps_bmp_)[sibling].modified()) {
|
||||
sibling_map->LoadAreaGraphics();
|
||||
|
||||
if (auto status = sibling_map->BuildTileset(); !status.ok()) {
|
||||
LOG_ERROR("OverworldGraphicsManager",
|
||||
"RefreshMultiAreaMapsSafely: Failed to refresh sibling map %d: %s",
|
||||
sibling, status.message().data());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto status = sibling_map->BuildTiles16Gfx(*overworld_->mutable_tiles16(),
|
||||
overworld_->tiles16().size()); !status.ok()) {
|
||||
LOG_ERROR("OverworldGraphicsManager",
|
||||
"RefreshMultiAreaMapsSafely: Failed to build tiles16 graphics for sibling map %d: %s",
|
||||
sibling, status.message().data());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto status = sibling_map->LoadPalette(); !status.ok()) {
|
||||
LOG_ERROR("OverworldGraphicsManager",
|
||||
"RefreshMultiAreaMapsSafely: Failed to load palette for sibling map %d: %s",
|
||||
sibling, status.message().data());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto status = sibling_map->BuildBitmap(overworld_->GetMapTiles(current_world_)); status.ok()) {
|
||||
(*maps_bmp_)[sibling].set_data(sibling_map->bitmap_data());
|
||||
(*maps_bmp_)[sibling].SetPalette(overworld_->current_area_palette());
|
||||
(*maps_bmp_)[sibling].set_modified(false);
|
||||
|
||||
// Update texture if it exists
|
||||
if ((*maps_bmp_)[sibling].texture()) {
|
||||
Renderer::Get().UpdateBitmap(&(*maps_bmp_)[sibling]);
|
||||
} else {
|
||||
EnsureMapTexture(sibling);
|
||||
}
|
||||
} else {
|
||||
LOG_ERROR("OverworldGraphicsManager",
|
||||
"RefreshMultiAreaMapsSafely: Failed to build bitmap for sibling map %d: %s",
|
||||
sibling, status.message().data());
|
||||
}
|
||||
}
|
||||
} else if (!is_current_map && !is_current_world) {
|
||||
// Mark non-visible siblings for deferred refresh
|
||||
(*maps_bmp_)[sibling].set_modified(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear processing set after completion
|
||||
for (int sibling : sibling_maps) {
|
||||
currently_processing.erase(sibling);
|
||||
}
|
||||
}
|
||||
|
||||
absl::Status OverworldGraphicsManager::RefreshMapPalette() {
|
||||
RETURN_IF_ERROR(
|
||||
overworld_->mutable_overworld_map(current_map_)->LoadPalette());
|
||||
const auto current_map_palette = overworld_->current_area_palette();
|
||||
|
||||
// Check if ZSCustomOverworld v3 is present
|
||||
uint8_t asm_version = (*rom_)[zelda3::OverworldCustomASMHasBeenApplied];
|
||||
bool use_v3_area_sizes = (asm_version >= 3 && asm_version != 0xFF);
|
||||
|
||||
if (use_v3_area_sizes) {
|
||||
// Use v3 area size system
|
||||
using zelda3::AreaSizeEnum;
|
||||
auto area_size = overworld_->overworld_map(current_map_)->area_size();
|
||||
|
||||
if (area_size != AreaSizeEnum::SmallArea) {
|
||||
// Get all sibling maps that need palette updates
|
||||
std::vector<int> sibling_maps;
|
||||
int parent_id = overworld_->overworld_map(current_map_)->parent();
|
||||
|
||||
switch (area_size) {
|
||||
case AreaSizeEnum::LargeArea:
|
||||
// 2x2 grid: parent, parent+1, parent+8, parent+9
|
||||
sibling_maps = {parent_id, parent_id + 1, parent_id + 8,
|
||||
parent_id + 9};
|
||||
break;
|
||||
case AreaSizeEnum::WideArea:
|
||||
// 2x1 grid: parent, parent+1
|
||||
sibling_maps = {parent_id, parent_id + 1};
|
||||
break;
|
||||
case AreaSizeEnum::TallArea:
|
||||
// 1x2 grid: parent, parent+8
|
||||
sibling_maps = {parent_id, parent_id + 8};
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Update palette for all siblings
|
||||
for (int sibling_index : sibling_maps) {
|
||||
if (sibling_index < 0 || sibling_index >= zelda3::kNumOverworldMaps) {
|
||||
continue;
|
||||
}
|
||||
RETURN_IF_ERROR(
|
||||
overworld_->mutable_overworld_map(sibling_index)->LoadPalette());
|
||||
(*maps_bmp_)[sibling_index].SetPalette(current_map_palette);
|
||||
}
|
||||
} else {
|
||||
// Small area - only update current map
|
||||
(*maps_bmp_)[current_map_].SetPalette(current_map_palette);
|
||||
}
|
||||
} else {
|
||||
// Legacy logic for vanilla and v2 ROMs
|
||||
if (overworld_->overworld_map(current_map_)->is_large_map()) {
|
||||
// We need to update the map and its siblings if it's a large map
|
||||
for (int i = 1; i < 4; i++) {
|
||||
int sibling_index =
|
||||
overworld_->overworld_map(current_map_)->parent() + i;
|
||||
if (i >= 2)
|
||||
sibling_index += 6;
|
||||
RETURN_IF_ERROR(
|
||||
overworld_->mutable_overworld_map(sibling_index)->LoadPalette());
|
||||
(*maps_bmp_)[sibling_index].SetPalette(current_map_palette);
|
||||
}
|
||||
}
|
||||
(*maps_bmp_)[current_map_].SetPalette(current_map_palette);
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status OverworldGraphicsManager::RefreshTile16Blockset() {
|
||||
LOG_DEBUG("OverworldGraphicsManager", "RefreshTile16Blockset called");
|
||||
if (current_blockset_ ==
|
||||
overworld_->overworld_map(current_map_)->area_graphics()) {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
current_blockset_ = overworld_->overworld_map(current_map_)->area_graphics();
|
||||
|
||||
overworld_->set_current_map(current_map_);
|
||||
*palette_ = overworld_->current_area_palette();
|
||||
|
||||
const auto tile16_data = overworld_->tile16_blockset_data();
|
||||
|
||||
gfx::UpdateTilemap(*tile16_blockset_, tile16_data);
|
||||
tile16_blockset_->atlas.SetPalette(*palette_);
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void OverworldGraphicsManager::ForceRefreshGraphics(int map_index) {
|
||||
if (map_index < 0 || map_index >= zelda3::kNumOverworldMaps) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO("OverworldGraphicsManager",
|
||||
"ForceRefreshGraphics: Forcing graphics reload for map %d", map_index);
|
||||
|
||||
// Mark bitmap as modified to force refresh
|
||||
(*maps_bmp_)[map_index].set_modified(true);
|
||||
|
||||
// Clear the blockset cache to force tile16 reload
|
||||
current_blockset_ = 0xFF;
|
||||
|
||||
// If this is the current map, also ensure sibling maps are refreshed for multi-area maps
|
||||
uint8_t asm_version = (*rom_)[zelda3::OverworldCustomASMHasBeenApplied];
|
||||
bool use_v3_area_sizes = (asm_version >= 3 && asm_version != 0xFF);
|
||||
|
||||
auto* map = overworld_->mutable_overworld_map(map_index);
|
||||
if (use_v3_area_sizes) {
|
||||
using zelda3::AreaSizeEnum;
|
||||
auto area_size = map->area_size();
|
||||
|
||||
if (area_size != AreaSizeEnum::SmallArea) {
|
||||
std::vector<int> sibling_maps;
|
||||
int parent_id = map->parent();
|
||||
|
||||
switch (area_size) {
|
||||
case AreaSizeEnum::LargeArea:
|
||||
sibling_maps = {parent_id, parent_id + 1, parent_id + 8, parent_id + 9};
|
||||
break;
|
||||
case AreaSizeEnum::WideArea:
|
||||
sibling_maps = {parent_id, parent_id + 1};
|
||||
break;
|
||||
case AreaSizeEnum::TallArea:
|
||||
sibling_maps = {parent_id, parent_id + 8};
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Mark all sibling maps as needing refresh
|
||||
for (int sibling : sibling_maps) {
|
||||
if (sibling >= 0 && sibling < zelda3::kNumOverworldMaps) {
|
||||
(*maps_bmp_)[sibling].set_modified(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (map->is_large_map()) {
|
||||
// Legacy large map handling
|
||||
int parent_id = map->parent();
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
int sibling = parent_id + (i < 2 ? i : i + 6);
|
||||
if (sibling >= 0 && sibling < zelda3::kNumOverworldMaps) {
|
||||
(*maps_bmp_)[sibling].set_modified(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace yaze
|
||||
|
||||
@@ -1,204 +0,0 @@
|
||||
#ifndef YAZE_APP_EDITOR_OVERWORLD_OVERWORLD_GRAPHICS_MANAGER_H
|
||||
#define YAZE_APP_EDITOR_OVERWORLD_OVERWORLD_GRAPHICS_MANAGER_H
|
||||
|
||||
#include <array>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/tilemap.h"
|
||||
#include "app/rom.h"
|
||||
#include "app/zelda3/overworld/overworld.h"
|
||||
#include "app/zelda3/overworld/overworld_map.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
|
||||
using Tilemap = gfx::Tilemap;
|
||||
|
||||
/**
|
||||
* @class OverworldGraphicsManager
|
||||
* @brief Manages all graphics-related operations for the Overworld Editor
|
||||
*
|
||||
* This class handles:
|
||||
* - Graphics loading and initialization
|
||||
* - Sprite graphics loading
|
||||
* - Deferred texture processing for smooth loading
|
||||
* - Map texture management
|
||||
* - Map refreshing (full and on-demand)
|
||||
* - Palette refreshing
|
||||
* - Tile16 blockset refreshing
|
||||
* - Multi-area map coordination
|
||||
*
|
||||
* Separating graphics management from the main OverworldEditor improves:
|
||||
* - Code organization and maintainability
|
||||
* - Performance optimization opportunities
|
||||
* - Testing and debugging
|
||||
* - Clear separation of concerns
|
||||
*/
|
||||
class OverworldGraphicsManager {
|
||||
public:
|
||||
OverworldGraphicsManager(
|
||||
zelda3::Overworld* overworld, Rom* rom,
|
||||
std::array<gfx::Bitmap, zelda3::kNumOverworldMaps>* maps_bmp,
|
||||
gfx::Bitmap* tile16_blockset_bmp, gfx::Bitmap* current_gfx_bmp,
|
||||
std::vector<gfx::Bitmap>* sprite_previews, gfx::SnesPalette* palette,
|
||||
Tilemap* tile16_blockset)
|
||||
: overworld_(overworld),
|
||||
rom_(rom),
|
||||
maps_bmp_(maps_bmp),
|
||||
tile16_blockset_bmp_(tile16_blockset_bmp),
|
||||
current_gfx_bmp_(current_gfx_bmp),
|
||||
sprite_previews_(sprite_previews),
|
||||
palette_(palette),
|
||||
tile16_blockset_(tile16_blockset) {}
|
||||
|
||||
// ============================================================================
|
||||
// Loading Operations
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Load all overworld graphics (maps, tilesets, sprites)
|
||||
*
|
||||
* This uses a multi-phase loading strategy:
|
||||
* - Phase 1: Create bitmaps without textures
|
||||
* - Phase 2: Create bitmaps for essential maps only
|
||||
* - Phase 3: Create textures for visible maps
|
||||
* - Deferred loading for remaining maps
|
||||
*/
|
||||
absl::Status LoadGraphics();
|
||||
|
||||
/**
|
||||
* @brief Load sprite graphics for all overworld maps
|
||||
*/
|
||||
absl::Status LoadSpriteGraphics();
|
||||
|
||||
// ============================================================================
|
||||
// Texture Management
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Process deferred texture creation (called per frame)
|
||||
*
|
||||
* Creates textures gradually to avoid frame drops.
|
||||
* Prioritizes textures for the current world and visible maps.
|
||||
*/
|
||||
void ProcessDeferredTextures();
|
||||
|
||||
/**
|
||||
* @brief Ensure a specific map has a texture created
|
||||
*
|
||||
* @param map_index Index of the map to ensure texture for
|
||||
*/
|
||||
void EnsureMapTexture(int map_index);
|
||||
|
||||
// ============================================================================
|
||||
// Refresh Operations
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Refresh the current overworld map
|
||||
*/
|
||||
void RefreshOverworldMap();
|
||||
|
||||
/**
|
||||
* @brief Refresh a specific map on-demand (only if visible)
|
||||
*
|
||||
* @param map_index Index of the map to refresh
|
||||
*/
|
||||
void RefreshOverworldMapOnDemand(int map_index);
|
||||
|
||||
/**
|
||||
* @brief Refresh a child map (legacy method)
|
||||
*
|
||||
* @param map_index Index of the map to refresh
|
||||
*/
|
||||
void RefreshChildMap(int map_index);
|
||||
|
||||
/**
|
||||
* @brief Refresh a child map with selective updates
|
||||
*
|
||||
* @param map_index Index of the map to refresh
|
||||
*/
|
||||
void RefreshChildMapOnDemand(int map_index);
|
||||
|
||||
/**
|
||||
* @brief Safely refresh multi-area maps (large, wide, tall)
|
||||
*
|
||||
* Handles coordination of multi-area maps without recursion.
|
||||
*
|
||||
* @param map_index Index of the map to refresh
|
||||
* @param map Pointer to the OverworldMap object
|
||||
*/
|
||||
void RefreshMultiAreaMapsSafely(int map_index, zelda3::OverworldMap* map);
|
||||
|
||||
/**
|
||||
* @brief Refresh the palette for the current map
|
||||
*
|
||||
* Also handles palette updates for multi-area maps.
|
||||
*/
|
||||
absl::Status RefreshMapPalette();
|
||||
|
||||
/**
|
||||
* @brief Refresh the tile16 blockset
|
||||
*
|
||||
* This should be called whenever area graphics change.
|
||||
*/
|
||||
absl::Status RefreshTile16Blockset();
|
||||
|
||||
/**
|
||||
* @brief Force a graphics refresh for a specific map
|
||||
*
|
||||
* Marks the map's bitmap as modified and clears the blockset cache
|
||||
* to force a full reload on next refresh. Use this when graphics
|
||||
* properties change (area_graphics, animated_gfx, custom tilesets).
|
||||
*
|
||||
* @param map_index Index of the map to force refresh
|
||||
*/
|
||||
void ForceRefreshGraphics(int map_index);
|
||||
|
||||
// ============================================================================
|
||||
// State Management
|
||||
// ============================================================================
|
||||
|
||||
void set_current_map(int map_index) { current_map_ = map_index; }
|
||||
void set_current_world(int world_index) { current_world_ = world_index; }
|
||||
void set_current_blockset(uint8_t blockset) { current_blockset_ = blockset; }
|
||||
|
||||
int current_map() const { return current_map_; }
|
||||
int current_world() const { return current_world_; }
|
||||
uint8_t current_blockset() const { return current_blockset_; }
|
||||
|
||||
bool all_gfx_loaded() const { return all_gfx_loaded_; }
|
||||
bool map_blockset_loaded() const { return map_blockset_loaded_; }
|
||||
|
||||
private:
|
||||
// Core dependencies
|
||||
zelda3::Overworld* overworld_;
|
||||
Rom* rom_;
|
||||
std::array<gfx::Bitmap, zelda3::kNumOverworldMaps>* maps_bmp_;
|
||||
gfx::Bitmap* tile16_blockset_bmp_;
|
||||
gfx::Bitmap* current_gfx_bmp_;
|
||||
std::vector<gfx::Bitmap>* sprite_previews_;
|
||||
gfx::SnesPalette* palette_;
|
||||
Tilemap* tile16_blockset_;
|
||||
|
||||
// State tracking
|
||||
int current_map_ = 0;
|
||||
int current_world_ = 0;
|
||||
uint8_t current_blockset_ = 0;
|
||||
bool all_gfx_loaded_ = false;
|
||||
bool map_blockset_loaded_ = false;
|
||||
|
||||
// Deferred texture loading
|
||||
std::vector<gfx::Bitmap*> deferred_map_textures_;
|
||||
std::mutex deferred_textures_mutex_;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_OVERWORLD_OVERWORLD_GRAPHICS_MANAGER_H
|
||||
|
||||
Reference in New Issue
Block a user