15#include "absl/status/status.h"
16#include "absl/strings/str_format.h"
17#include "imgui/imgui.h"
72 for (
auto& room :
rooms_) {
88 .display_name =
"Dungeon Controls",
89 .window_title =
" Dungeon Controls",
91 .category =
"Dungeon",
92 .shortcut_hint =
"Ctrl+Shift+D",
93 .visibility_flag =
nullptr,
95 .disabled_tooltip =
"Load a ROM to access dungeon controls",
98 panel_manager->RegisterPanel(
100 .display_name =
"Room List",
101 .window_title =
" Room List",
103 .category =
"Dungeon",
104 .shortcut_hint =
"Ctrl+Shift+R",
105 .visibility_flag =
nullptr,
107 .disabled_tooltip =
"Load a ROM to browse dungeon rooms",
110 panel_manager->RegisterPanel(
112 .display_name =
"Entrance List",
113 .window_title =
" Entrance List",
115 .category =
"Dungeon",
116 .shortcut_hint =
"Ctrl+Shift+E",
117 .visibility_flag =
nullptr,
119 .disabled_tooltip =
"Load a ROM to browse dungeon entrances",
122 panel_manager->RegisterPanel(
123 {.card_id =
"dungeon.entrance_properties",
124 .display_name =
"Entrance Properties",
125 .window_title =
" Entrance Properties",
127 .category =
"Dungeon",
129 .visibility_flag =
nullptr,
131 .disabled_tooltip =
"Load a ROM to edit entrance properties",
134 panel_manager->RegisterPanel(
136 .display_name =
"Room Matrix",
137 .window_title =
" Room Matrix",
139 .category =
"Dungeon",
140 .shortcut_hint =
"Ctrl+Shift+M",
141 .visibility_flag =
nullptr,
143 .disabled_tooltip =
"Load a ROM to view the room matrix",
146 panel_manager->RegisterPanel(
148 .display_name =
"Room Graphics",
149 .window_title =
" Room Graphics",
151 .category =
"Dungeon",
152 .shortcut_hint =
"Ctrl+Shift+G",
153 .visibility_flag =
nullptr,
155 .disabled_tooltip =
"Load a ROM to view room graphics",
158 panel_manager->RegisterPanel(
160 .display_name =
"Palette Editor",
161 .window_title =
" Palette Editor",
163 .category =
"Dungeon",
164 .shortcut_hint =
"Ctrl+Shift+P",
165 .visibility_flag =
nullptr,
167 .disabled_tooltip =
"Load a ROM to edit dungeon palettes",
175 panel_manager->RegisterEditorPanel(std::make_unique<DungeonRoomSelectorPanel>(
178 panel_manager->RegisterEditorPanel(std::make_unique<DungeonEntranceListPanel>(
182 panel_manager->RegisterEditorPanel(std::make_unique<DungeonRoomMatrixPanel>(
187 panel_manager->RegisterEditorPanel(std::make_unique<DungeonEntrancesPanel>(
199 return absl::FailedPreconditionError(
"ROM not loaded");
204 if (!dim_table.IsLoaded()) {
211 return absl::FailedPreconditionError(
"GameData not available");
228 std::make_unique<emu::render::EmulatorRenderService>(
rom_);
231 LOG_ERROR(
"DungeonEditorV2",
"Failed to initialize render service: %s",
232 status.message().data());
246 auto graphics_panel = std::make_unique<DungeonRoomGraphicsPanel>(
260 auto object_editor = std::make_unique<ObjectEditorPanel>(
267 rooms_[current_room_id_].RenderRoomGraphics();
272 object_editor->SetRooms(&
rooms_);
290 auto sprite_panel = std::make_unique<SpriteEditorPanel>(
295 auto item_panel = std::make_unique<ItemEditorPanel>(
303 auto minecart_panel = std::make_unique<MinecartTrackEditorPanel>();
329 if (room_id >= 0 && room_id < (
int)
rooms_.size()) {
330 rooms_[room_id].RenderRoomGraphics();
336 return absl::OkStatus();
348 if (loading_card.
Begin()) {
349 ImGui::TextColored(theme.text_secondary_gray,
"Loading dungeon data...");
351 "Independent editor cards will appear once ROM data is loaded.");
354 return absl::OkStatus();
359 if (ImGui::IsKeyPressed(ImGuiKey_Delete)) {
362 viewer->DeleteSelectedObjects();
370 return absl::OkStatus();
377 return absl::UnimplementedError(
"Undo not available");
384 return absl::UnimplementedError(
"Redo not available");
389 return absl::FailedPreconditionError(
"ROM not loaded");
395 LOG_ERROR(
"DungeonEditorV2",
"Failed to save palette changes: %s",
396 status.message().data());
399 LOG_INFO(
"DungeonEditorV2",
"Saved %zu modified colors to ROM",
403 for (
auto& room :
rooms_) {
404 auto status = room.SaveObjects();
406 LOG_ERROR(
"DungeonEditorV2",
"Failed to save room objects: %s",
407 status.message().data());
412 if (!sys_status.ok()) {
413 LOG_ERROR(
"DungeonEditorV2",
"Failed to save room system data: %s",
414 sys_status.message().data());
422 LOG_ERROR(
"DungeonEditorV2",
"DungeonEditorSystem save failed: %s",
423 status.message().data());
428 return absl::OkStatus();
434 std::string card_id = absl::StrFormat(
"dungeon.room_%d", room_id);
435 bool panel_visible =
true;
440 if (!panel_visible) {
452 std::string active_category =
457 if (active_category !=
"Dungeon" && !is_pinned) {
464 std::string base_name = absl::StrFormat(
"[%03X] %s", room_id,
467 std::string card_name_str = absl::StrFormat(
471 room_cards_[room_id] = std::make_shared<gui::PanelWindow>(
487 ImGui::SetNextWindowDockID(
room_dock_id_, ImGuiCond_FirstUseEver);
489 if (room_card->Begin(&open)) {
491 if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) {
513 if (room_id < 0 || room_id >= 0x128) {
514 ImGui::Text(
"Invalid room ID: %d", room_id);
518 auto& room =
rooms_[room_id];
520 if (!room.IsLoaded()) {
523 ImGui::TextColored(theme.text_error_red,
"Failed to load room: %s",
524 status.message().data());
530 if (!sys_status.ok()) {
531 LOG_ERROR(
"DungeonEditorV2",
"Failed to load system data: %s",
532 sys_status.message().data());
537 if (room.IsLoaded()) {
538 bool needs_render =
false;
543 if (room.blocks().empty()) {
544 room.LoadRoomGraphics(room.blockset);
546 LOG_DEBUG(
"[DungeonEditorV2]",
"Loaded room %d graphics from ROM",
555 if (room.GetTileObjects().empty()) {
558 LOG_DEBUG(
"[DungeonEditorV2]",
"Loaded room %d objects from ROM",
568 auto& bg1_bitmap = room.bg1_buffer().bitmap();
569 if (needs_render || !bg1_bitmap.is_active() || bg1_bitmap.width() == 0) {
570 room.RenderRoomGraphics();
571 LOG_DEBUG(
"[DungeonEditorV2]",
"Rendered room %d to bitmaps", room_id);
575 if (room.IsLoaded()) {
576 ImGui::TextColored(theme.text_success_green,
ICON_MD_CHECK " Loaded");
578 ImGui::TextColored(theme.text_error_red,
ICON_MD_PENDING " Not Loaded");
581 ImGui::TextDisabled(
"Objects: %zu", room.GetTileObjects().size());
587 viewer->DrawDungeonCanvas(room_id);
614 if (room_id >= 0 && room_id < (
int)
rooms_.size()) {
615 auto& room =
rooms_[room_id];
616 if (!room.IsLoaded()) {
620 if (room.IsLoaded()) {
629 auto dungeon_main_pal_group =
659 std::string card_id = absl::StrFormat(
"dungeon.room_%d", room_id);
674 std::string room_name = absl::StrFormat(
"[%03X] %s", room_id,
677 std::string base_card_id = absl::StrFormat(
"dungeon.room_%d", room_id);
680 {.card_id = base_card_id,
681 .display_name = room_name,
684 .category =
"Dungeon",
686 .visibility_flag =
nullptr,
687 .priority = 200 + room_id});
694 if (entrance_id < 0 || entrance_id >=
static_cast<int>(
entrances_.size())) {
735 LOG_ERROR(
"DungeonEditorV2",
"Cannot place object: Invalid room ID %d",
743 "Placing object ID=0x%02X at position (%d,%d) in room %03X", obj.
id_,
746 room.RenderRoomGraphics();
748 "Object placed and room re-rendered successfully");
753 viewer->object_interaction().HandleCopySelected();
754 viewer->object_interaction().HandleDeleteSelected();
756 return absl::OkStatus();
761 viewer->object_interaction().HandleCopySelected();
763 return absl::OkStatus();
768 viewer->object_interaction().HandlePasteObjects();
770 return absl::OkStatus();
774 if (room_id < 0 || room_id >=
static_cast<int>(
rooms_.size()))
return;
781 int room_id, std::vector<zelda3::RoomObject> snapshot) {
782 if (room_id < 0 || room_id >=
static_cast<int>(
rooms_.size())) {
783 return absl::InvalidArgumentError(
"Invalid room ID");
786 auto& room =
rooms_[room_id];
787 room.GetTileObjects() = std::move(snapshot);
788 room.RenderRoomGraphics();
789 return absl::OkStatus();
797 if (new_room_id < 0 || new_room_id >=
static_cast<int>(
rooms_.size())) {
823 if (swap_index < 0) {
835 std::string old_card_id = absl::StrFormat(
"dungeon.room_%d", old_room_id);
846 std::string new_room_name = absl::StrFormat(
849 std::string new_card_id = absl::StrFormat(
"dungeon.room_%d", new_room_id);
852 {.card_id = new_card_id,
853 .display_name = new_room_name,
856 .category =
"Dungeon",
858 .visibility_flag =
nullptr,
859 .priority = 200 + new_room_id});
871 auto viewer = std::make_unique<DungeonCanvasViewer>(
rom_);
872 viewer->SetRooms(&
rooms_);
878 viewer->object_interaction().SetMutationHook(
881 viewer->object_interaction().SetCacheInvalidationCallback(
883 if (room_id >= 0 && room_id <
static_cast<int>(
rooms_.size())) {
884 rooms_[room_id].MarkObjectsDirty();
885 rooms_[room_id].RenderRoomGraphics();
889 viewer->object_interaction().SetObjectPlacedCallback(
895 viewer->SetRoomNavigationCallback(
896 [
this](
int target_room) {
897 if (target_room >= 0 &&
898 target_room <
static_cast<int>(
rooms_.size())) {
903 viewer->SetRoomSwapCallback(
904 [
this](
int old_room,
int new_room) {
907 viewer->SetShowObjectPanelCallback([
this]() {
910 viewer->SetShowSpritePanelCallback([
this]() {
913 viewer->SetShowItemPanelCallback([
this]() {
920 return it->second.get();
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
class MinecartTrackEditorPanel * minecart_track_editor_panel_
static constexpr const char * kControlPanelId
void SetAgentMode(bool enabled)
std::array< zelda3::Room, 0x128 > rooms_
uint64_t current_palette_group_id_
void add_room(int room_id)
class ItemEditorPanel * item_editor_panel_
std::unordered_map< int, std::vector< std::vector< zelda3::RoomObject > > > undo_history_
std::array< zelda3::RoomEntrance, 0x8C > entrances_
gfx::PaletteGroup current_palette_group_
void OnEntranceSelected(int entrance_id)
static constexpr const char * kEntranceListId
absl::Status Save() override
gfx::IRenderer * renderer_
class SpriteEditorPanel * sprite_editor_panel_
void FocusRoom(int room_id)
void HandleObjectPlaced(const zelda3::RoomObject &obj)
std::unique_ptr< ObjectEditorPanel > owned_object_editor_panel_
std::unique_ptr< zelda3::DungeonEditorSystem > dungeon_editor_system_
void PushUndoSnapshot(int room_id)
absl::Status Copy() override
void Initialize() override
void OnRoomSelected(int room_id, bool request_focus=true)
~DungeonEditorV2() override
DungeonRoomGraphicsPanel * room_graphics_panel_
ObjectEditorPanel * object_editor_panel_
void ProcessDeferredTextures()
gfx::SnesPalette current_palette_
ImVector< int > active_rooms_
absl::Status Cut() override
std::map< int, std::unique_ptr< DungeonCanvasViewer > > room_viewers_
gui::PaletteEditorWidget palette_editor_
void ShowPanel(const std::string &card_id)
absl::Status Paste() override
void SwapRoomInPanel(int old_room_id, int new_room_id)
void DrawRoomTab(int room_id)
void ProcessPendingSwap()
absl::Status RestoreFromSnapshot(int room_id, std::vector< zelda3::RoomObject > snapshot)
PendingSwap pending_swap_
static constexpr const char * kObjectToolsId
DungeonCanvasViewer * GetViewerForRoom(int room_id)
absl::Status Update() override
absl::Status Undo() override
ImGuiWindowClass room_window_class_
void SelectObject(int obj_id)
std::unique_ptr< emu::render::EmulatorRenderService > render_service_
DungeonRoomLoader room_loader_
std::unordered_map< int, std::vector< std::vector< zelda3::RoomObject > > > redo_history_
std::unordered_map< int, std::shared_ptr< gui::PanelWindow > > room_cards_
absl::Status Redo() override
static constexpr const char * kRoomGraphicsId
static constexpr const char * kRoomMatrixId
static constexpr const char * kRoomSelectorId
DungeonRoomSelector room_selector_
zelda3::GameData * game_data_
static constexpr const char * kPaletteEditorId
void ClearRedo(int room_id)
uint64_t current_palette_id_
void SetCurrentPaletteGroup(const gfx::PaletteGroup &group)
Set the current palette group for graphics rendering.
absl::Status LoadRoomEntrances(std::array< zelda3::RoomEntrance, 0x8C > &entrances)
absl::Status LoadRoom(int room_id, zelda3::Room &room)
void set_entrances(std::array< zelda3::RoomEntrance, 0x8C > *entrances)
void SetRoomSelectedCallback(std::function< void(int)> callback)
void set_active_rooms(const ImVector< int > &rooms)
void set_rooms(std::array< zelda3::Room, 0x128 > *rooms)
zelda3::GameData * game_data() const
EditorDependencies dependencies_
std::string MakePanelTitle(const std::string &base_title) const
void SetCanvasViewer(DungeonCanvasViewer *viewer)
void SetProjectRoot(const std::string &root)
void SetCanvasViewer(DungeonCanvasViewer *viewer)
void SetCurrentPaletteGroup(const gfx::PaletteGroup &group)
void SetGameData(zelda3::GameData *game_data)
void SelectObject(int obj_id)
void SetCurrentRoom(int room_id)
void SetAgentOptimizedLayout(bool enabled)
bool ShowPanel(size_t session_id, const std::string &base_card_id)
void RegisterPanel(size_t session_id, const PanelDescriptor &base_info)
std::string GetActiveCategory() const
bool IsPanelVisible(size_t session_id, const std::string &base_card_id) const
void UnregisterPanel(size_t session_id, const std::string &base_card_id)
bool IsPanelPinned(size_t session_id, const std::string &base_card_id) const
void RegisterEditorPanel(std::unique_ptr< EditorPanel > panel)
Register an EditorPanel instance for central drawing.
void SetCanvasViewer(DungeonCanvasViewer *viewer)
void ProcessTextureQueue(IRenderer *renderer)
Defines an abstract interface for all rendering operations.
void Initialize(zelda3::GameData *game_data)
Initialize the palette manager with GameData.
static PaletteManager & Get()
Get the singleton instance.
absl::Status SaveAllToRom()
Save ALL modified palettes to ROM.
Draggable, dockable panel for editor sub-windows.
bool Begin(bool *p_open=nullptr)
void SetDefaultSize(float width, float height)
static CustomObjectManager & Get()
void Initialize(const std::string &custom_objects_folder)
static ObjectDimensionTable & Get()
#define ICON_MD_GRID_VIEW
#define ICON_MD_DOOR_FRONT
#define LOG_DEBUG(category, format,...)
#define LOG_ERROR(category, format,...)
#define LOG_INFO(category, format,...)
#define ASSIGN_OR_RETURN(type_variable_name, expression)
const AgentUITheme & GetTheme()
Editors are the view controllers for the application.
absl::StatusOr< PaletteGroup > CreatePaletteGroupFromLargePalette(SnesPalette &palette, int num_colors)
Create a PaletteGroup by dividing a large palette into sub-palettes.
std::string GetRoomLabel(int id)
Convenience function to get a room label.
#define RETURN_IF_ERROR(expr)
project::YazeProject * project
PanelManager * panel_manager
PaletteGroup dungeon_main
std::string custom_objects_folder
gfx::PaletteGroupMap palette_groups