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 DungeonEditorPaletteRefreshTestPeer;
47class DungeonEditorV2MinecartTrackTestPeer;
48class DungeonEditorV2ObjectTileEditorTestPeer;
49class DungeonEditorV2RegularEntranceTestPeer;
50class DungeonEditorV2ReloadTestPeer;
51class DungeonEditorV2SpawnPointTestPeer;
52class DungeonEditorV2SpawnRejectionTestPeer;
53class MinecartTrackEditorPanel;
54class ObjectTileEditorPanel;
55class OverlayManagerPanel;
56class PaletteEditorContent;
57class RoomTagEditorPanel;
58class WaterFillPanel;
59
100class DungeonEditorV2 : public Editor {
101 public:
102 explicit DungeonEditorV2(Rom* rom = nullptr);
103
104 ~DungeonEditorV2() override;
105
108 dependencies_.game_data = game_data; // Also set base class dependency
112 }
114 dungeon_editor_system_->SetGameData(game_data);
115 }
117 // Note: Canvas viewer game data is set lazily in GetViewerForRoom
118 // but we should update existing viewers
119 room_viewers_.ForEach(
120 [game_data](int, std::unique_ptr<DungeonCanvasViewer>& viewer) {
121 if (viewer)
122 viewer->SetGameData(game_data);
123 });
124 if (workbench_viewer_) {
125 workbench_viewer_->SetGameData(game_data);
126 }
129 }
130 }
131
132 // Editor interface
133 void SetDependencies(const EditorDependencies& deps) override;
134 void Initialize() override;
135 absl::Status Load() override;
136 absl::Status Update() override;
137 absl::Status Undo() override;
138 absl::Status Redo() override;
139 absl::Status Cut() override;
140 absl::Status Copy() override;
141 absl::Status Paste() override;
142 absl::Status Find() override { return absl::UnimplementedError("Find"); }
143 absl::Status Save() override;
144 absl::Status BeginSaveTransaction() override;
145 void RollbackSaveTransaction() override;
146 void CommitSaveTransaction() override;
147 void ContributeStatus(StatusBar* status_bar) override;
148 absl::Status SaveRoom(int room_id);
149 int LoadedRoomCount() const;
150 // Room-specific pending state used by room counts and room-level UI.
151 int PendingRoomCount() const;
152 bool HasPendingRoomChanges() const;
153 // All dungeon save domains plus unapplied editor-local work.
154 bool HasPendingDungeonChanges() const;
156 absl::Status PrepareProjectSave();
157 bool CurrentRoomHasPendingChanges() const;
158 int TotalRoomCount() const { return static_cast<int>(rooms_.size()); }
159
160 // Collect PC write ranges for all dirty/loaded rooms (for write conflict
161 // analysis). Returns pairs of (start_pc, end_pc) covering header and object
162 // regions that would be written during save.
163 std::vector<std::pair<uint32_t, uint32_t>> CollectWriteRanges() const;
164
165 // ROM management
166 void SetRom(Rom* rom) {
167 const bool rom_changed = rom_ != rom;
168 rom_ = rom;
172
173 // Propagate ROM to all rooms
175 // Reset viewers on ROM change
176 if (rom_changed) {
177 rooms_.Clear();
178 room_viewers_.Clear();
179 workbench_viewer_.reset();
181 }
182 }
183 // Reloads same-address ROM state in place so rooms, viewers, and workspace
184 // panel bindings retain stable addresses.
185 absl::Status RefreshRomBackedState();
186 Rom* rom() const { return rom_; }
187
188 // Room management
189 void add_room(int room_id);
190 void FocusRoom(int room_id);
191
192 // Agent/Automation controls
193 void SelectObject(int obj_id);
194 void SetAgentMode(bool enabled);
195
196 // ROM state
197 bool IsRomLoaded() const override { return rom_ && rom_->is_loaded(); }
198 std::string GetRomStatus() const override {
199 if (!rom_)
200 return "No ROM loaded";
201 if (!rom_->is_loaded())
202 return "ROM failed to load";
203 return absl::StrFormat("ROM loaded: %s", rom_->title());
204 }
205
206 // Open a workspace window by its id using WorkspaceWindowManager.
207 void OpenWindow(const std::string& window_id) {
210 }
211 }
212
213 // Explicit workflow toggle between integrated Workbench and standalone panels.
214 void SetWorkbenchWorkflowMode(bool enabled, bool show_toast = true);
215 // Queue a workflow mode change to run at a safe point in the next update.
216 void QueueWorkbenchWorkflowMode(bool enabled, bool show_toast = true);
217 // Queue a mode flip (Workbench <-> Standalone) for next update.
218 void ToggleWorkbenchWorkflowMode(bool show_toast = true);
219 bool IsWorkbenchWorkflowEnabled() const;
220
221 // Panel card IDs for programmatic access
222 static constexpr const char* kRoomSelectorId = "dungeon.room_selector";
223 static constexpr const char* kEntranceListId = "dungeon.entrance_list";
224 static constexpr const char* kRoomMatrixId = "dungeon.room_matrix";
225 static constexpr const char* kRoomGraphicsId = "dungeon.room_graphics";
226 static constexpr const char* kObjectSelectorId = "dungeon.object_selector";
227 static constexpr const char* kObjectToolsId = kObjectSelectorId;
228 static constexpr const char* kDoorEditorId = "dungeon.door_editor";
229 static constexpr const char* kPaletteEditorId = "dungeon.palette_editor";
230
231 // Public accessors for WASM API and automation
234 const ImVector<int>& active_rooms() const {
236 }
238 const DungeonRoomStore& rooms() const { return rooms_; }
239 gfx::IRenderer* renderer() const { return renderer_; }
250
255 const std::deque<int>& GetRecentRooms() const { return recent_rooms_; }
256
257 private:
266 friend class
269 friend class
271 friend class
273 friend class
275 friend class
277 friend class
279 friend class
281 friend class
283 friend class
285
287
288 // Draw the Room Panels
289 void DrawRoomPanels();
290 void DrawRoomTab(int room_id);
291
292 // Texture processing (critical for rendering)
295
296 // Room selection callback
297 void OnRoomSelected(int room_id, bool request_focus = true);
298 void OnRoomSelected(int room_id, RoomSelectionIntent intent);
299 void OnEntranceSelected(int entrance_id);
300 int ResolveEntranceRoomId(int entrance_id) const;
301
302 // Sync all sub-panels to the current room configuration
303 void SyncPanelsToRoom(int room_id);
305 void HandleArenaPaletteChanged(const std::string& group_name,
306 int palette_index);
309 uint8_t ResolveSelectedEntranceBlocksetForRoom(int room_id) const;
310 void ApplyEntranceRenderContext(int room_id);
311 void ConfigureViewerRenderContext(DungeonCanvasViewer* viewer, int room_id);
314
315 // Show or create a standalone room panel
316 void ShowRoomPanel(int room_id);
317
318 // Convenience action for Settings panel.
319 void SaveAllRooms();
320
321 // Object placement callback
322 void HandleObjectPlaced(const zelda3::RoomObject& obj);
323 void OpenGraphicsEditorForObject(int room_id,
324 const zelda3::RoomObject& object);
325 absl::Status OpenObjectTileEditorForObject(int room_id,
326 const zelda3::RoomObject& object);
327
328 // Helper to get or create a viewer for a specific room
333 int room_id);
334 void TouchViewerLru(int room_id);
335 void RemoveViewerFromLru(int room_id);
336
337 absl::Status SaveRoomData(int room_id);
338 absl::Status RunWithSaveTransaction(
339 const std::function<absl::Status()>& operation);
340
341 // Data
345 std::array<zelda3::RoomEntrance, zelda3::kNumDungeonEntranceSlots> entrances_;
346 std::array<zelda3::DungeonSpawnPoint, zelda3::kNumDungeonSpawnPoints>
348
350 std::vector<std::pair<int, zelda3::Room::SaveDirtySnapshot>> room_states;
351 std::array<bool, zelda3::kNumDungeonEntranceSlots> entrance_dirty_states{};
352 std::array<bool, zelda3::kNumDungeonSpawnPoints> spawn_dirty_states{};
354 bool pit_damage_dirty = false;
356 };
357 std::optional<SaveTransactionSnapshot> save_transaction_snapshot_;
358
359 // Current selection state
361
362 // Active room tabs and card tracking for jump-to
363 ImVector<int> active_rooms_;
365
366 // Recent rooms history for quick navigation (most recent first, max 10)
367 static constexpr size_t kMaxRecentRooms = 10;
368 std::deque<int> recent_rooms_;
369 std::vector<int> pinned_rooms_;
370
371 // Workbench panel pointer (owned by WorkspaceWindowManager, stored for notifications).
373
374 // Palette management
380
381 // Components - these do all the work
384 static constexpr int kMaxCachedViewers = 20;
387 std::unique_ptr<DungeonCanvasViewer> workbench_viewer_;
388 std::unique_ptr<DungeonCanvasViewer> workbench_compare_viewer_;
389
391 // Panel pointers. WorkspaceWindowManager owns these when available; fallback
392 // unique_ptrs keep non-workspace tests and direct embedding paths alive.
393 // Workbench mode embeds room-local utilities in the inspector drawer and
394 // closes/hides their standalone window entries.
408
409 // Object editor is retained as the non-window backend for inspector actions.
410 // Other owned_* fields are fallbacks for tests without WorkspaceWindowManager.
411 std::unique_ptr<ObjectSelectorContent> owned_object_selector_panel_;
412 std::unique_ptr<ObjectEditorContent> owned_object_editor_content_;
413 std::unique_ptr<DoorEditorContent> owned_door_editor_panel_;
414 std::unique_ptr<RoomTagEditorPanel> owned_room_tag_editor_panel_;
415 std::unique_ptr<CustomCollisionPanel> owned_custom_collision_panel_;
416 std::unique_ptr<WaterFillPanel> owned_water_fill_panel_;
417 std::unique_ptr<MinecartTrackEditorPanel> owned_minecart_track_editor_panel_;
418 std::unique_ptr<zelda3::DungeonEditorSystem> dungeon_editor_system_;
419 std::unique_ptr<emu::render::EmulatorRenderService> render_service_;
420
421 bool is_loaded_ = false;
422
423 // Docking class for room windows to dock together
424 ImGuiWindowClass room_window_class_;
425
426 // Shared dock ID for all room panels to auto-dock together
427 ImGuiID room_dock_id_ = 0;
428
429 // Dynamic room cards - created per open room
430 std::unordered_map<int, std::shared_ptr<gui::PanelWindow>> room_cards_;
431
432 // Stable window slot mapping: room_id -> slot_id.
433 // Slot IDs are used in the "###" part of the window title so ImGui treats the
434 // window as the same entity even when its displayed room changes.
436 std::unordered_map<int, int> room_panel_slot_ids_;
437
438 // Pending undo snapshot: captured on mutation callback (before edit),
439 // finalized on cache invalidation callback (after edit) by pushing an undo
440 // action to the inherited undo_manager_.
441 struct PendingUndo {
442 int room_id = -1;
443 std::vector<zelda3::RoomObject> before_objects;
444 std::vector<size_t> before_selection;
445 };
447 bool has_pending_undo_ = false;
449
455
461
462 // Pending room swap (deferred until after draw phase completes)
463 struct PendingSwap {
464 int old_room_id = -1;
465 int new_room_id = -1;
466 bool pending = false;
467 };
469
471 bool enabled = false;
472 bool show_toast = true;
473 bool pending = false;
474 };
476
477 // Two-phase undo capture: BeginUndoSnapshot saves state before mutation,
478 // FinalizeUndoAction captures state after mutation and pushes the action.
479 void BeginUndoSnapshot(int room_id);
480 void FinalizeUndoAction(int room_id);
481 void RestoreRoomObjects(int room_id,
482 const std::vector<zelda3::RoomObject>& objects,
483 const std::vector<size_t>& selected_indices);
484
485 void BeginCollisionUndoSnapshot(int room_id);
486 void FinalizeCollisionUndoAction(int room_id);
487 void RestoreRoomCustomCollision(int room_id,
488 const zelda3::CustomCollisionMap& map);
489
490 void BeginWaterFillUndoSnapshot(int room_id);
491 void FinalizeWaterFillUndoAction(int room_id);
492 void RestoreRoomWaterFill(int room_id, const WaterFillSnapshot& snap);
493 void SwapRoomInPanel(int old_room_id, int new_room_id);
494 void ProcessPendingSwap(); // Process deferred swap after draw
496
497 // Room panel slot IDs provide stable ImGui window IDs across "swap room in
498 // panel" navigation. This keeps the window position/dock state when the room
499 // changes via the directional arrows.
500 int GetOrCreateRoomPanelSlotId(int room_id);
501 void ReleaseRoomPanelSlotId(int room_id);
502
503 // Defensive guard: returns true iff room_id is within the valid range
504 // [0, kNumberOfRooms). Use this instead of open-coded range checks.
505 static bool IsValidRoomId(int room_id) {
506 return room_id >= 0 && room_id < zelda3::kNumberOfRooms;
507 }
508};
509
510} // namespace editor
511} // namespace yaze
512
513#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:155
auto title() const
Definition rom.h:167
DungeonEditorV2 - Simplified dungeon editor using component delegation.
class MinecartTrackEditorPanel * minecart_track_editor_panel_
friend class DungeonEditorV2SpawnRejectionTestPeer
std::unique_ptr< CustomCollisionPanel > owned_custom_collision_panel_
friend class DungeonEditorV2MinecartTrackTestPeer
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)
absl::Status OpenObjectTileEditorForObject(int room_id, const zelda3::RoomObject &object)
class ItemEditorPanel * item_editor_panel_
std::unique_ptr< RoomTagEditorPanel > owned_room_tag_editor_panel_
void SetDependencies(const EditorDependencies &deps) override
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)
friend class DungeonEditorV2ObjectTileEditorTestPeer
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_
int ResolveEntranceRoomId(int entrance_id) const
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)
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
void HandleDungeonPaletteChanged(gui::DungeonPaletteChange change)
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)
void HandleArenaPaletteChanged(const std::string &group_name, int palette_index)
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
Unified dependency container for all editor types.
Definition editor.h:169
zelda3::GameData * game_data
Definition editor.h:172
WorkspaceWindowManager * window_manager
Definition editor.h:181
Represents a group of palettes.