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"
27#include "imgui/imgui.h"
30#include "rom/rom.h"
31#include "util/lru_cache.h"
33#include "zelda3/dungeon/room.h"
36#include "zelda3/game_data.h"
37
38namespace yaze {
39namespace editor {
40
41class MinecartTrackEditorPanel;
42class ObjectTileEditorPanel;
43class OverlayManagerPanel;
44class RoomTagEditorPanel;
45
85class DungeonEditorV2 : public Editor {
86 public:
87 explicit DungeonEditorV2(Rom* rom = nullptr)
90 if (rom) {
92 for (auto& room : rooms_) {
93 room.SetRom(rom);
94 }
95 }
96 }
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 }
110 for (auto& room : rooms_) {
111 room.SetGameData(game_data);
112 }
113 // Note: Canvas viewer game data is set lazily in GetViewerForRoom
114 // but we should update existing viewers
115 room_viewers_.ForEach(
116 [game_data](int, std::unique_ptr<DungeonCanvasViewer>& viewer) {
117 if (viewer)
118 viewer->SetGameData(game_data);
119 });
120 }
121
122 // Editor interface
123 void Initialize() override;
124 absl::Status Load() override;
125 absl::Status Update() override;
126 absl::Status Undo() override;
127 absl::Status Redo() override;
128 absl::Status Cut() override;
129 absl::Status Copy() override;
130 absl::Status Paste() override;
131 absl::Status Find() override { return absl::UnimplementedError("Find"); }
132 absl::Status Save() override;
133 absl::Status SaveRoom(int room_id);
134 int LoadedRoomCount() const;
135 int TotalRoomCount() const { return static_cast<int>(rooms_.size()); }
136
137 // Collect PC write ranges for all dirty/loaded rooms (for write conflict
138 // analysis). Returns pairs of (start_pc, end_pc) covering header and object
139 // regions that would be written during save.
140 std::vector<std::pair<uint32_t, uint32_t>> CollectWriteRanges() const;
141
142 // ROM management
143 void SetRom(Rom* rom) {
144 rom_ = rom;
147
148 // Propagate ROM to all rooms
149 if (rom) {
150 for (auto& room : rooms_) {
151 room.SetRom(rom);
152 }
153 }
154
155 // Reset viewers on ROM change
156 room_viewers_.Clear();
157
158 // Create render service if needed
159 if (rom && rom->is_loaded() && !render_service_) {
161 std::make_unique<emu::render::EmulatorRenderService>(rom);
162 render_service_->Initialize();
163 }
164 }
165 Rom* rom() const { return rom_; }
166
167 // Room management
168 void add_room(int room_id);
169 void FocusRoom(int room_id);
170
171 // Agent/Automation controls
172 void SelectObject(int obj_id);
173 void SetAgentMode(bool enabled);
174
175 // ROM state
176 bool IsRomLoaded() const override { return rom_ && rom_->is_loaded(); }
177 std::string GetRomStatus() const override {
178 if (!rom_)
179 return "No ROM loaded";
180 if (!rom_->is_loaded())
181 return "ROM failed to load";
182 return absl::StrFormat("ROM loaded: %s", rom_->title());
183 }
184
185 // Show a panel by its card_id using PanelManager
186 void ShowPanel(const std::string& card_id) {
189 }
190 }
191
192 // Explicit workflow toggle between integrated Workbench and standalone panels.
193 void SetWorkbenchWorkflowMode(bool enabled, bool show_toast = true);
194 // Queue a workflow mode change to run at a safe point in the next update.
195 void QueueWorkbenchWorkflowMode(bool enabled, bool show_toast = true);
196 bool IsWorkbenchWorkflowEnabled() const;
197
198 // Panel card IDs for programmatic access
199 static constexpr const char* kRoomSelectorId = "dungeon.room_selector";
200 static constexpr const char* kEntranceListId = "dungeon.entrance_list";
201 static constexpr const char* kRoomMatrixId = "dungeon.room_matrix";
202 static constexpr const char* kRoomGraphicsId = "dungeon.room_graphics";
203 static constexpr const char* kObjectToolsId = "dungeon.object_editor";
204 static constexpr const char* kPaletteEditorId = "dungeon.palette_editor";
205
206 // Public accessors for WASM API and automation
209 const ImVector<int>& active_rooms() const {
211 }
212 std::array<zelda3::Room, 0x128>& rooms() { return rooms_; }
213 const std::array<zelda3::Room, 0x128>& rooms() const { return rooms_; }
214 gfx::IRenderer* renderer() const { return renderer_; }
218
223 const std::deque<int>& GetRecentRooms() const { return recent_rooms_; }
224
225 private:
228 friend class
230 friend class
232
234
235 // Draw the Room Panels
236 void DrawRoomPanels();
237 void DrawRoomTab(int room_id);
238
239 // Texture processing (critical for rendering)
241
242 // Room selection callback
243 void OnRoomSelected(int room_id, bool request_focus = true);
244 void OnRoomSelected(int room_id, RoomSelectionIntent intent);
245 void OnEntranceSelected(int entrance_id);
246
247 // Sync all sub-panels to the current room configuration
248 void SyncPanelsToRoom(int room_id);
249
250 // Show or create a standalone room panel
251 void ShowRoomPanel(int room_id);
252
253 // Convenience action for Settings panel.
254 void SaveAllRooms();
255
256 // Object placement callback
257 void HandleObjectPlaced(const zelda3::RoomObject& obj);
258 void OpenGraphicsEditorForObject(int room_id,
259 const zelda3::RoomObject& object);
260
261 // Helper to get or create a viewer for a specific room
265 void TouchViewerLru(int room_id);
266 void RemoveViewerFromLru(int room_id);
267
268 absl::Status SaveRoomData(int room_id);
269
270 // Data
273 std::array<zelda3::Room, 0x128> rooms_;
274 std::array<zelda3::RoomEntrance, 0x8C> entrances_;
275
276 // Current selection state
278
279 // Active room tabs and card tracking for jump-to
280 ImVector<int> active_rooms_;
282
283 // Recent rooms history for quick navigation (most recent first, max 10)
284 static constexpr size_t kMaxRecentRooms = 10;
285 std::deque<int> recent_rooms_;
286 std::vector<int> pinned_rooms_;
287
288 // Workbench panel pointer (owned by PanelManager, stored for notifications).
290
291 // Palette management
296
297 // Components - these do all the work
300 static constexpr int kMaxCachedViewers = 20;
303 std::unique_ptr<DungeonCanvasViewer> workbench_viewer_;
304 std::unique_ptr<DungeonCanvasViewer> workbench_compare_viewer_;
305
307 // Panel pointers - these are owned by PanelManager when available.
308 // Store pointers for direct access to panel methods.
320
321 // Fallback ownership for tests when PanelManager is not available.
322 // In production, this remains nullptr and panels are owned by PanelManager.
323 std::unique_ptr<ObjectEditorPanel> owned_object_editor_panel_;
324 std::unique_ptr<zelda3::DungeonEditorSystem> dungeon_editor_system_;
325 std::unique_ptr<emu::render::EmulatorRenderService> render_service_;
326
327 bool is_loaded_ = false;
328
329 // Docking class for room windows to dock together
330 ImGuiWindowClass room_window_class_;
331
332 // Shared dock ID for all room panels to auto-dock together
333 ImGuiID room_dock_id_ = 0;
334
335 // Dynamic room cards - created per open room
336 std::unordered_map<int, std::shared_ptr<gui::PanelWindow>> room_cards_;
337
338 // Stable window slot mapping: room_id -> slot_id.
339 // Slot IDs are used in the "###" part of the window title so ImGui treats the
340 // window as the same entity even when its displayed room changes.
342 std::unordered_map<int, int> room_panel_slot_ids_;
343
344 // Pending undo snapshot: captured on mutation callback (before edit),
345 // finalized on cache invalidation callback (after edit) by pushing an undo
346 // action to the inherited undo_manager_.
347 struct PendingUndo {
348 int room_id = -1;
349 std::vector<zelda3::RoomObject> before_objects;
350 };
352 bool has_pending_undo_ = false;
353
359
365
366 // Pending room swap (deferred until after draw phase completes)
367 struct PendingSwap {
368 int old_room_id = -1;
369 int new_room_id = -1;
370 bool pending = false;
371 };
373
375 bool enabled = false;
376 bool show_toast = true;
377 bool pending = false;
378 };
380
381 // Two-phase undo capture: BeginUndoSnapshot saves state before mutation,
382 // FinalizeUndoAction captures state after mutation and pushes the action.
383 void BeginUndoSnapshot(int room_id);
384 void FinalizeUndoAction(int room_id);
385 void RestoreRoomObjects(int room_id,
386 const std::vector<zelda3::RoomObject>& objects);
387
388 void BeginCollisionUndoSnapshot(int room_id);
389 void FinalizeCollisionUndoAction(int room_id);
390 void RestoreRoomCustomCollision(int room_id,
391 const zelda3::CustomCollisionMap& map);
392
393 void BeginWaterFillUndoSnapshot(int room_id);
394 void FinalizeWaterFillUndoAction(int room_id);
395 void RestoreRoomWaterFill(int room_id, const WaterFillSnapshot& snap);
396 void SwapRoomInPanel(int old_room_id, int new_room_id);
397 void ProcessPendingSwap(); // Process deferred swap after draw
399
400 // Room panel slot IDs provide stable ImGui window IDs across "swap room in
401 // panel" navigation. This keeps the window position/dock state when the room
402 // changes via the directional arrows.
403 int GetOrCreateRoomPanelSlotId(int room_id);
404 void ReleaseRoomPanelSlotId(int room_id);
405
406 // Defensive guard: returns true iff room_id is within the valid range
407 // [0, kNumberOfRooms). Use this instead of open-coded range checks.
408 static bool IsValidRoomId(int room_id) {
409 return room_id >= 0 && room_id < zelda3::kNumberOfRooms;
410 }
411};
412
413} // namespace editor
414} // namespace yaze
415
416#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)
std::array< zelda3::Room, 0x128 > rooms_
void RestoreRoomObjects(int room_id, const std::vector< zelda3::RoomObject > &objects)
class CustomCollisionPanel * custom_collision_panel_
void RestoreRoomWaterFill(int room_id, const WaterFillSnapshot &snap)
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_
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::unique_ptr< ObjectEditorPanel > owned_object_editor_panel_
std::string GetRomStatus() const override
std::unique_ptr< zelda3::DungeonEditorSystem > dungeon_editor_system_
class DungeonWorkbenchPanel * workbench_panel_
void FinalizeWaterFillUndoAction(int room_id)
std::unordered_map< int, int > room_panel_slot_ids_
friend class DungeonEditorV2RomSafetyTest_UndoSnapshotLeakDetection_Test
friend class DungeonEditorV2RomSafetyTest_ViewerCacheLRUEviction_Test
void OnRoomSelected(int room_id, bool request_focus=true)
friend class DungeonEditorV2RomSafetyTest_ViewerCacheNeverEvictsActiveRooms_Test
PendingCollisionUndo pending_collision_undo_
DungeonRoomGraphicsPanel * room_graphics_panel_
ObjectTileEditorPanel * object_tile_editor_panel_
OverlayManagerPanel * overlay_manager_panel_
ObjectEditorPanel * object_editor_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_
void ShowPanel(const std::string &card_id)
const std::deque< int > & GetRecentRooms() const
Get the list of recently visited room IDs.
static bool IsValidRoomId(int room_id)
void SwapRoomInPanel(int old_room_id, int new_room_id)
static constexpr int kMaxCachedViewers
static constexpr size_t kMaxRecentRooms
void SetWorkbenchWorkflowMode(bool enabled, bool show_toast=true)
class RoomTagEditorPanel * room_tag_editor_panel_
friend class DungeonEditorV2RomSafetyTest_ViewerCacheLRUAccessOrderUpdate_Test
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
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 * kRoomGraphicsId
void QueueWorkbenchWorkflowMode(bool enabled, bool show_toast=true)
static constexpr const char * kRoomMatrixId
ObjectEditorPanel * object_editor_panel() const
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)
const std::array< zelda3::Room, 0x128 > & rooms() const
std::array< zelda3::Room, 0x128 > & rooms()
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()
EditorPanel for displaying room graphics blocks.
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
Interface for editor classes.
Definition editor.h:236
zelda3::GameData * game_data() const
Definition editor.h:297
EditorDependencies dependencies_
Definition editor.h:306
EditorType type_
Definition editor.h:305
EditorPanel for placing and managing dungeon pot items.
Unified panel for dungeon object editing.
void SetGameData(zelda3::GameData *game_data)
Panel for editing the tile8 composition of dungeon objects.
bool ShowPanel(size_t session_id, const std::string &base_card_id)
EditorPanel showing all room tag slots and their usage across rooms.
EditorPanel for placing and managing dungeon sprites.
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:166
Represents a group of palettes.