yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
dungeon_canvas_viewer.h
Go to the documentation of this file.
1#ifndef YAZE_APP_EDITOR_DUNGEON_DUNGEON_CANVAS_VIEWER_H
2#define YAZE_APP_EDITOR_DUNGEON_DUNGEON_CANVAS_VIEWER_H
3
4#include <array>
5#include <cstdint>
6#include <functional>
7#include <map>
8#include <optional>
9#include <string>
10#include <unordered_map>
11#include <vector>
12
13#include "app/editor/editor.h"
18#include "core/project.h"
21#include "dungeon_room_store.h"
22#include "imgui/imgui.h"
23#include "rom/rom.h"
25#include "zelda3/dungeon/room.h"
27#include "zelda3/game_data.h"
28
29namespace yaze {
30namespace editor {
31
32class MinecartTrackEditorPanel;
33class DungeonCanvasViewerTestPeer;
34
35enum class DungeonConnectedLinkType : uint8_t {
36 Door,
39};
40
42 int from_room_id = -1;
43 int to_room_id = -1;
46 // Provenance fields populated by CollectDungeonConnectedRoomLinkDiagnostics
47 // for hover tooltips and issue reports. Defaults represent "not applicable".
48 int slot_index = -1; // Staircase slot 0..3, -1 otherwise.
49 int16_t object_id = -1; // Placed staircase tile-object id, -1 otherwise.
50 int door_index = -1; // Index in room.GetDoors() for Door links.
52};
53
54// Discriminator for staircase configuration anomalies surfaced through
55// CollectDungeonConnectedRoomLinkDiagnostics. Each kind explains a different
56// failure mode the runtime silently ignores.
57enum class DungeonStaircaseIssueKind : uint8_t {
58 // Header destination is non-zero but no placed interroom-stair object
59 // consumes that slot. Runtime never traverses the link.
61 // Placed interroom-stair object exists but the matched header slot is 0
62 // (unset) or out of range — the runtime would jump to an invalid room.
64 // Placed interroom-stair object beyond the 4 header slots. The runtime
65 // can only consume four; extras can never be reached.
67};
68
69// Captures one anomaly found while resolving a room's staircase connectivity.
70// Field meaning depends on `kind`; defaults represent "not applicable".
72 int from_room_id = -1;
74 int slot_index = -1; // 0..3 for UnusedHeader/MissingDestination.
75 int header_room_id = -1; // Header destination value (raw, may be invalid).
76 int16_t object_id = -1; // Placed object id (MissingDestination/Extra).
77};
78
80 std::vector<DungeonConnectedRoomLink> links;
81 std::vector<DungeonStaircaseIssue> staircase_issues;
82};
83
92
93inline constexpr std::array<const char*, 6> kDungeonIssueCategoryLabels = {{
94 "Palette mismatch",
95 "Object draw mismatch",
96 "Door render mismatch",
97 "Entity mismatch",
98 "Overlay or collision mismatch",
99 "General room render mismatch",
100}};
101
103 return static_cast<int>(category);
104}
105
106inline const char* GetIssueCategoryLabel(int index) {
107 if (index < 0 ||
108 index >= static_cast<int>(kDungeonIssueCategoryLabels.size())) {
109 return kDungeonIssueCategoryLabels.back();
110 }
111 return kDungeonIssueCategoryLabels[static_cast<size_t>(index)];
112}
113
115 const DungeonObjectInteraction& object_interaction) {
116 if (!object_interaction.GetSelectedObjectIndices().empty()) {
118 }
119 if (object_interaction.HasEntitySelection()) {
120 switch (object_interaction.GetSelectedEntity().type) {
121 case EntityType::Door:
124 case EntityType::Item:
126 default:
127 break;
128 }
129 }
131}
132
133std::vector<DungeonConnectedRoomLink> CollectDungeonConnectedRoomLinks(
134 int room_id, const zelda3::Room& room,
135 const std::function<bool(int, zelda3::DoorDirection)>& has_reciprocal_door);
136
137// Returns the same set of links as CollectDungeonConnectedRoomLinks plus any
138// stale staircase header slots — i.e. header destinations with no placed
139// interroom-stair object consuming them. Provenance fields on each returned
140// link are populated for tooltip/issue-report surfacing.
141DungeonConnectedRoomLinkDiagnostics CollectDungeonConnectedRoomLinkDiagnostics(
142 int room_id, const zelda3::Room& room,
143 const std::function<bool(int, zelda3::DoorDirection)>& has_reciprocal_door);
144
145// Renders a single connectivity link as a one-line human-readable description
146// for tooltips and issue reports. Caller decides separator/heading.
148 const DungeonConnectedRoomLink& link);
149
150// Renders a staircase issue entry as a one-line warning for tooltips and
151// issue reports.
153 const DungeonStaircaseIssue& issue);
154
155// Returns the room id adjacent to `room_id` in the given direction, or -1 if
156// no such neighbor exists (out of the 16x16 room matrix).
157inline int NeighborRoomId(int room_id, zelda3::DoorDirection dir) {
158 if (room_id < 0 || room_id >= zelda3::kNumberOfRooms) {
159 return -1;
160 }
161
162 constexpr int kCols = 16;
163 const int row = room_id / kCols;
164 const int col = room_id % kCols;
165
166 int candidate = -1;
167 switch (dir) {
169 candidate = row > 0 ? room_id - kCols : -1;
170 break;
172 candidate = room_id + kCols;
173 break;
175 candidate = col > 0 ? room_id - 1 : -1;
176 break;
178 candidate = col < kCols - 1 ? room_id + 1 : -1;
179 break;
180 }
181
182 return (candidate >= 0 && candidate < zelda3::kNumberOfRooms) ? candidate
183 : -1;
184}
185
186// Returns the opposite cardinal direction (N<->S, E<->W).
201
206 Manual, // Use ObjectDrawer routines
207 Emulator, // Use SNES emulator
208 Hybrid // Emulator with manual fallback
209};
210
212 public:
213 explicit DungeonCanvasViewer(Rom* rom = nullptr)
217 [this](int target_room, std::optional<size_t> target_door_index,
218 int target_tile_x, int target_tile_y) {
219 NavigateToRoom(target_room);
220 if (target_tile_x >= 0 && target_tile_y >= 0) {
221 ScrollToTile(target_tile_x, target_tile_y);
222 TriggerCanvasPingRect(target_tile_x * 8, target_tile_y * 8, 24, 24);
223 } else {
225 }
226 if (target_door_index.has_value()) {
228 *target_door_index);
229 }
230 });
231 }
232
233 void DrawDungeonCanvas(int room_id);
234 std::optional<int> DrawConnectedRoomMatrix(int center_room_id);
235 void DrawConnectedToolbarControls(int center_room_id);
236 void Draw(int room_id);
237 void TriggerChangePing();
238 void TriggerCanvasPingRect(int pixel_x, int pixel_y, int pixel_w,
239 int pixel_h);
241 const std::vector<zelda3::RoomObject>& previous_objects,
242 const std::vector<zelda3::RoomObject>& next_objects);
243
244 // Unscaled content size of the connected-room graph centered on
245 // `center_room_id`. Used by the workbench to reserve scroll space for the
246 // body child before DrawConnectedRoomMatrix runs, so the matrix doesn't need
247 // to open its own BeginChild scroll wrapper.
248 ImVec2 GetConnectedContentSize(int center_room_id);
249 // Clamped connected-canvas zoom scale that matches the one used by
250 // DrawConnectedRoomMatrix when it next runs.
251 float ConnectedCanvasScale() const;
252 void SetConnectedControlsInline(bool inline_controls) {
253 connected_controls_inline_ = inline_controls;
254 }
255
263 EditorContext context() const { return {rom_, game_data_}; }
270 Rom* rom() const { return rom_; }
273 void SetRenderer(gfx::IRenderer* renderer) { renderer_ = renderer; }
274
275 // Room data access
281 // Rebind ROM-backed context without replacing this viewer. This preserves
282 // panel aliases into viewer state while discarding selections and caches
283 // that may refer to data decoded from the previous ROM bytes.
285 DungeonRoomStore* rooms, int room_id);
286 DungeonRoomStore* rooms() const { return rooms_; }
287 bool HasRooms() const { return rooms_ != nullptr; }
288
289 // Best-effort "current room" context for auxiliary panels that are driven by
290 // whichever room the viewer is currently drawing.
291 int current_room_id() const { return current_room_id_; }
292
293 // Workbench/Inspector integration.
294 void SetCompactHeaderMode(bool compact) { compact_header_mode_ = compact; }
296 void SetRoomDetailsExpanded(bool expanded) { show_room_details_ = expanded; }
297
298 // Used by overworld editor when double-clicking entrances
299 void set_active_rooms(const ImVector<int>& rooms) { active_rooms_ = rooms; }
301
302 // Palette access
305 }
306 void SetCurrentPaletteId(uint64_t id) { current_palette_id_ = id; }
308 bool force_refresh = false) {
309 bool palette_changed = current_palette_group_.name() != group.name() ||
310 current_palette_group_.size() != group.size();
311 for (int i = 0; !palette_changed && i < static_cast<int>(group.size());
312 ++i) {
313 palette_changed =
315 }
316 if (!palette_changed && !force_refresh) {
317 return;
318 }
319
321 object_interaction_.SetCurrentPaletteGroup(group, force_refresh);
322 }
323 void SetEntranceRenderContext(int entrance_id, uint8_t entrance_blockset) {
324 current_entrance_id_ = entrance_id;
325 current_entrance_blockset_ = entrance_blockset;
326 }
334 }
335 void SetRoomNavigationCallback(std::function<void(int)> callback) {
336 room_navigation_callback_ = std::move(callback);
337 }
338 // Callback to swap the current room in-place (for arrow navigation)
339 void SetRoomSwapCallback(std::function<void(int, int)> callback) {
340 room_swap_callback_ = std::move(callback);
341 }
342
343 bool CanNavigateRooms() const {
344 return room_swap_callback_ != nullptr ||
345 room_navigation_callback_ != nullptr;
346 }
347
348 // Navigate to another dungeon room using the same semantics as the header
349 // arrow buttons:
350 // - If a swap callback exists (workbench mode), swap in-place.
351 // - Otherwise, use the navigation callback (opens/switches room view).
352 void NavigateToRoom(int target_room) {
353 if (target_room < 0 || target_room >= zelda3::kNumberOfRooms) {
354 return;
355 }
358 } else if (room_navigation_callback_) {
359 room_navigation_callback_(target_room);
360 }
361 }
362 void SetShowObjectPanelCallback(std::function<void()> callback) {
363 show_object_panel_callback_ = std::move(callback);
364 }
365 void SetShowSpritePanelCallback(std::function<void()> callback) {
366 show_sprite_panel_callback_ = std::move(callback);
367 }
368 void SetShowItemPanelCallback(std::function<void()> callback) {
369 show_item_panel_callback_ = std::move(callback);
370 }
371 void SetShowRoomListCallback(std::function<void()> callback) {
372 show_room_list_callback_ = std::move(callback);
373 }
374 void SetShowRoomMatrixCallback(std::function<void()> callback) {
375 show_room_matrix_callback_ = std::move(callback);
376 }
377 void SetShowEntranceListCallback(std::function<void()> callback) {
378 show_entrance_list_callback_ = std::move(callback);
379 }
380
387
390 }
392
393 // Overlay toggles (used by settings panels / workbench UI).
417 }
424
425 bool show_grid() const { return show_grid_; }
426 void set_show_grid(bool show) { show_grid_ = show; }
427 int custom_grid_size() const { return custom_grid_size_; }
428 void set_custom_grid_size(int size) {
429 custom_grid_size_ = size > 0 ? size : 1;
430 }
432 void set_show_object_bounds(bool show) { show_object_bounds_ = show; }
436 }
440 void set_show_texture_debug(bool show) { show_texture_debug_ = show; }
441 bool show_layer_info() const { return show_layer_info_; }
442 void set_show_layer_info(bool show) { show_layer_info_ = show; }
451
452 // Mutable pointer accessors for OverlayManagerPanel binding.
453 bool* mutable_show_grid() { return &show_grid_; }
475 void SetShowRoomGraphicsCallback(std::function<void()> callback) {
476 show_room_graphics_callback_ = std::move(callback);
477 }
478 void SetShowDoorEditorCallback(std::function<void()> callback) {
479 show_door_editor_callback_ = std::move(callback);
480 }
481 void SetShowDungeonSettingsCallback(std::function<void()> callback) {
482 show_dungeon_settings_callback_ = std::move(callback);
483 }
484 void SetSaveRoomCallback(std::function<void(int)> callback) {
485 save_room_callback_ = std::move(callback);
486 }
488 std::function<void(int, const zelda3::RoomObject&)> callback) {
489 edit_graphics_callback_ = std::move(callback);
490 }
494 void SetPinned(bool pinned) { is_pinned_ = pinned; }
495 void SetPinCallback(std::function<void(bool)> callback) {
496 pin_callback_ = std::move(callback);
497 }
499 const project::YazeProject* project() const { return project_; }
500
501 // Canvas access
503 const gui::Canvas& canvas() const { return canvas_; }
504
505 // Object interaction access
507
511
512 // Enable/disable object interaction mode
513 void SetObjectInteractionEnabled(bool enabled) {
515 }
519
520 // Make the room header controls non-interactive while still allowing canvas
521 // pan/zoom (useful for split/compare views).
522 void SetHeaderReadOnly(bool read_only) { header_read_only_ = read_only; }
523 bool header_read_only() const { return header_read_only_; }
524
525 // Hide the header chrome entirely (useful for stitched/split layouts where a
526 // parent container provides its own metadata/controls).
527 void SetHeaderVisible(bool visible) { header_visible_ = visible; }
528 bool header_visible() const { return header_visible_; }
535
536 // Set and get the object render mode
541
542 // Layer visibility controls (per-room) using RoomLayerManager
543 void SetLayerVisible(int room_id, zelda3::LayerType layer, bool visible) {
544 GetRoomLayerManager(room_id).SetLayerVisible(layer, visible);
545 }
546 bool IsLayerVisible(int room_id, zelda3::LayerType layer) const {
547 auto it = room_layer_managers_.find(room_id);
548 return it != room_layer_managers_.end() ? it->second.IsLayerVisible(layer)
549 : true;
550 }
551
552 // Legacy compatibility - BG1 visibility (combines layout + objects)
553 void SetBG1Visible(int room_id, bool visible) {
554 auto& mgr = GetRoomLayerManager(room_id);
555 mgr.SetLayerVisible(zelda3::LayerType::BG1_Layout, visible);
556 mgr.SetLayerVisible(zelda3::LayerType::BG1_Objects, visible);
557 }
558 void SetBG2Visible(int room_id, bool visible) {
559 auto& mgr = GetRoomLayerManager(room_id);
560 mgr.SetLayerVisible(zelda3::LayerType::BG2_Layout, visible);
561 mgr.SetLayerVisible(zelda3::LayerType::BG2_Objects, visible);
562 }
563 bool IsBG1Visible(int room_id) const {
564 auto it = room_layer_managers_.find(room_id);
565 if (it == room_layer_managers_.end())
566 return true;
567 return it->second.IsLayerVisible(zelda3::LayerType::BG1_Layout) ||
568 it->second.IsLayerVisible(zelda3::LayerType::BG1_Objects);
569 }
570 bool IsBG2Visible(int room_id) const {
571 auto it = room_layer_managers_.find(room_id);
572 if (it == room_layer_managers_.end())
573 return true;
574 return it->second.IsLayerVisible(zelda3::LayerType::BG2_Layout) ||
575 it->second.IsLayerVisible(zelda3::LayerType::BG2_Objects);
576 }
577
578 // Layer blend mode controls
579 void SetLayerBlendMode(int room_id, zelda3::LayerType layer,
581 GetRoomLayerManager(room_id).SetLayerBlendMode(layer, mode);
582 }
584 zelda3::LayerType layer) const {
585 auto it = room_layer_managers_.find(room_id);
586 return it != room_layer_managers_.end()
587 ? it->second.GetLayerBlendMode(layer)
589 }
590
591 // Per-object translucency
592 void SetObjectTranslucent(int room_id, size_t object_index, bool translucent,
593 uint8_t alpha = 128) {
594 GetRoomLayerManager(room_id).SetObjectTranslucency(object_index,
595 translucent, alpha);
596 }
597
598 // Layer manager access
600 return room_layer_managers_[room_id];
601 }
603 static zelda3::RoomLayerManager default_manager;
604 auto it = room_layer_managers_.find(room_id);
605 return it != room_layer_managers_.end() ? it->second : default_manager;
606 }
607
608 // Legacy BG2 layer type (mapped to blend mode)
609 void SetBG2LayerType(int room_id, int type) {
610 auto& mgr = GetRoomLayerManager(room_id);
612 switch (type) {
613 case 0:
615 break;
616 case 1:
618 break;
619 case 2:
621 break;
622 case 3:
624 break;
625 case 4:
627 break;
628 default:
630 break;
631 }
632 mgr.SetLayerBlendMode(zelda3::LayerType::BG2_Layout, mode);
633 mgr.SetLayerBlendMode(zelda3::LayerType::BG2_Objects, mode);
634 }
635 int GetBG2LayerType(int room_id) const {
637 switch (mode) {
639 return 0;
641 return 1;
643 return 2;
645 return 3;
647 return 4;
648 }
649 return 0;
650 }
651
652 // Set the object to be placed
654 // Pass palette group first so ghost preview can render correctly
657 }
660 false);
661 }
662
663 // Scroll the canvas to center on the given tile coordinates.
664 // The pending target is consumed by Draw() on the next frame.
665 void ScrollToTile(int tile_x, int tile_y) {
666 pending_scroll_target_ = {tile_x, tile_y};
667 }
669 return pending_scroll_target_.has_value();
670 }
671 std::optional<std::pair<int, int>> GetPendingScrollTarget() const {
673 }
674
675 // Object manipulation
677
678 // Entity visibility controls
679 void SetSpritesVisible(bool visible) {
681 }
683 void SetPotItemsVisible(bool visible) {
685 }
687
690 int col = 0;
691 int row = 0;
692 bool placed = false;
693 bool connected_to_start = false;
694 };
695
696 std::array<bool, zelda3::kNumberOfRooms> room_mask{};
697 std::array<RoomPlacement, zelda3::kNumberOfRooms> room_positions{};
698 std::array<std::string, zelda3::kNumberOfRooms> room_floor_labels{};
699 std::vector<std::string> floor_order;
700 std::vector<DungeonConnectedRoomLink> links;
701 // Staircase configuration anomalies aggregated across the whole
702 // connected component. Order is "first-encountered during BFS".
703 std::vector<DungeonStaircaseIssue> staircase_issues;
704 // Resolved links whose destination room falls outside the project's
705 // current dungeon registry scope (only populated when scoping is active).
706 // The runtime would still traverse these; the matrix omits the target
707 // rooms by default but surfaces the link as a diagnostic so the user can
708 // see "current dungeon connects out to room X via staircase".
709 std::vector<DungeonConnectedRoomLink> out_of_scope_links;
710 // True when BFS is restricted to a project-registry dungeon group.
712 int room_count = 0;
714 int min_col = 0;
715 int max_col = 0;
716 int min_row = 0;
717 int max_row = 0;
718 };
719
720 private:
722 friend class
724 friend class
726 friend class
728 friend class
730
732 int x = 0;
733 int y = 0;
734 int w = 0;
735 int h = 0;
736 };
737
739 const zelda3::RoomObject& object, int canvas_x,
740 int canvas_y);
741 void DrawRoomHeader(zelda3::Room& room, int room_id);
742 void DrawRoomNavigation(int room_id);
743 void DrawRoomPropertyTable(zelda3::Room& room, int room_id);
744 void DrawRecentRoomBreadcrumbs(int room_id);
745 void DrawLayerControls(zelda3::Room& room, int room_id);
746 void DrawCompactLayerToggles(int room_id);
747 void PopulateCanvasContextMenu(int room_id);
748 void AddInteractionContextMenuItems(int room_id);
749 void AddLoadedRoomContextMenuItems(int room_id);
751 std::vector<gui::CanvasMenuItem> BuildSelectionContextMenuItems(int room_id);
752 std::optional<zelda3::RoomObject> GetObjectUnderContextCursor(int room_id);
755 std::optional<gui::CanvasMenuItem> BuildOpenContextMenu();
760 std::string BuildRoomMetadataSummary(const zelda3::Room& room,
761 int room_id) const;
762 std::string BuildDrawIssueReport(const zelda3::Room& room, int room_id) const;
763 std::string BuildSelectionIssueReport(const zelda3::Room& room,
764 int room_id) const;
765 void OpenIssueReportPopup(const std::string& title,
766 const std::string& summary,
767 const std::string& kind_label,
768 const std::string& diagnostics, int room_id,
769 int default_category_index);
770 absl::Status PrepareIssueReportPopup(const std::string& title,
771 const std::string& summary,
772 const std::string& kind_label,
773 const std::string& diagnostics,
774 int room_id, int default_category_index);
775 std::string BuildIssueReportClipboardText() const;
776 absl::Status CaptureIssueReportScreenshot();
777 absl::Status AppendIssueReportToLog();
778 absl::Status EnsureIssueReportPersisted();
781 void SetIssueReportPopupStatus(std::string message, bool is_error);
783 void DrawIssueReportStatusMessage() const;
784 void RenderSprites(const gui::CanvasRuntime& rt, const zelda3::Room& room);
785 void RenderPotItems(const gui::CanvasRuntime& rt, const zelda3::Room& room);
787 const zelda3::Room& room);
788
789 // Touch interaction: long-press context menu for entities
791 const zelda3::Room& room);
792
793 // Visualization
795 const zelda3::Room& room);
798 GetCollisionOverlayCache(int room_id);
799
800 // Draw semi-transparent overlay on stored value 1 / BG2 overlay objects when
801 // mask mode is active.
803 const zelda3::Room& room);
804 void DrawPersistentDebugWindows(int room_id);
805 void DrawRoomDebugWindow(zelda3::Room& room, int room_id);
806 void DrawTextureDebugWindow(zelda3::Room& room, int room_id);
807 void DrawLayerInfoWindow(int room_id);
809 int room_id);
810 void DrawCoordinateOverlayHud(int room_id);
811 bool ValidateRoomCanvasRequest(int room_id);
815 void RefreshActiveRoomCanvasState(zelda3::Room& room, int room_id);
817 void SyncCanvasCaptureRegion(const gui::CanvasRuntime& canvas_rt);
818 void ConsumePendingCanvasScroll(const gui::CanvasRuntime& canvas_rt);
819 void DrawHeaderHiddenMetadataHud(int room_id);
820 void PrepareRoomStateForCanvas(zelda3::Room& room, int room_id);
821 void DrawRoomCanvasContent(const gui::CanvasRuntime& canvas_rt,
822 zelda3::Room& room, int room_id);
823 void HandleRoomCanvasDropTargets(zelda3::Room& room, int room_id);
824 void DrawChangePingOverlay(const gui::CanvasRuntime& canvas_rt,
825 const zelda3::Room& room);
826 void RecordVisitedRoom(int room_id);
827
828 // Room graphics management
829 // Load: Read from ROM, Render: Process pixels, Draw: Display on canvas
830 absl::Status LoadAndRenderRoomGraphics(int room_id);
835 int ApplyConnectedStaircaseIssueAutoFixes(int center_room_id);
836 bool DrawRoomBackgroundLayers(int room_id); // Draw room buffers to canvas
837
838 Rom* rom_ = nullptr;
840 gui::Canvas canvas_{"##DungeonCanvas", ImVec2(0x200, 0x200)};
841 gui::Canvas connected_canvas_{"##DungeonConnectedCanvas",
842 ImVec2(0x200, 0x200)};
843 // ObjectRenderer removed - use ObjectDrawer for rendering (production system)
845
846 // Touch gesture handler for long-press context menus on touch devices
848
849 // Scroll target
850 std::optional<std::pair<int, int>> pending_scroll_target_;
851
852 // Room data
855 std::vector<int> recently_visited_rooms_;
858 // Used by overworld editor for double-click entrance → open dungeon room
859 ImVector<int> active_rooms_;
868 // Last-frame visibility of the connected-mode side panel (overview /
869 // preview). Used to apply hysteresis when the viewport width crosses the
870 // panel's min-size threshold, so dragging the pane splitter past the
871 // threshold doesn't cause a sudden ~220 px horizontal snap.
874 ImVec2 connected_controls_custom_position_value_ = ImVec2(0.0f, 0.0f);
879
880 // Object interaction state
882
883 // Per-room layer managers (4-way visibility, blend modes, per-object translucency)
884 std::map<int, zelda3::RoomLayerManager> room_layer_managers_;
885
886 // Palette data
890 std::function<void(int)> room_navigation_callback_;
891 std::function<void(int, int)>
892 room_swap_callback_; // (old_room_id, new_room_id)
893 std::function<void()> show_object_panel_callback_;
894 std::function<void()> show_sprite_panel_callback_;
895 std::function<void()> show_item_panel_callback_;
896 std::function<void()> show_room_list_callback_;
897 std::function<void()> show_room_matrix_callback_;
898 std::function<void()> show_entrance_list_callback_;
899 std::function<void()> show_room_graphics_callback_;
900 std::function<void()> show_door_editor_callback_;
901 std::function<void()> show_dungeon_settings_callback_;
902 std::function<void(int)> save_room_callback_;
903 std::function<void(int, const zelda3::RoomObject&)> edit_graphics_callback_;
906 bool is_pinned_ = false;
907 std::function<void(bool)> pin_callback_;
909
919 bool show_room_details_ = false;
921 bool header_read_only_ = false;
922 bool header_visible_ = true;
924
926 std::vector<uint16_t> track_tile_order_;
927 std::vector<uint16_t> switch_tile_order_;
929 std::unordered_map<int, DungeonRenderingHelpers::CollisionOverlayCache>
931 std::bitset<256> minecart_sprite_ids_{};
932
933 // Object rendering cache
941 std::vector<ObjectRenderCache> object_render_cache_;
942 uint64_t last_palette_hash_ = 0;
943
944 // Debug state flags
948 bool show_layer_info_ = false;
949 bool show_grid_ = false; // Grid off by default for dungeon editor
951 false; // Show camera coordinates on hover (toggle via Debug menu)
952 int layout_override_ = -1; // -1 for no override
955 ObjectRenderMode::Emulator; // Default to emulator rendering
956
957 // Object outline filtering toggles
959 bool show_type1_objects = true; // Standard objects (0x00-0xFF)
960 bool show_type2_objects = true; // Extended objects (0x100-0x1FF)
961 bool show_type3_objects = true; // Special objects (0xF00-0xFFF)
962 bool show_layer0_objects = true; // Primary / upper layer (BG1)
963 bool show_layer1_objects = true; // BG2 overlay / lower layer (BG2)
964 bool show_layer2_objects = true; // BG1 overlay room stream
965 };
967
968 // Entity overlay visibility toggles
970 bool show_sprites = true; // Show sprite entities
971 bool show_pot_items = true; // Show pot item entities
972 bool show_chests = true; // Show chest entities (future)
973 };
975
977
978 // Previous room state for change detection (per-viewer)
981 int prev_layout_ = -1;
983
992 std::string issue_report_popup_id_ = "##DungeonIssueReportPopup";
1004 std::vector<ChangePingRect> change_ping_rects_;
1006};
1007
1008} // namespace editor
1009} // namespace yaze
1010
1011#endif
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
std::optional< zelda3::RoomObject > GetObjectUnderContextCursor(int room_id)
gui::CanvasMenuItem BuildLayerVisibilityContextMenu(int room_id)
void HandleRoomCanvasDropTargets(zelda3::Room &room, int room_id)
bool RoomHasNonExitDoorInDirection(int room_id, zelda3::DoorDirection dir)
const zelda3::RoomLayerManager & GetRoomLayerManager(int room_id) const
void DrawChangePingOverlay(const gui::CanvasRuntime &canvas_rt, const zelda3::Room &room)
void SetShowEntranceListCallback(std::function< void()> callback)
void RefreshActiveRoomCanvasState(zelda3::Room &room, int room_id)
gui::CanvasMenuItem BuildRoomContextMenu(int room_id)
gui::CanvasMenuItem BuildDebugContextMenu(int room_id)
friend class DungeonEditorPaletteRefreshTest_CachedRoomRefreshesThroughViewerCompositePreparation_Test
std::optional< std::pair< int, int > > pending_scroll_target_
void SetLayerBlendMode(int room_id, zelda3::LayerType layer, zelda3::LayerBlendMode mode)
void SyncCanvasCaptureRegion(const gui::CanvasRuntime &canvas_rt)
std::vector< gui::CanvasMenuItem > BuildSelectionContextMenuItems(int room_id)
void SetEditorSystem(zelda3::DungeonEditorSystem *system)
int ApplyConnectedStaircaseIssueAutoFixes(int center_room_id)
void RenderPotItems(const gui::CanvasRuntime &rt, const zelda3::Room &room)
std::optional< int > DrawConnectedRoomMatrix(int center_room_id)
absl::Status LoadAndRenderRoomGraphics(int room_id)
friend class DungeonEditorPaletteRefreshTest_DungeonMainNotificationSkipsUnrelatedCachedViewerWork_Test
void SetMinecartTrackPanel(MinecartTrackEditorPanel *panel)
void SetObjectTranslucent(int room_id, size_t object_index, bool translucent, uint8_t alpha=128)
void SetShowItemPanelCallback(std::function< void()> callback)
std::function< void()> show_door_editor_callback_
void SetShowRoomGraphicsCallback(std::function< void()> callback)
void SetConnectedControlsInline(bool inline_controls)
void SetProject(const project::YazeProject *project)
std::function< void(int, const zelda3::RoomObject &) edit_graphics_callback_)
const DungeonRenderingHelpers::CollisionOverlayCache & GetCollisionOverlayCache(int room_id)
void SetIssueReportPopupStatus(std::string message, bool is_error)
void set_active_rooms(const ImVector< int > &rooms)
void SetRenderer(gfx::IRenderer *renderer)
void SetEditGraphicsCallback(std::function< void(int, const zelda3::RoomObject &)> callback)
void SetBG2LayerType(int room_id, int type)
friend class DungeonEditorPaletteRefreshTest_CachedPanelPaletteRefreshSurvivesWorkbenchModeAndPreservesRoomPalette_Test
std::function< void()> show_entrance_list_callback_
void DrawTextureDebugWindow(zelda3::Room &room, int room_id)
zelda3::Room * EnsureRoomLoadedForConnectedView(int room_id)
void SetShowRoomListCallback(std::function< void()> callback)
ConnectedRoomGraphData connected_graph_cache_
zelda3::Room * PrepareActiveRoomForCanvasFrame(int room_id)
void RefreshRomBackedState(Rom *rom, zelda3::GameData *game_data, DungeonRoomStore *rooms, int room_id)
DungeonObjectInteraction object_interaction_
void SetObjectRenderMode(ObjectRenderMode mode)
void PrepareRoomStateForCanvas(zelda3::Room &room, int room_id)
void OpenIssueReportPopup(const std::string &title, const std::string &summary, const std::string &kind_label, const std::string &diagnostics, int room_id, int default_category_index)
void DrawRoomCanvasContent(const gui::CanvasRuntime &canvas_rt, zelda3::Room &room, int room_id)
DungeonObjectInteraction & object_interaction()
void DrawRoomDebugWindow(zelda3::Room &room, int room_id)
ConnectedRoomGraphData BuildConnectedRoomGraph(int start_room_id)
friend class DungeonEditorPaletteRefreshTest_CachedCompositePreparationPreservesTargetEntranceContext_Test
std::string BuildRoomMetadataSummary(const zelda3::Room &room, int room_id) const
std::function< void()> show_room_list_callback_
void TriggerCanvasPingRect(int pixel_x, int pixel_y, int pixel_w, int pixel_h)
void SetShowRoomMatrixCallback(std::function< void()> callback)
const project::YazeProject * project_
void SetRoomNavigationCallback(std::function< void(int)> callback)
gui::CanvasMenuItem BuildReportContextMenu(int room_id)
void SetShowSpritePanelCallback(std::function< void()> callback)
void SetPreviewObject(const zelda3::RoomObject &object)
std::function< void()> show_object_panel_callback_
std::function< void(bool)> pin_callback_
std::optional< std::pair< int, int > > GetPendingScrollTarget() const
void SetBG1Visible(int room_id, bool visible)
void SetBG2Visible(int room_id, bool visible)
bool IsLayerVisible(int room_id, zelda3::LayerType layer) const
std::unordered_map< int, DungeonRenderingHelpers::CollisionOverlayCache > collision_overlay_cache_
void DrawLayerControls(zelda3::Room &room, int room_id)
void SetShowObjectPanelCallback(std::function< void()> callback)
void RenderSprites(const gui::CanvasRuntime &rt, const zelda3::Room &room)
gfx::Bitmap * PrepareRoomCompositeBitmap(int room_id)
zelda3::RoomLayerManager & GetRoomLayerManager(int room_id)
ObjectRenderMode GetObjectRenderMode() const
void DrawMaskHighlights(const gui::CanvasRuntime &rt, const zelda3::Room &room)
void TriggerObjectChangePing(const std::vector< zelda3::RoomObject > &previous_objects, const std::vector< zelda3::RoomObject > &next_objects)
gui::CanvasFrameOptions BuildRoomCanvasFrameOptions() const
std::string BuildDrawIssueReport(const zelda3::Room &room, int room_id) const
void SetEntranceRenderContext(int entrance_id, uint8_t entrance_blockset)
absl::Status PrepareIssueReportPopup(const std::string &title, const std::string &summary, const std::string &kind_label, const std::string &diagnostics, int room_id, int default_category_index)
std::function< void()> show_dungeon_settings_callback_
void SetRoomSwapCallback(std::function< void(int, int)> callback)
const project::YazeProject * project() const
std::function< void()> show_room_matrix_callback_
void ScrollToTile(int tile_x, int tile_y)
zelda3::LayerBlendMode GetLayerBlendMode(int room_id, zelda3::LayerType layer) const
std::function< void()> show_room_graphics_callback_
void ConsumePendingCanvasScroll(const gui::CanvasRuntime &canvas_rt)
void SetSaveRoomCallback(std::function< void(int)> callback)
void SetCurrentPaletteGroup(const gfx::PaletteGroup &group, bool force_refresh=false)
void DrawRoomHeader(zelda3::Room &room, int room_id)
std::function< void(int)> room_navigation_callback_
void RenderEntityOverlay(const gui::CanvasRuntime &rt, const zelda3::Room &room)
std::function< void()> show_item_panel_callback_
std::map< int, zelda3::RoomLayerManager > room_layer_managers_
std::vector< ObjectRenderCache > object_render_cache_
void DisplayObjectInfo(const gui::CanvasRuntime &rt, const zelda3::RoomObject &object, int canvas_x, int canvas_y)
std::function< void()> show_sprite_panel_callback_
std::function< void(int, int)> room_swap_callback_
DungeonRenderingHelpers::TrackCollisionConfig track_collision_config_
void SetLayerVisible(int room_id, zelda3::LayerType layer, bool visible)
void DrawObjectPositionOutlines(const gui::CanvasRuntime &rt, const zelda3::Room &room)
std::optional< gui::CanvasMenuItem > BuildOpenContextMenu()
void DrawRoomPropertyTable(zelda3::Room &room, int room_id)
void HandleTouchLongPressContextMenu(const gui::CanvasRuntime &rt, const zelda3::Room &room)
void SetShowDungeonSettingsCallback(std::function< void()> callback)
void SetRooms(DungeonRoomStore *rooms)
MinecartTrackEditorPanel * minecart_track_panel_
std::vector< ChangePingRect > change_ping_rects_
void DrawRoomCanvasOverlays(const gui::CanvasRuntime &rt, zelda3::Room &room, int room_id)
std::string BuildSelectionIssueReport(const zelda3::Room &room, int room_id) const
gui::CanvasMenuItem BuildCopyExportContextMenu(int room_id)
void SetGameData(zelda3::GameData *game_data)
void SetShowDoorEditorCallback(std::function< void()> callback)
void SetPinCallback(std::function< void(bool)> callback)
std::function< void(int)> save_room_callback_
Handles object selection, placement, and interaction within the dungeon canvas.
void SetDoorPairNavigationCallback(std::function< void(int, std::optional< size_t >, int, int)> callback)
void SetEditorSystem(zelda3::DungeonEditorSystem *system)
std::vector< size_t > GetSelectedObjectIndices() const
void SetCurrentPaletteGroup(const gfx::PaletteGroup &group, bool force_refresh=false)
void SelectEntity(EntityType type, size_t index)
void SetPreviewObject(const zelda3::RoomObject &object, bool loaded)
Represents a bitmap image optimized for SNES ROM hacking.
Definition bitmap.h:67
Defines an abstract interface for all rendering operations.
Definition irenderer.h:60
Handles touch input integration for Canvas.
Modern, robust canvas for drawing and manipulating graphics.
Definition canvas.h:64
RoomLayerManager - Manages layer visibility and compositing.
void SetLayerBlendMode(LayerType layer, LayerBlendMode mode)
void SetLayerVisible(LayerType layer, bool visible)
void SetObjectTranslucency(size_t object_index, bool translucent, uint8_t alpha=128)
std::string FormatDungeonConnectedLinkDescription(const DungeonConnectedRoomLink &link)
int NeighborRoomId(int room_id, zelda3::DoorDirection dir)
DungeonConnectedRoomLinkDiagnostics CollectDungeonConnectedRoomLinkDiagnostics(int room_id, const zelda3::Room &room, const std::function< bool(int, zelda3::DoorDirection)> &has_reciprocal_door)
const char * GetIssueCategoryLabel(int index)
constexpr std::array< const char *, 6 > kDungeonIssueCategoryLabels
zelda3::DoorDirection OppositeDir(zelda3::DoorDirection dir)
DungeonIssueCategory GetDefaultSelectionIssueCategory(const DungeonObjectInteraction &object_interaction)
ObjectRenderMode
Handles the main dungeon canvas rendering and interaction.
constexpr int ToIssueCategoryIndex(DungeonIssueCategory category)
std::vector< DungeonConnectedRoomLink > CollectDungeonConnectedRoomLinks(int room_id, const zelda3::Room &room, const std::function< bool(int, zelda3::DoorDirection)> &has_reciprocal_door)
std::string FormatDungeonStaircaseIssueDescription(const DungeonStaircaseIssue &issue)
DoorType
Door types from ALTTP.
Definition door_types.h:33
@ NormalDoor
Normal door (upper layer)
LayerBlendMode
Layer blend modes for compositing.
LayerType
Layer types for the 4-way visibility system.
constexpr int kNumberOfRooms
DoorDirection
Door direction on room walls.
Definition door_types.h:18
@ South
Bottom wall (horizontal door, 4x3 tiles)
@ North
Top wall (horizontal door, 4x3 tiles)
@ East
Right wall (vertical door, 3x4 tiles)
@ West
Left wall (vertical door, 3x4 tiles)
std::array< RoomPlacement, zelda3::kNumberOfRooms > room_positions
std::array< std::string, zelda3::kNumberOfRooms > room_floor_labels
std::vector< DungeonConnectedRoomLink > links
std::vector< DungeonStaircaseIssue > staircase_issues
Lightweight view into the essential runtime context (Rom + GameData)
Definition editor.h:72
zelda3::GameData * game_data
Definition editor.h:74
Represents a group of palettes.
const SnesPalette & palette_ref(int i) const
Declarative menu item definition.
Definition canvas_menu.h:64
Modern project structure with comprehensive settings consolidation.
Definition project.h:172