From 9835eb2ab1a46ae404a156561b28b5c55a767ffb Mon Sep 17 00:00:00 2001 From: scawful Date: Sun, 5 Oct 2025 18:20:12 -0400 Subject: [PATCH] refactor: Introduce OverworldEntityRenderer for Enhanced Entity Visualization - Added OverworldEntityRenderer class to separate entity rendering logic from OverworldEditor, improving maintainability and testability. - Updated OverworldEditor to utilize the new entity renderer, removing legacy drawing functions for entrances, exits, items, and sprites. - Enhanced initialization and drawing methods to streamline entity visualization and interaction within the editor. - Updated CMake configuration to include the new renderer files, ensuring proper integration into the build system. --- src/app/editor/editor_library.cmake | 1 + src/app/editor/overworld/overworld_editor.cc | 260 ++---------------- src/app/editor/overworld/overworld_editor.h | 7 +- .../overworld/overworld_entity_renderer.cc | 260 ++++++++++++++++++ .../overworld/overworld_entity_renderer.h | 90 ++++++ 5 files changed, 377 insertions(+), 241 deletions(-) create mode 100644 src/app/editor/overworld/overworld_entity_renderer.cc create mode 100644 src/app/editor/overworld/overworld_entity_renderer.h diff --git a/src/app/editor/editor_library.cmake b/src/app/editor/editor_library.cmake index ea6dc064..e2593569 100644 --- a/src/app/editor/editor_library.cmake +++ b/src/app/editor/editor_library.cmake @@ -35,6 +35,7 @@ set( app/editor/overworld/map_properties.cc app/editor/graphics/gfx_group_editor.cc app/editor/overworld/entity.cc + app/editor/overworld/overworld_entity_renderer.cc app/editor/system/settings_editor.cc app/editor/system/command_manager.cc app/editor/system/extension_manager.cc diff --git a/src/app/editor/overworld/overworld_editor.cc b/src/app/editor/overworld/overworld_editor.cc index d6a4337b..c13f8c3c 100644 --- a/src/app/editor/overworld/overworld_editor.cc +++ b/src/app/editor/overworld/overworld_editor.cc @@ -16,7 +16,6 @@ #include "app/core/asar_wrapper.h" #include "app/core/features.h" #include "app/core/window.h" -#include "app/editor/overworld/entity.h" #include "app/editor/overworld/map_properties.h" #include "app/editor/overworld/tile16_editor.h" #include "app/gfx/arena.h" @@ -62,6 +61,10 @@ void OverworldEditor::Initialize() { overworld_manager_ = std::make_unique(&overworld_, rom_, this); + // Initialize OverworldEntityRenderer for entity visualization + entity_renderer_ = std::make_unique( + &overworld_, &ow_map_canvas_, &sprite_previews_); + // Setup overworld canvas context menu SetupOverworldCanvasContextMenu(); @@ -1100,10 +1103,24 @@ void OverworldEditor::DrawOverworldCanvas() { if (overworld_.is_loaded()) { DrawOverworldMaps(); - DrawOverworldExits(ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling()); - DrawOverworldEntrances(ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling()); - DrawOverworldItems(); - DrawOverworldSprites(); + + // Update entity renderer state + entity_renderer_->set_current_map(current_map_); + + // Draw all entities using the entity renderer + int mode_int = static_cast(current_mode); + entity_renderer_->DrawExits(ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling(), + current_world_, mode_int); + entity_renderer_->DrawEntrances(ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling(), + current_world_, mode_int); + entity_renderer_->DrawItems(current_world_, mode_int); + entity_renderer_->DrawSprites(current_world_, game_state_, mode_int); + + // Check if entity renderer wants to jump to a tab (e.g., double-clicked entrance/exit) + if (entity_renderer_->jump_to_tab() != -1) { + jump_to_tab_ = entity_renderer_->jump_to_tab(); + entity_renderer_->reset_jump_to_tab(); + } // Draw overlay preview if enabled if (show_overlay_preview_) { @@ -1243,237 +1260,8 @@ absl::Status OverworldEditor::DrawAreaGraphics() { } // DrawTileSelector() removed - replaced by individual card system in Update() - -void OverworldEditor::DrawOverworldEntrances(ImVec2 canvas_p0, ImVec2 scrolling) { - int i = 0; - for (auto& each : overworld_.entrances()) { - if (each.map_id_ < 0x40 + (current_world_ * 0x40) && - each.map_id_ >= (current_world_ * 0x40) && !each.deleted) { - // Use theme-aware color with proper transparency - ImVec4 entrance_color = gui::GetEntranceColor(); - if (each.is_hole_) { - // Holes are more opaque for visibility - entrance_color.w = 0.78f; // 200/255 alpha - } - ow_map_canvas_.DrawRect(each.x_, each.y_, 16, 16, entrance_color); - std::string str = util::HexByte(each.entrance_id_); - - if (current_mode == EditingMode::ENTRANCES) { - HandleEntityDragging(&each, canvas_p0, scrolling, is_dragging_entity_, - dragged_entity_, current_entity_); - - if (IsMouseHoveringOverEntity(each, canvas_p0, scrolling) && - ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) { - jump_to_tab_ = each.entrance_id_; - } - - if (IsMouseHoveringOverEntity(each, canvas_p0, scrolling) && - ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { - current_entrance_id_ = i; - current_entrance_ = each; - } - } - - ow_map_canvas_.DrawText(str, each.x_, each.y_); - } - i++; - } - - if (DrawEntranceInserterPopup()) { - // Get the deleted entrance ID and insert it at the mouse position - auto deleted_entrance_id = overworld_.deleted_entrances().back(); - overworld_.deleted_entrances().pop_back(); - auto& entrance = overworld_.entrances()[deleted_entrance_id]; - entrance.map_id_ = current_map_; - entrance.entrance_id_ = deleted_entrance_id; - entrance.x_ = ow_map_canvas_.hover_mouse_pos().x; - entrance.y_ = ow_map_canvas_.hover_mouse_pos().y; - entrance.deleted = false; - } - - if (current_mode == EditingMode::ENTRANCES) { - const auto is_hovering = - IsMouseHoveringOverEntity(current_entrance_, canvas_p0, scrolling); - - if (!is_hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { - ImGui::OpenPopup("Entrance Inserter"); - } else { - if (DrawOverworldEntrancePopup( - overworld_.entrances()[current_entrance_id_])) { - overworld_.entrances()[current_entrance_id_] = current_entrance_; - } - - if (overworld_.entrances()[current_entrance_id_].deleted) { - overworld_.mutable_deleted_entrances()->emplace_back( - current_entrance_id_); - } - } - } -} - -void OverworldEditor::DrawOverworldExits(ImVec2 canvas_p0, ImVec2 scrolling) { - int i = 0; - for (auto& each : *overworld_.mutable_exits()) { - if (each.map_id_ < 0x40 + (current_world_ * 0x40) && - each.map_id_ >= (current_world_ * 0x40) && !each.deleted_) { - ow_map_canvas_.DrawRect(each.x_, each.y_, 16, 16, - gui::GetExitColor()); - if (current_mode == EditingMode::EXITS) { - each.entity_id_ = i; - HandleEntityDragging(&each, ow_map_canvas_.zero_point(), - ow_map_canvas_.scrolling(), is_dragging_entity_, - dragged_entity_, current_entity_, true); - - if (IsMouseHoveringOverEntity(each, canvas_p0, scrolling) && - ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) { - jump_to_tab_ = each.room_id_; - } - - if (IsMouseHoveringOverEntity(each, canvas_p0, scrolling) && - ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { - current_exit_id_ = i; - current_exit_ = each; - current_entity_ = &each; - current_entity_->entity_id_ = i; - ImGui::OpenPopup("Exit editor"); - } - } - - std::string str = util::HexByte(i); - ow_map_canvas_.DrawText(str, each.x_, each.y_); - } - i++; - } - - DrawExitInserterPopup(); - if (current_mode == EditingMode::EXITS) { - const auto hovering = IsMouseHoveringOverEntity( - overworld_.mutable_exits()->at(current_exit_id_), - ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling()); - - if (!hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { - ImGui::OpenPopup("Exit Inserter"); - } else { - if (DrawExitEditorPopup( - overworld_.mutable_exits()->at(current_exit_id_))) { - overworld_.mutable_exits()->at(current_exit_id_) = current_exit_; - } - } - } -} - -void OverworldEditor::DrawOverworldItems() { - int i = 0; - for (auto& item : *overworld_.mutable_all_items()) { - // Get the item's bitmap and real X and Y positions - if (item.room_map_id_ < 0x40 + (current_world_ * 0x40) && - item.room_map_id_ >= (current_world_ * 0x40) && !item.deleted) { - ow_map_canvas_.DrawRect(item.x_, item.y_, 16, 16, gui::GetItemColor()); - - if (current_mode == EditingMode::ITEMS) { - // Check if this item is being clicked and dragged - HandleEntityDragging(&item, ow_map_canvas_.zero_point(), - ow_map_canvas_.scrolling(), is_dragging_entity_, - dragged_entity_, current_entity_); - - const auto hovering = IsMouseHoveringOverEntity( - item, ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling()); - if (hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { - current_item_id_ = i; - current_item_ = item; - current_entity_ = &item; - } - } - std::string item_name = ""; - if (item.id_ < zelda3::kSecretItemNames.size()) { - item_name = zelda3::kSecretItemNames[item.id_]; - } else { - item_name = absl::StrFormat("0x%02X", item.id_); - } - ow_map_canvas_.DrawText(item_name, item.x_, item.y_); - } - i++; - } - - DrawItemInsertPopup(); - if (current_mode == EditingMode::ITEMS) { - const auto hovering = IsMouseHoveringOverEntity( - overworld_.mutable_all_items()->at(current_item_id_), - ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling()); - - if (!hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { - ImGui::OpenPopup("Item Inserter"); - } else { - if (DrawItemEditorPopup( - overworld_.mutable_all_items()->at(current_item_id_))) { - overworld_.mutable_all_items()->at(current_item_id_) = current_item_; - } - } - } -} - -void OverworldEditor::DrawOverworldSprites() { - int i = 0; - for (auto& sprite : *overworld_.mutable_sprites(game_state_)) { - // Filter sprites by current world - only show sprites for the current world - if (!sprite.deleted() && sprite.map_id() < 0x40 + (current_world_ * 0x40) && - sprite.map_id() >= (current_world_ * 0x40)) { - // Sprites are already stored with global coordinates (realX, realY from - // ROM loading) So we can use sprite.x_ and sprite.y_ directly - int sprite_x = sprite.x_; - int sprite_y = sprite.y_; - - // Temporarily update sprite coordinates for entity interaction - int original_x = sprite.x_; - int original_y = sprite.y_; - - ow_map_canvas_.DrawRect(sprite_x, sprite_y, kTile16Size, kTile16Size, - gui::GetSpriteColor()); - if (current_mode == EditingMode::SPRITES) { - HandleEntityDragging(&sprite, ow_map_canvas_.zero_point(), - ow_map_canvas_.scrolling(), is_dragging_entity_, - dragged_entity_, current_entity_); - if (IsMouseHoveringOverEntity(sprite, ow_map_canvas_.zero_point(), - ow_map_canvas_.scrolling()) && - ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { - current_sprite_id_ = i; - current_sprite_ = sprite; - } - } - if (core::FeatureFlags::get().overworld.kDrawOverworldSprites) { - if (sprite_previews_[sprite.id()].is_active()) { - ow_map_canvas_.DrawBitmap(sprite_previews_[sprite.id()], sprite_x, - sprite_y, 2.0f); - } - } - - ow_map_canvas_.DrawText(absl::StrFormat("%s", sprite.name()), sprite_x, - sprite_y); - - // Restore original coordinates - sprite.x_ = original_x; - sprite.y_ = original_y; - } - i++; - } - - DrawSpriteInserterPopup(); - if (current_mode == EditingMode::SPRITES) { - const auto hovering = IsMouseHoveringOverEntity( - overworld_.mutable_sprites(game_state_)->at(current_sprite_id_), - ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling()); - - if (!hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { - ImGui::OpenPopup("Sprite Inserter"); - } else { - if (DrawSpriteEditorPopup(overworld_.mutable_sprites(game_state_) - ->at(current_sprite_id_))) { - overworld_.mutable_sprites(game_state_)->at(current_sprite_id_) = - current_sprite_; - } - } - } -} +// DrawOverworldEntrances(), DrawOverworldExits(), DrawOverworldItems(), DrawOverworldSprites() +// removed - moved to OverworldEntityRenderer absl::Status OverworldEditor::Save() { if (core::FeatureFlags::get().overworld.kSaveOverworldMaps) { diff --git a/src/app/editor/overworld/overworld_editor.h b/src/app/editor/overworld/overworld_editor.h index e8c653ba..0d894c29 100644 --- a/src/app/editor/overworld/overworld_editor.h +++ b/src/app/editor/overworld/overworld_editor.h @@ -7,6 +7,7 @@ #include "app/editor/graphics/palette_editor.h" #include "app/editor/overworld/tile16_editor.h" #include "app/editor/overworld/map_properties.h" +#include "app/editor/overworld/overworld_entity_renderer.h" #include "app/gfx/bitmap.h" #include "app/gfx/snes_palette.h" #include "app/gfx/tilemap.h" @@ -123,11 +124,6 @@ class OverworldEditor : public Editor, public gfx::GfxContext { void RefreshMapProperties(); absl::Status RefreshTile16Blockset(); - void DrawOverworldEntrances(ImVec2 canvas_p, ImVec2 scrolling); - void DrawOverworldExits(ImVec2 zero, ImVec2 scrolling); - void DrawOverworldItems(); - void DrawOverworldSprites(); - void DrawOverworldMaps(); void DrawOverworldEdits(); void RenderUpdatedMapBitmap(const ImVec2& click_position, @@ -276,6 +272,7 @@ class OverworldEditor : public Editor, public gfx::GfxContext { // Map properties system for UI organization std::unique_ptr map_properties_system_; std::unique_ptr overworld_manager_; + std::unique_ptr entity_renderer_; // Scratch space for large layouts // Scratch space canvas for tile16 drawing (like a mini overworld) diff --git a/src/app/editor/overworld/overworld_entity_renderer.cc b/src/app/editor/overworld/overworld_entity_renderer.cc new file mode 100644 index 00000000..05919e67 --- /dev/null +++ b/src/app/editor/overworld/overworld_entity_renderer.cc @@ -0,0 +1,260 @@ +#include "overworld_entity_renderer.h" + +#include "absl/strings/str_format.h" +#include "app/core/features.h" +#include "app/editor/overworld/entity.h" +#include "app/gui/canvas.h" +#include "app/zelda3/common.h" +#include "util/hex.h" +#include "imgui/imgui.h" + +namespace yaze { +namespace editor { + +using namespace ImGui; + +// Entity colors - these could be theme-dependent in the future +namespace { +ImVec4 GetEntranceColor() { return ImVec4(0.0f, 1.0f, 0.0f, 0.5f); } // Green +ImVec4 GetExitColor() { return ImVec4(1.0f, 0.0f, 0.0f, 0.5f); } // Red +ImVec4 GetItemColor() { return ImVec4(1.0f, 1.0f, 0.0f, 0.5f); } // Yellow +ImVec4 GetSpriteColor() { return ImVec4(1.0f, 0.5f, 0.0f, 0.5f); } // Orange +} // namespace + +void OverworldEntityRenderer::DrawEntrances(ImVec2 canvas_p0, ImVec2 scrolling, + int current_world, + int current_mode) { + int i = 0; + for (auto& each : overworld_->entrances()) { + if (each.map_id_ < 0x40 + (current_world * 0x40) && + each.map_id_ >= (current_world * 0x40) && !each.deleted) { + // Use theme-aware color with proper transparency + ImVec4 entrance_color = GetEntranceColor(); + if (each.is_hole_) { + // Holes are more opaque for visibility + entrance_color.w = 0.78f; // 200/255 alpha + } + canvas_->DrawRect(each.x_, each.y_, 16, 16, entrance_color); + std::string str = util::HexByte(each.entrance_id_); + + if (current_mode == 1) { // EditingMode::ENTRANCES + HandleEntityDragging(&each, canvas_p0, scrolling, is_dragging_entity_, + dragged_entity_, current_entity_); + + if (IsMouseHoveringOverEntity(each, canvas_p0, scrolling) && + ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) { + jump_to_tab_ = each.entrance_id_; + } + + if (IsMouseHoveringOverEntity(each, canvas_p0, scrolling) && + ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { + current_entrance_id_ = i; + current_entrance_ = each; + } + } + + canvas_->DrawText(str, each.x_, each.y_); + } + i++; + } + + if (DrawEntranceInserterPopup()) { + // Get the deleted entrance ID and insert it at the mouse position + auto deleted_entrance_id = overworld_->deleted_entrances().back(); + overworld_->deleted_entrances().pop_back(); + auto& entrance = overworld_->entrances()[deleted_entrance_id]; + entrance.map_id_ = current_map_; + entrance.entrance_id_ = deleted_entrance_id; + entrance.x_ = canvas_->hover_mouse_pos().x; + entrance.y_ = canvas_->hover_mouse_pos().y; + entrance.deleted = false; + } + + if (current_mode == 1) { // EditingMode::ENTRANCES + const auto is_hovering = + IsMouseHoveringOverEntity(current_entrance_, canvas_p0, scrolling); + + if (!is_hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { + ImGui::OpenPopup("Entrance Inserter"); + } else { + if (DrawOverworldEntrancePopup( + overworld_->entrances()[current_entrance_id_])) { + overworld_->entrances()[current_entrance_id_] = current_entrance_; + } + + if (overworld_->entrances()[current_entrance_id_].deleted) { + overworld_->mutable_deleted_entrances()->emplace_back( + current_entrance_id_); + } + } + } +} + +void OverworldEntityRenderer::DrawExits(ImVec2 canvas_p0, ImVec2 scrolling, + int current_world, + int current_mode) { + int i = 0; + for (auto& each : *overworld_->mutable_exits()) { + if (each.map_id_ < 0x40 + (current_world * 0x40) && + each.map_id_ >= (current_world * 0x40) && !each.deleted_) { + canvas_->DrawRect(each.x_, each.y_, 16, 16, GetExitColor()); + if (current_mode == 2) { // EditingMode::EXITS + each.entity_id_ = i; + HandleEntityDragging(&each, canvas_->zero_point(), + canvas_->scrolling(), is_dragging_entity_, + dragged_entity_, current_entity_, true); + + if (IsMouseHoveringOverEntity(each, canvas_p0, scrolling) && + ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) { + jump_to_tab_ = each.room_id_; + } + + if (IsMouseHoveringOverEntity(each, canvas_p0, scrolling) && + ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { + current_exit_id_ = i; + current_exit_ = each; + current_entity_ = &each; + current_entity_->entity_id_ = i; + ImGui::OpenPopup("Exit editor"); + } + } + + std::string str = util::HexByte(i); + canvas_->DrawText(str, each.x_, each.y_); + } + i++; + } + + DrawExitInserterPopup(); + if (current_mode == 2) { // EditingMode::EXITS + const auto hovering = IsMouseHoveringOverEntity( + overworld_->mutable_exits()->at(current_exit_id_), + canvas_->zero_point(), canvas_->scrolling()); + + if (!hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { + ImGui::OpenPopup("Exit Inserter"); + } else { + if (DrawExitEditorPopup( + overworld_->mutable_exits()->at(current_exit_id_))) { + overworld_->mutable_exits()->at(current_exit_id_) = current_exit_; + } + } + } +} + +void OverworldEntityRenderer::DrawItems(int current_world, int current_mode) { + int i = 0; + for (auto& item : *overworld_->mutable_all_items()) { + // Get the item's bitmap and real X and Y positions + if (item.room_map_id_ < 0x40 + (current_world * 0x40) && + item.room_map_id_ >= (current_world * 0x40) && !item.deleted) { + canvas_->DrawRect(item.x_, item.y_, 16, 16, GetItemColor()); + + if (current_mode == 3) { // EditingMode::ITEMS + // Check if this item is being clicked and dragged + HandleEntityDragging(&item, canvas_->zero_point(), + canvas_->scrolling(), is_dragging_entity_, + dragged_entity_, current_entity_); + + const auto hovering = IsMouseHoveringOverEntity( + item, canvas_->zero_point(), canvas_->scrolling()); + if (hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { + current_item_id_ = i; + current_item_ = item; + current_entity_ = &item; + } + } + std::string item_name = ""; + if (item.id_ < zelda3::kSecretItemNames.size()) { + item_name = zelda3::kSecretItemNames[item.id_]; + } else { + item_name = absl::StrFormat("0x%02X", item.id_); + } + canvas_->DrawText(item_name, item.x_, item.y_); + } + i++; + } + + DrawItemInsertPopup(); + if (current_mode == 3) { // EditingMode::ITEMS + const auto hovering = IsMouseHoveringOverEntity( + overworld_->mutable_all_items()->at(current_item_id_), + canvas_->zero_point(), canvas_->scrolling()); + + if (!hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { + ImGui::OpenPopup("Item Inserter"); + } else { + if (DrawItemEditorPopup( + overworld_->mutable_all_items()->at(current_item_id_))) { + overworld_->mutable_all_items()->at(current_item_id_) = current_item_; + } + } + } +} + +void OverworldEntityRenderer::DrawSprites(int current_world, int game_state, + int current_mode) { + int i = 0; + for (auto& sprite : *overworld_->mutable_sprites(game_state)) { + // Filter sprites by current world - only show sprites for the current world + if (!sprite.deleted() && sprite.map_id() < 0x40 + (current_world * 0x40) && + sprite.map_id() >= (current_world * 0x40)) { + // Sprites are already stored with global coordinates (realX, realY from + // ROM loading) So we can use sprite.x_ and sprite.y_ directly + int sprite_x = sprite.x_; + int sprite_y = sprite.y_; + + // Temporarily update sprite coordinates for entity interaction + int original_x = sprite.x_; + int original_y = sprite.y_; + + canvas_->DrawRect(sprite_x, sprite_y, 16, 16, GetSpriteColor()); + if (current_mode == 4) { // EditingMode::SPRITES + HandleEntityDragging(&sprite, canvas_->zero_point(), + canvas_->scrolling(), is_dragging_entity_, + dragged_entity_, current_entity_); + if (IsMouseHoveringOverEntity(sprite, canvas_->zero_point(), + canvas_->scrolling()) && + ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { + current_sprite_id_ = i; + current_sprite_ = sprite; + } + } + if (core::FeatureFlags::get().overworld.kDrawOverworldSprites) { + if ((*sprite_previews_)[sprite.id()].is_active()) { + canvas_->DrawBitmap((*sprite_previews_)[sprite.id()], sprite_x, + sprite_y, 2.0f); + } + } + + canvas_->DrawText(absl::StrFormat("%s", sprite.name()), sprite_x, + sprite_y); + + // Restore original coordinates + sprite.x_ = original_x; + sprite.y_ = original_y; + } + i++; + } + + DrawSpriteInserterPopup(); + if (current_mode == 4) { // EditingMode::SPRITES + const auto hovering = IsMouseHoveringOverEntity( + overworld_->mutable_sprites(game_state)->at(current_sprite_id_), + canvas_->zero_point(), canvas_->scrolling()); + + if (!hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { + ImGui::OpenPopup("Sprite Inserter"); + } else { + if (DrawSpriteEditorPopup(overworld_->mutable_sprites(game_state) + ->at(current_sprite_id_))) { + overworld_->mutable_sprites(game_state)->at(current_sprite_id_) = + current_sprite_; + } + } + } +} + +} // namespace editor +} // namespace yaze + diff --git a/src/app/editor/overworld/overworld_entity_renderer.h b/src/app/editor/overworld/overworld_entity_renderer.h new file mode 100644 index 00000000..057b0f53 --- /dev/null +++ b/src/app/editor/overworld/overworld_entity_renderer.h @@ -0,0 +1,90 @@ +#ifndef YAZE_APP_EDITOR_OVERWORLD_OVERWORLD_ENTITY_RENDERER_H +#define YAZE_APP_EDITOR_OVERWORLD_OVERWORLD_ENTITY_RENDERER_H + +#include + +#include "app/gfx/bitmap.h" +#include "app/gui/canvas.h" +#include "app/zelda3/overworld/overworld.h" +#include "app/zelda3/overworld/overworld_entrance.h" +#include "app/zelda3/overworld/overworld_exit.h" +#include "app/zelda3/overworld/overworld_item.h" +#include "app/zelda3/sprite/sprite.h" +#include "imgui/imgui.h" + +namespace yaze { +namespace editor { + +class OverworldEditor; // Forward declaration + +/** + * @class OverworldEntityRenderer + * @brief Handles visualization of all overworld entities (entrances, exits, items, sprites) + * + * This class separates entity rendering logic from the main OverworldEditor, + * making it easier to maintain and test entity visualization independently. + */ +class OverworldEntityRenderer { + public: + OverworldEntityRenderer(zelda3::Overworld* overworld, gui::Canvas* canvas, + std::vector* sprite_previews) + : overworld_(overworld), canvas_(canvas), + sprite_previews_(sprite_previews) {} + + // Main rendering methods + void DrawEntrances(ImVec2 canvas_p0, ImVec2 scrolling, int current_world, + int current_mode); + void DrawExits(ImVec2 canvas_p0, ImVec2 scrolling, int current_world, + int current_mode); + void DrawItems(int current_world, int current_mode); + void DrawSprites(int current_world, int game_state, int current_mode); + + // State accessors + int current_entrance_id() const { return current_entrance_id_; } + int current_exit_id() const { return current_exit_id_; } + int current_item_id() const { return current_item_id_; } + int current_sprite_id() const { return current_sprite_id_; } + int jump_to_tab() const { return jump_to_tab_; } + + zelda3::OverworldEntrance& current_entrance() { return current_entrance_; } + zelda3::OverworldExit& current_exit() { return current_exit_; } + zelda3::OverworldItem& current_item() { return current_item_; } + zelda3::Sprite& current_sprite() { return current_sprite_; } + + void set_current_map(int map) { current_map_ = map; } + void set_is_dragging_entity(bool value) { is_dragging_entity_ = value; } + void set_dragged_entity(zelda3::GameEntity* entity) { dragged_entity_ = entity; } + void set_current_entity(zelda3::GameEntity* entity) { current_entity_ = entity; } + + void reset_jump_to_tab() { jump_to_tab_ = -1; } + + private: + zelda3::Overworld* overworld_; + gui::Canvas* canvas_; + std::vector* sprite_previews_; + + // Current entity selections + int current_entrance_id_ = 0; + int current_exit_id_ = 0; + int current_item_id_ = 0; + int current_sprite_id_ = 0; + int current_map_ = 0; + int jump_to_tab_ = -1; + + // Entity interaction state + bool is_dragging_entity_ = false; + zelda3::GameEntity* dragged_entity_ = nullptr; + zelda3::GameEntity* current_entity_ = nullptr; + + // Current entity instances for editing + zelda3::OverworldEntrance current_entrance_; + zelda3::OverworldExit current_exit_; + zelda3::OverworldItem current_item_; + zelda3::Sprite current_sprite_; +}; + +} // namespace editor +} // namespace yaze + +#endif // YAZE_APP_EDITOR_OVERWORLD_OVERWORLD_ENTITY_RENDERER_H +