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.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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<OverworldEditorManager>(&overworld_, rom_, this);
|
||||
|
||||
// Initialize OverworldEntityRenderer for entity visualization
|
||||
entity_renderer_ = std::make_unique<OverworldEntityRenderer>(
|
||||
&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<int>(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) {
|
||||
|
||||
@@ -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<MapPropertiesSystem> map_properties_system_;
|
||||
std::unique_ptr<OverworldEditorManager> overworld_manager_;
|
||||
std::unique_ptr<OverworldEntityRenderer> entity_renderer_;
|
||||
|
||||
// Scratch space for large layouts
|
||||
// Scratch space canvas for tile16 drawing (like a mini overworld)
|
||||
|
||||
260
src/app/editor/overworld/overworld_entity_renderer.cc
Normal file
260
src/app/editor/overworld/overworld_entity_renderer.cc
Normal file
@@ -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
|
||||
|
||||
90
src/app/editor/overworld/overworld_entity_renderer.h
Normal file
90
src/app/editor/overworld/overworld_entity_renderer.h
Normal file
@@ -0,0 +1,90 @@
|
||||
#ifndef YAZE_APP_EDITOR_OVERWORLD_OVERWORLD_ENTITY_RENDERER_H
|
||||
#define YAZE_APP_EDITOR_OVERWORLD_OVERWORLD_ENTITY_RENDERER_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#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<gfx::Bitmap>* 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<gfx::Bitmap>* 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
|
||||
|
||||
Reference in New Issue
Block a user