yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
dungeon_editor_v2.h
Go to the documentation of this file.
1#ifndef YAZE_APP_EDITOR_DUNGEON_EDITOR_V2_H
2#define YAZE_APP_EDITOR_DUNGEON_EDITOR_V2_H
3
4#include <array>
5#include <cstdint>
6#include <deque>
7#include <memory>
8#include <optional>
9#include <string>
10#include <unordered_map>
11#include <utility>
12#include <vector>
13
14#include "absl/status/status.h"
15#include "absl/strings/str_format.h"
16#include "app/editor/editor.h"
24#include "dungeon_room_loader.h"
26#include "dungeon_room_store.h"
28#include "imgui/imgui.h"
31#include "rom/rom.h"
33#include "util/lru_cache.h"
36#include "zelda3/dungeon/room.h"
39#include "zelda3/game_data.h"
40
41namespace yaze {
42namespace editor {
43
44class MinecartTrackEditorPanel;
45class ObjectTileEditorPanel;
46class OverlayManagerPanel;
47class RoomTagEditorPanel;
48
88class DungeonEditorV2 : public Editor {
89 public:
97
98 ~DungeonEditorV2() override;
99
102 dependencies_.game_data = game_data; // Also set base class dependency
106 }
108 dungeon_editor_system_->SetGameData(game_data);
109 }
111 // Note: Canvas viewer game data is set lazily in GetViewerForRoom
112 // but we should update existing viewers
113 room_viewers_.ForEach(
114 [game_data](int, std::unique_ptr<DungeonCanvasViewer>& viewer) {
115 if (viewer)
116 viewer->SetGameData(game_data);
117 });
118 }
119
120 // Editor interface
121 void Initialize() override;
122 absl::Status Load() override;
123 absl::Status Update() override;
124 absl::Status Undo() override;
125 absl::Status Redo() override;
126 absl::Status Cut() override;
127 absl::Status Copy() override;
128 absl::Status Paste() override;
129 absl::Status Find() override { return absl::UnimplementedError("Find"); }
130 absl::Status Save() override;
131 void ContributeStatus(StatusBar* status_bar) override;
132 absl::Status SaveRoom(int room_id);
133 int LoadedRoomCount() const;
134 int TotalRoomCount() const { return static_cast<int>(rooms_.size()); }
135
136 // Collect PC write ranges for all dirty/loaded rooms (for write conflict
137 // analysis). Returns pairs of (start_pc, end_pc) covering header and object
138 // regions that would be written during save.
139 std::vector<std::pair<uint32_t, uint32_t>> CollectWriteRanges() const;
140
141 // ROM management
142 void SetRom(Rom* rom) {
143 rom_ = rom;
146
147 // Propagate ROM to all rooms
148 if (rom) {
150 }
151
152 // Reset viewers on ROM change
153 room_viewers_.Clear();
154 }
155 Rom* rom() const { return rom_; }
156
157 // Room management
158 void add_room(int room_id);
159 void FocusRoom(int room_id);
160
161 // Agent/Automation controls
162 void SelectObject(int obj_id);
163 void SetAgentMode(bool enabled);
164
165 // ROM state
166 bool IsRomLoaded() const override { return rom_ && rom_->is_loaded(); }
167 std::string GetRomStatus() const override {
168 if (!rom_)
169 return "No ROM loaded";
170 if (!rom_->is_loaded())
171 return "ROM failed to load";
172 return absl::StrFormat("ROM loaded: %s", rom_->title());
173 }
174
175 // Open a workspace window by its id using WorkspaceWindowManager.
176 void OpenWindow(const std::string& window_id) {
179 }
180 }
181
182 // Explicit workflow toggle between integrated Workbench and standalone panels.
183 void SetWorkbenchWorkflowMode(bool enabled, bool show_toast = true);
184 // Queue a workflow mode change to run at a safe point in the next update.
185 void QueueWorkbenchWorkflowMode(bool enabled, bool show_toast = true);
186 // Queue a mode flip (Workbench <-> Standalone) for next update.
187 void ToggleWorkbenchWorkflowMode(bool show_toast = true);
188 bool IsWorkbenchWorkflowEnabled() const;
189
190 // Panel card IDs for programmatic access
191 static constexpr const char* kRoomSelectorId = "dungeon.room_selector";
192 static constexpr const char* kEntranceListId = "dungeon.entrance_list";
193 static constexpr const char* kRoomMatrixId = "dungeon.room_matrix";
194 static constexpr const char* kRoomGraphicsId = "dungeon.room_graphics";
195 static constexpr const char* kObjectSelectorId = "dungeon.object_selector";
196 static constexpr const char* kObjectEditorId = "dungeon.object_editor";
197 static constexpr const char* kObjectToolsId = kObjectSelectorId;
198 static constexpr const char* kDoorEditorId = "dungeon.door_editor";
199 static constexpr const char* kPaletteEditorId = "dungeon.palette_editor";
200
201 // Public accessors for WASM API and automation
204 const ImVector<int>& active_rooms() const {
206 }
208 const DungeonRoomStore& rooms() const { return rooms_; }
209 gfx::IRenderer* renderer() const { return renderer_; }
220
225 const std::deque<int>& GetRecentRooms() const { return recent_rooms_; }
226
227 private:
230 friend class
232 friend class
234
236
237 // Draw the Room Panels
238 void DrawRoomPanels();
239 void DrawRoomTab(int room_id);
240
241 // Texture processing (critical for rendering)
243
244 // Room selection callback
245 void OnRoomSelected(int room_id, bool request_focus = true);
246 void OnRoomSelected(int room_id, RoomSelectionIntent intent);
247 void OnEntranceSelected(int entrance_id);
248
249 // Sync all sub-panels to the current room configuration
250 void SyncPanelsToRoom(int room_id);
251
252 // Show or create a standalone room panel
253 void ShowRoomPanel(int room_id);
254
255 // Convenience action for Settings panel.
256 void SaveAllRooms();
257
258 // Object placement callback
259 void HandleObjectPlaced(const zelda3::RoomObject& obj);
260 void OpenGraphicsEditorForObject(int room_id,
261 const zelda3::RoomObject& object);
262
263 // Helper to get or create a viewer for a specific room
267 void TouchViewerLru(int room_id);
268 void RemoveViewerFromLru(int room_id);
269
270 absl::Status SaveRoomData(int room_id);
271
272 // Data
276 std::array<zelda3::RoomEntrance, 0x8C> entrances_;
277
278 // Current selection state
280
281 // Active room tabs and card tracking for jump-to
282 ImVector<int> active_rooms_;
284
285 // Recent rooms history for quick navigation (most recent first, max 10)
286 static constexpr size_t kMaxRecentRooms = 10;
287 std::deque<int> recent_rooms_;
288 std::vector<int> pinned_rooms_;
289
290 // Workbench panel pointer (owned by WorkspaceWindowManager, stored for notifications).
292
293 // Palette management
298
299 // Components - these do all the work
302 static constexpr int kMaxCachedViewers = 20;
305 std::unique_ptr<DungeonCanvasViewer> workbench_viewer_;
306 std::unique_ptr<DungeonCanvasViewer> workbench_compare_viewer_;
307
309 // Panel pointers - these are owned by WorkspaceWindowManager when available.
310 // Store pointers for direct access to panel methods.
324
325 // Fallback ownership for tests when WorkspaceWindowManager is not available.
326 // In production, this remains nullptr and panels are owned by WorkspaceWindowManager.
327 std::unique_ptr<ObjectSelectorContent> owned_object_selector_panel_;
328 std::unique_ptr<ObjectEditorContent> owned_object_editor_content_;
329 std::unique_ptr<DoorEditorContent> owned_door_editor_panel_;
330 std::unique_ptr<zelda3::DungeonEditorSystem> dungeon_editor_system_;
331 std::unique_ptr<emu::render::EmulatorRenderService> render_service_;
332
333 bool is_loaded_ = false;
334
335 // Docking class for room windows to dock together
336 ImGuiWindowClass room_window_class_;
337
338 // Shared dock ID for all room panels to auto-dock together
339 ImGuiID room_dock_id_ = 0;
340
341 // Dynamic room cards - created per open room
342 std::unordered_map<int, std::shared_ptr<gui::PanelWindow>> room_cards_;
343
344 // Stable window slot mapping: room_id -> slot_id.
345 // Slot IDs are used in the "###" part of the window title so ImGui treats the
346 // window as the same entity even when its displayed room changes.
348 std::unordered_map<int, int> room_panel_slot_ids_;
349
350 // Pending undo snapshot: captured on mutation callback (before edit),
351 // finalized on cache invalidation callback (after edit) by pushing an undo
352 // action to the inherited undo_manager_.
353 struct PendingUndo {
354 int room_id = -1;
355 std::vector<zelda3::RoomObject> before_objects;
356 };
358 bool has_pending_undo_ = false;
359
365
371
372 // Pending room swap (deferred until after draw phase completes)
373 struct PendingSwap {
374 int old_room_id = -1;
375 int new_room_id = -1;
376 bool pending = false;
377 };
379
381 bool enabled = false;
382 bool show_toast = true;
383 bool pending = false;
384 };
386
387 // Two-phase undo capture: BeginUndoSnapshot saves state before mutation,
388 // FinalizeUndoAction captures state after mutation and pushes the action.
389 void BeginUndoSnapshot(int room_id);
390 void FinalizeUndoAction(int room_id);
391 void RestoreRoomObjects(int room_id,
392 const std::vector<zelda3::RoomObject>& objects);
393
394 void BeginCollisionUndoSnapshot(int room_id);
395 void FinalizeCollisionUndoAction(int room_id);
396 void RestoreRoomCustomCollision(int room_id,
397 const zelda3::CustomCollisionMap& map);
398
399 void BeginWaterFillUndoSnapshot(int room_id);
400 void FinalizeWaterFillUndoAction(int room_id);
401 void RestoreRoomWaterFill(int room_id, const WaterFillSnapshot& snap);
402 void SwapRoomInPanel(int old_room_id, int new_room_id);
403 void ProcessPendingSwap(); // Process deferred swap after draw
405
406 // Room panel slot IDs provide stable ImGui window IDs across "swap room in
407 // panel" navigation. This keeps the window position/dock state when the room
408 // changes via the directional arrows.
409 int GetOrCreateRoomPanelSlotId(int room_id);
410 void ReleaseRoomPanelSlotId(int room_id);
411
412 // Defensive guard: returns true iff room_id is within the valid range
413 // [0, kNumberOfRooms). Use this instead of open-coded range checks.
414 static bool IsValidRoomId(int room_id) {
415 return room_id >= 0 && room_id < zelda3::kNumberOfRooms;
416 }
417};
418
419} // namespace editor
420} // namespace yaze
421
422#endif // YAZE_APP_EDITOR_DUNGEON_EDITOR_V2_H
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Definition rom.h:28
bool is_loaded() const
Definition rom.h:132
auto title() const
Definition rom.h:137
DungeonEditorV2 - Simplified dungeon editor using component delegation.
void BeginCollisionUndoSnapshot(int room_id)
class MinecartTrackEditorPanel * minecart_track_editor_panel_
absl::Status SaveRoom(int room_id)
void RestoreRoomObjects(int room_id, const std::vector< zelda3::RoomObject > &objects)
class CustomCollisionPanel * custom_collision_panel_
void ToggleWorkbenchWorkflowMode(bool show_toast=true)
void RestoreRoomWaterFill(int room_id, const WaterFillSnapshot &snap)
void ContributeStatus(StatusBar *status_bar) override
void OpenGraphicsEditorForObject(int room_id, const zelda3::RoomObject &object)
class ItemEditorPanel * item_editor_panel_
absl::Status SaveRoomData(int room_id)
std::array< zelda3::RoomEntrance, 0x8C > entrances_
ObjectSelectorContent * object_selector_panel() const
util::LruCache< int, std::unique_ptr< DungeonCanvasViewer > > room_viewers_
gfx::PaletteGroup current_palette_group_
void OnEntranceSelected(int entrance_id)
static constexpr const char * kEntranceListId
class SpriteEditorPanel * sprite_editor_panel_
void HandleObjectPlaced(const zelda3::RoomObject &obj)
class DungeonSettingsPanel * dungeon_settings_panel_
std::string GetRomStatus() const override
DoorEditorContent * door_editor_panel() const
std::unique_ptr< zelda3::DungeonEditorSystem > dungeon_editor_system_
void OpenWindow(const std::string &window_id)
void FinalizeWaterFillUndoAction(int room_id)
DoorEditorContent * door_editor_panel_
std::unordered_map< int, int > room_panel_slot_ids_
friend class DungeonEditorV2RomSafetyTest_UndoSnapshotLeakDetection_Test
class DungeonWorkbenchContent * workbench_panel_
friend class DungeonEditorV2RomSafetyTest_ViewerCacheLRUEviction_Test
void OnRoomSelected(int room_id, bool request_focus=true)
friend class DungeonEditorV2RomSafetyTest_ViewerCacheNeverEvictsActiveRooms_Test
ObjectEditorContent * object_editor_content() const
PendingCollisionUndo pending_collision_undo_
ObjectTileEditorPanel * object_tile_editor_panel_
ObjectSelectorContent * object_selector_panel_
OverlayManagerPanel * overlay_manager_panel_
gfx::IRenderer * renderer() const
std::unique_ptr< DungeonCanvasViewer > workbench_compare_viewer_
PendingWaterFillUndo pending_water_fill_undo_
bool IsRomLoaded() const override
gui::PaletteEditorWidget palette_editor_
std::unique_ptr< ObjectEditorContent > owned_object_editor_content_
std::unique_ptr< DoorEditorContent > owned_door_editor_panel_
const std::deque< int > & GetRecentRooms() const
Get the list of recently visited room IDs.
static constexpr const char * kObjectEditorId
static bool IsValidRoomId(int room_id)
void SwapRoomInPanel(int old_room_id, int new_room_id)
RoomGraphicsContent * room_graphics_panel_
const DungeonRoomStore & rooms() const
static constexpr int kMaxCachedViewers
static constexpr size_t kMaxRecentRooms
void SetWorkbenchWorkflowMode(bool enabled, bool show_toast=true)
class RoomTagEditorPanel * room_tag_editor_panel_
std::unique_ptr< ObjectSelectorContent > owned_object_selector_panel_
friend class DungeonEditorV2RomSafetyTest_ViewerCacheLRUAccessOrderUpdate_Test
static constexpr const char * kDoorEditorId
static constexpr const char * kObjectToolsId
DungeonCanvasViewer * GetViewerForRoom(int room_id)
absl::Status Update() override
class WaterFillPanel * water_fill_panel_
void BeginWaterFillUndoSnapshot(int room_id)
absl::Status Find() override
ObjectSelectorContent * object_editor_panel() const
std::unique_ptr< emu::render::EmulatorRenderService > render_service_
DungeonCanvasViewer * GetWorkbenchViewer()
std::unordered_map< int, std::shared_ptr< gui::PanelWindow > > room_cards_
void SetGameData(zelda3::GameData *game_data) override
static constexpr const char * kObjectSelectorId
static constexpr const char * kRoomGraphicsId
void QueueWorkbenchWorkflowMode(bool enabled, bool show_toast=true)
ObjectEditorContent * object_editor_content_
static constexpr const char * kRoomMatrixId
static constexpr const char * kRoomSelectorId
const ImVector< int > & active_rooms() const
std::unique_ptr< DungeonCanvasViewer > workbench_viewer_
void RestoreRoomCustomCollision(int room_id, const zelda3::CustomCollisionMap &map)
std::vector< std::pair< uint32_t, uint32_t > > CollectWriteRanges() const
void FinalizeCollisionUndoAction(int room_id)
static constexpr const char * kPaletteEditorId
PendingWorkflowMode pending_workflow_mode_
DungeonCanvasViewer * GetWorkbenchCompareViewer()
Manages loading and saving of dungeon room data.
void SetGameData(zelda3::GameData *game_data)
Handles room and entrance selection UI.
const ImVector< int > & active_rooms() const
void SetGameData(zelda3::GameData *game_data)
Interface for editor classes.
Definition editor.h:240
zelda3::GameData * game_data() const
Definition editor.h:307
EditorDependencies dependencies_
Definition editor.h:316
EditorType type_
Definition editor.h:315
WindowContent for placing and managing dungeon pot items.
Browse and place dungeon objects.
void SetGameData(zelda3::GameData *game_data)
Panel for editing the tile8 composition of dungeon objects.
WindowContent for displaying room graphics blocks.
WindowContent showing all room tag slots and their usage across rooms.
WindowContent for placing and managing dungeon sprites.
A session-aware status bar displayed at the bottom of the application.
Definition status_bar.h:54
bool OpenWindow(size_t session_id, const std::string &base_window_id)
Defines an abstract interface for all rendering operations.
Definition irenderer.h:60
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
RoomSelectionIntent
Intent for room selection in the dungeon editor.
constexpr int kNumberOfRooms
std::unique_ptr< DungeonEditorSystem > CreateDungeonEditorSystem(Rom *rom, GameData *game_data)
Factory function to create dungeon editor system.
std::vector< zelda3::RoomObject > before_objects
zelda3::GameData * game_data
Definition editor.h:167
WorkspaceWindowManager * window_manager
Definition editor.h:176
Represents a group of palettes.