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 <functional>
8#include <memory>
9#include <optional>
10#include <string>
11#include <unordered_map>
12#include <utility>
13#include <vector>
14
15#include "absl/status/status.h"
16#include "absl/strings/str_format.h"
17#include "app/editor/editor.h"
25#include "dungeon_room_loader.h"
27#include "dungeon_room_store.h"
29#include "imgui/imgui.h"
32#include "rom/rom.h"
34#include "util/lru_cache.h"
37#include "zelda3/dungeon/room.h"
40#include "zelda3/game_data.h"
41
42namespace yaze {
43namespace editor {
44
45class CustomCollisionPanel;
46class DungeonEditorV2RegularEntranceTestPeer;
47class DungeonEditorV2SpawnPointTestPeer;
48class DungeonEditorV2SpawnRejectionTestPeer;
49class MinecartTrackEditorPanel;
50class ObjectTileEditorPanel;
51class OverlayManagerPanel;
52class PaletteEditorContent;
53class RoomTagEditorPanel;
54class WaterFillPanel;
55
96class DungeonEditorV2 : public Editor {
97 public:
98 explicit DungeonEditorV2(Rom* rom = nullptr);
99
100 ~DungeonEditorV2() override;
101
104 dependencies_.game_data = game_data; // Also set base class dependency
108 }
110 dungeon_editor_system_->SetGameData(game_data);
111 }
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 if (workbench_viewer_) {
121 workbench_viewer_->SetGameData(game_data);
122 }
125 }
126 }
127
128 // Editor interface
129 void Initialize() override;
130 absl::Status Load() override;
131 absl::Status Update() override;
132 absl::Status Undo() override;
133 absl::Status Redo() override;
134 absl::Status Cut() override;
135 absl::Status Copy() override;
136 absl::Status Paste() override;
137 absl::Status Find() override { return absl::UnimplementedError("Find"); }
138 absl::Status Save() override;
139 absl::Status BeginSaveTransaction() override;
140 void RollbackSaveTransaction() override;
141 void CommitSaveTransaction() override;
142 void ContributeStatus(StatusBar* status_bar) override;
143 absl::Status SaveRoom(int room_id);
144 int LoadedRoomCount() const;
145 int PendingRoomCount() const;
146 bool HasPendingRoomChanges() const;
147 bool CurrentRoomHasPendingChanges() const;
148 int TotalRoomCount() const { return static_cast<int>(rooms_.size()); }
149
150 // Collect PC write ranges for all dirty/loaded rooms (for write conflict
151 // analysis). Returns pairs of (start_pc, end_pc) covering header and object
152 // regions that would be written during save.
153 std::vector<std::pair<uint32_t, uint32_t>> CollectWriteRanges() const;
154
155 // ROM management
156 void SetRom(Rom* rom) {
157 const bool rom_changed = rom_ != rom;
158 rom_ = rom;
162
163 // Propagate ROM to all rooms
165 // Reset viewers on ROM change
166 if (rom_changed) {
167 rooms_.Clear();
168 room_viewers_.Clear();
169 workbench_viewer_.reset();
171 }
172 }
173 Rom* rom() const { return rom_; }
174
175 // Room management
176 void add_room(int room_id);
177 void FocusRoom(int room_id);
178
179 // Agent/Automation controls
180 void SelectObject(int obj_id);
181 void SetAgentMode(bool enabled);
182
183 // ROM state
184 bool IsRomLoaded() const override { return rom_ && rom_->is_loaded(); }
185 std::string GetRomStatus() const override {
186 if (!rom_)
187 return "No ROM loaded";
188 if (!rom_->is_loaded())
189 return "ROM failed to load";
190 return absl::StrFormat("ROM loaded: %s", rom_->title());
191 }
192
193 // Open a workspace window by its id using WorkspaceWindowManager.
194 void OpenWindow(const std::string& window_id) {
197 }
198 }
199
200 // Explicit workflow toggle between integrated Workbench and standalone panels.
201 void SetWorkbenchWorkflowMode(bool enabled, bool show_toast = true);
202 // Queue a workflow mode change to run at a safe point in the next update.
203 void QueueWorkbenchWorkflowMode(bool enabled, bool show_toast = true);
204 // Queue a mode flip (Workbench <-> Standalone) for next update.
205 void ToggleWorkbenchWorkflowMode(bool show_toast = true);
206 bool IsWorkbenchWorkflowEnabled() const;
207
208 // Panel card IDs for programmatic access
209 static constexpr const char* kRoomSelectorId = "dungeon.room_selector";
210 static constexpr const char* kEntranceListId = "dungeon.entrance_list";
211 static constexpr const char* kRoomMatrixId = "dungeon.room_matrix";
212 static constexpr const char* kRoomGraphicsId = "dungeon.room_graphics";
213 static constexpr const char* kObjectSelectorId = "dungeon.object_selector";
214 static constexpr const char* kObjectToolsId = kObjectSelectorId;
215 static constexpr const char* kDoorEditorId = "dungeon.door_editor";
216 static constexpr const char* kPaletteEditorId = "dungeon.palette_editor";
217
218 // Public accessors for WASM API and automation
221 const ImVector<int>& active_rooms() const {
223 }
225 const DungeonRoomStore& rooms() const { return rooms_; }
226 gfx::IRenderer* renderer() const { return renderer_; }
237
242 const std::deque<int>& GetRecentRooms() const { return recent_rooms_; }
243
244 private:
249 friend class
252 friend class
254 friend class
256 friend class
258 friend class
260 friend class
262 friend class
264 friend class
266 friend class
268
270
271 // Draw the Room Panels
272 void DrawRoomPanels();
273 void DrawRoomTab(int room_id);
274
275 // Texture processing (critical for rendering)
277
278 // Room selection callback
279 void OnRoomSelected(int room_id, bool request_focus = true);
280 void OnRoomSelected(int room_id, RoomSelectionIntent intent);
281 void OnEntranceSelected(int entrance_id);
282
283 // Sync all sub-panels to the current room configuration
284 void SyncPanelsToRoom(int room_id);
286 uint8_t ResolveSelectedEntranceBlocksetForRoom(int room_id) const;
287 void ApplyEntranceRenderContext(int room_id);
288 void ConfigureViewerRenderContext(DungeonCanvasViewer* viewer, int room_id);
290
291 // Show or create a standalone room panel
292 void ShowRoomPanel(int room_id);
293
294 // Convenience action for Settings panel.
295 void SaveAllRooms();
296
297 // Object placement callback
298 void HandleObjectPlaced(const zelda3::RoomObject& obj);
299 void OpenGraphicsEditorForObject(int room_id,
300 const zelda3::RoomObject& object);
301
302 // Helper to get or create a viewer for a specific room
307 int room_id);
308 void TouchViewerLru(int room_id);
309 void RemoveViewerFromLru(int room_id);
310
311 absl::Status SaveRoomData(int room_id);
312 absl::Status RunWithSaveTransaction(
313 const std::function<absl::Status()>& operation);
314
315 // Data
319 std::array<zelda3::RoomEntrance, zelda3::kNumDungeonEntranceSlots> entrances_;
320 std::array<zelda3::DungeonSpawnPoint, zelda3::kNumDungeonSpawnPoints>
322
324 std::vector<std::pair<int, zelda3::Room::SaveDirtySnapshot>> room_states;
325 std::array<bool, zelda3::kNumDungeonEntranceSlots> entrance_dirty_states{};
326 std::array<bool, zelda3::kNumDungeonSpawnPoints> spawn_dirty_states{};
328 bool pit_damage_dirty = false;
330 };
331 std::optional<SaveTransactionSnapshot> save_transaction_snapshot_;
332
333 // Current selection state
335
336 // Active room tabs and card tracking for jump-to
337 ImVector<int> active_rooms_;
339
340 // Recent rooms history for quick navigation (most recent first, max 10)
341 static constexpr size_t kMaxRecentRooms = 10;
342 std::deque<int> recent_rooms_;
343 std::vector<int> pinned_rooms_;
344
345 // Workbench panel pointer (owned by WorkspaceWindowManager, stored for notifications).
347
348 // Palette management
353
354 // Components - these do all the work
357 static constexpr int kMaxCachedViewers = 20;
360 std::unique_ptr<DungeonCanvasViewer> workbench_viewer_;
361 std::unique_ptr<DungeonCanvasViewer> workbench_compare_viewer_;
362
364 // Panel pointers. WorkspaceWindowManager owns these when available; fallback
365 // unique_ptrs keep non-workspace tests and direct embedding paths alive.
366 // Workbench mode embeds room-local utilities in the inspector drawer and
367 // closes/hides their standalone window entries.
381
382 // Object editor is retained as the non-window backend for inspector actions.
383 // Other owned_* fields are fallbacks for tests without WorkspaceWindowManager.
384 std::unique_ptr<ObjectSelectorContent> owned_object_selector_panel_;
385 std::unique_ptr<ObjectEditorContent> owned_object_editor_content_;
386 std::unique_ptr<DoorEditorContent> owned_door_editor_panel_;
387 std::unique_ptr<RoomTagEditorPanel> owned_room_tag_editor_panel_;
388 std::unique_ptr<CustomCollisionPanel> owned_custom_collision_panel_;
389 std::unique_ptr<WaterFillPanel> owned_water_fill_panel_;
390 std::unique_ptr<MinecartTrackEditorPanel> owned_minecart_track_editor_panel_;
391 std::unique_ptr<zelda3::DungeonEditorSystem> dungeon_editor_system_;
392 std::unique_ptr<emu::render::EmulatorRenderService> render_service_;
393
394 bool is_loaded_ = false;
395
396 // Docking class for room windows to dock together
397 ImGuiWindowClass room_window_class_;
398
399 // Shared dock ID for all room panels to auto-dock together
400 ImGuiID room_dock_id_ = 0;
401
402 // Dynamic room cards - created per open room
403 std::unordered_map<int, std::shared_ptr<gui::PanelWindow>> room_cards_;
404
405 // Stable window slot mapping: room_id -> slot_id.
406 // Slot IDs are used in the "###" part of the window title so ImGui treats the
407 // window as the same entity even when its displayed room changes.
409 std::unordered_map<int, int> room_panel_slot_ids_;
410
411 // Pending undo snapshot: captured on mutation callback (before edit),
412 // finalized on cache invalidation callback (after edit) by pushing an undo
413 // action to the inherited undo_manager_.
414 struct PendingUndo {
415 int room_id = -1;
416 std::vector<zelda3::RoomObject> before_objects;
417 std::vector<size_t> before_selection;
418 };
420 bool has_pending_undo_ = false;
422
428
434
435 // Pending room swap (deferred until after draw phase completes)
436 struct PendingSwap {
437 int old_room_id = -1;
438 int new_room_id = -1;
439 bool pending = false;
440 };
442
444 bool enabled = false;
445 bool show_toast = true;
446 bool pending = false;
447 };
449
450 // Two-phase undo capture: BeginUndoSnapshot saves state before mutation,
451 // FinalizeUndoAction captures state after mutation and pushes the action.
452 void BeginUndoSnapshot(int room_id);
453 void FinalizeUndoAction(int room_id);
454 void RestoreRoomObjects(int room_id,
455 const std::vector<zelda3::RoomObject>& objects,
456 const std::vector<size_t>& selected_indices);
457
458 void BeginCollisionUndoSnapshot(int room_id);
459 void FinalizeCollisionUndoAction(int room_id);
460 void RestoreRoomCustomCollision(int room_id,
461 const zelda3::CustomCollisionMap& map);
462
463 void BeginWaterFillUndoSnapshot(int room_id);
464 void FinalizeWaterFillUndoAction(int room_id);
465 void RestoreRoomWaterFill(int room_id, const WaterFillSnapshot& snap);
466 void SwapRoomInPanel(int old_room_id, int new_room_id);
467 void ProcessPendingSwap(); // Process deferred swap after draw
469
470 // Room panel slot IDs provide stable ImGui window IDs across "swap room in
471 // panel" navigation. This keeps the window position/dock state when the room
472 // changes via the directional arrows.
473 int GetOrCreateRoomPanelSlotId(int room_id);
474 void ReleaseRoomPanelSlotId(int room_id);
475
476 // Defensive guard: returns true iff room_id is within the valid range
477 // [0, kNumberOfRooms). Use this instead of open-coded range checks.
478 static bool IsValidRoomId(int room_id) {
479 return room_id >= 0 && room_id < zelda3::kNumberOfRooms;
480 }
481};
482
483} // namespace editor
484} // namespace yaze
485
486#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:144
auto title() const
Definition rom.h:149
DungeonEditorV2 - Simplified dungeon editor using component delegation.
class MinecartTrackEditorPanel * minecart_track_editor_panel_
friend class DungeonEditorV2SpawnRejectionTestPeer
std::unique_ptr< CustomCollisionPanel > owned_custom_collision_panel_
class CustomCollisionPanel * custom_collision_panel_
friend class DungeonEditorV2RegularEntranceTestPeer
friend class DungeonEditorV2RomSafetyTest_SaveAllRoomsRollsBackEarlierWritesOnLateFailure_Test
friend class DungeonEditorPaletteRefreshTest_CachedRoomRefreshesThroughViewerCompositePreparation_Test
void ToggleWorkbenchWorkflowMode(bool show_toast=true)
friend class DungeonEditorPaletteRefreshTest_SharedHudEditRefreshesRoomUsingDifferentDungeonPalette_Test
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_
std::unique_ptr< RoomTagEditorPanel > owned_room_tag_editor_panel_
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)
std::string GetRomStatus() const override
DoorEditorContent * door_editor_panel() const
std::unique_ptr< zelda3::DungeonEditorSystem > dungeon_editor_system_
std::array< zelda3::RoomEntrance, zelda3::kNumDungeonEntranceSlots > entrances_
void OpenWindow(const std::string &window_id)
DoorEditorContent * door_editor_panel_
std::unordered_map< int, int > room_panel_slot_ids_
friend class DungeonEditorV2RomSafetyTest_UndoSnapshotLeakDetection_Test
void RestoreRoomObjects(int room_id, const std::vector< zelda3::RoomObject > &objects, const std::vector< size_t > &selected_indices)
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_
uint8_t ResolveSelectedEntranceBlocksetForRoom(int room_id) const
DungeonCanvasViewer * GetWorkbenchCompareViewer(int room_id)
void WireViewerPanelCallbacks(DungeonCanvasViewer *viewer)
gfx::IRenderer * renderer() const
std::unique_ptr< DungeonCanvasViewer > workbench_compare_viewer_
PendingWaterFillUndo pending_water_fill_undo_
std::unique_ptr< MinecartTrackEditorPanel > owned_minecart_track_editor_panel_
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 bool IsValidRoomId(int room_id)
void SwapRoomInPanel(int old_room_id, int new_room_id)
RoomGraphicsContent * room_graphics_panel_
absl::Status RunWithSaveTransaction(const std::function< absl::Status()> &operation)
const DungeonRoomStore & rooms() const
static constexpr int kMaxCachedViewers
static constexpr size_t kMaxRecentRooms
std::array< zelda3::DungeonSpawnPoint, zelda3::kNumDungeonSpawnPoints > spawn_points_
void SetWorkbenchWorkflowMode(bool enabled, bool show_toast=true)
class RoomTagEditorPanel * room_tag_editor_panel_
std::optional< SaveTransactionSnapshot > save_transaction_snapshot_
std::unique_ptr< ObjectSelectorContent > owned_object_selector_panel_
friend class DungeonEditorV2RomSafetyTest_ViewerCacheLRUAccessOrderUpdate_Test
friend class DungeonEditorPaletteRefreshTest_CompareViewerScopesEntranceContextToRequestedRoom_Test
static constexpr const char * kDoorEditorId
friend class DungeonEditorV2RomSafetyTest_LateCoordinatorRollbackRestoresEntranceDirtyState_Test
static constexpr const char * kObjectToolsId
DungeonCanvasViewer * GetViewerForRoom(int room_id)
absl::Status Update() override
class WaterFillPanel * water_fill_panel_
void RefreshWorkbenchViewerRuntimeContext(DungeonCanvasViewer *viewer, int room_id)
absl::Status Find() override
friend class DungeonEditorPaletteRefreshTest_DungeonMainEditRefreshesResolvedAliasesOnly_Test
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 ApplyEntranceRenderContext(int room_id)
void SetGameData(zelda3::GameData *game_data) override
friend class DungeonEditorV2RomSafetyTest_ObjectStreamUndoRedoRestoresSelectionIdentity_Test
static constexpr const char * kObjectSelectorId
void ConfigureViewerRenderContext(DungeonCanvasViewer *viewer, int room_id)
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
PaletteEditorContent * palette_editor_panel_
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
std::unique_ptr< WaterFillPanel > owned_water_fill_panel_
static constexpr const char * kPaletteEditorId
PendingWorkflowMode pending_workflow_mode_
void InvalidateDungeonPaletteUsers(gui::DungeonPaletteChange change)
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:245
zelda3::GameData * game_data() const
Definition editor.h:320
EditorDependencies dependencies_
Definition editor.h:333
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 wrapper for PaletteEditorWidget in dungeon context.
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::vector< zelda3::RoomObject > before_objects
std::vector< std::pair< int, zelda3::Room::SaveDirtySnapshot > > room_states
std::array< bool, zelda3::kNumDungeonSpawnPoints > spawn_dirty_states
std::array< bool, zelda3::kNumDungeonEntranceSlots > entrance_dirty_states
zelda3::GameData * game_data
Definition editor.h:172
WorkspaceWindowManager * window_manager
Definition editor.h:181
Represents a group of palettes.