yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
overworld_editor.h
Go to the documentation of this file.
1#ifndef YAZE_APP_EDITOR_OVERWORLDEDITOR_H
2#define YAZE_APP_EDITOR_OVERWORLDEDITOR_H
3
4#include <chrono>
5#include <memory>
6#include <optional>
7
8#include "absl/status/status.h"
9#include "app/editor/editor.h"
24#include "app/gfx/core/bitmap.h"
28#include "app/gui/core/input.h"
30#include "rom/rom.h"
31#include "imgui/imgui.h"
33
34// =============================================================================
35// Overworld Editor - UI Layer
36// =============================================================================
37//
38// ARCHITECTURE OVERVIEW:
39// ----------------------
40// The OverworldEditor is the main UI class for editing overworld maps.
41// It orchestrates several subsystems:
42//
43// 1. TILE EDITING SYSTEM
44// - Tile16Editor: Popup for editing individual 16x16 tiles
45// - Tile selection and painting on the main canvas
46// - Undo/Redo stack for paint operations
47//
48// 2. ENTITY SYSTEM
49// - OverworldEntityRenderer: Draws entities on the canvas
50// - entity_operations.cc: Insertion/deletion logic
51// - overworld_entity_interaction.cc: Drag/drop and click handling
52//
53// 3. MAP PROPERTIES SYSTEM
54// - MapPropertiesSystem: Toolbar and context menus
55// - OverworldSidebar: Property editing tabs
56// - Graphics, palettes, music per area
57//
58// 4. CANVAS SYSTEM
59// - ow_map_canvas_: Main overworld display (4096x4096)
60// - blockset_canvas_: Tile16 selector
61// - scratch_canvas_: Layout workspace
62//
63// EDITING MODES:
64// --------------
65// - DRAW_TILE: Left-click paints tiles, right-click opens tile16 editor
66// - MOUSE: Left-click selects entities, right-click opens context menus
67//
68// KEY WORKFLOWS:
69// --------------
70// See README.md in this directory for complete workflow documentation.
71//
72// SUBSYSTEM ORGANIZATION:
73// -----------------------
74// The class is organized into logical sections marked with comment blocks.
75// Each section groups related methods and state for a specific subsystem.
76// =============================================================================
77
78namespace yaze {
79namespace editor {
80
81// =============================================================================
82// Constants
83// =============================================================================
84
85constexpr unsigned int k4BPP = 4;
86constexpr unsigned int kByteSize = 3;
87constexpr unsigned int kMessageIdSize = 5;
88constexpr unsigned int kNumSheetsToLoad = 223;
91constexpr ImVec2 kCurrentGfxCanvasSize(0x100 + 1, 0x10 * 0x40 + 1);
92constexpr ImVec2 kBlocksetCanvasSize(0x100 + 1, 0x4000 + 1);
93constexpr ImVec2 kGraphicsBinCanvasSize(0x100 + 1, kNumSheetsToLoad * 0x40 + 1);
94
95constexpr ImGuiTableFlags kOWMapFlags =
96 ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable |
97 ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingStretchProp;
98
99constexpr absl::string_view kWorldList =
100 "Light World\0Dark World\0Extra World\0";
101
102constexpr absl::string_view kGamePartComboString = "Part 0\0Part 1\0Part 2\0";
103
104constexpr absl::string_view kOWMapTable = "#MapSettingsTable";
105
124class OverworldEditor : public Editor, public gfx::GfxContext {
126
127 public:
128 // ===========================================================================
129 // Construction and Initialization
130 // ===========================================================================
131
135 // MapPropertiesSystem will be initialized after maps_bmp_ and canvas are
136 // ready
137 }
138
141 dependencies_ = deps;
142 }
143
150
151 // ===========================================================================
152 // Editor Interface Implementation
153 // ===========================================================================
154
155 void Initialize() override;
156 absl::Status Load() override;
157 absl::Status Update() final;
158 absl::Status Undo() override;
159 absl::Status Redo() override;
160 absl::Status Cut() override { return absl::UnimplementedError("Cut"); }
161 absl::Status Copy() override;
162 absl::Status Paste() override;
163 absl::Status Find() override { return absl::UnimplementedError("Find"); }
164 absl::Status Save() override;
165 absl::Status Clear() override;
166
169
170 // ===========================================================================
171 // ZSCustomOverworld ASM Patching
172 // ===========================================================================
173 // These methods handle upgrading vanilla ROMs to support expanded features.
174 // See overworld_version_helper.h for version detection and feature matrix.
175
178 absl::Status ApplyZSCustomOverworldASM(int target_version);
179
181 absl::Status UpdateROMVersionMarkers(int target_version);
182
183 int jump_to_tab() { return jump_to_tab_; }
184 int jump_to_tab_ = -1;
185
186 // ===========================================================================
187 // ROM State
188 // ===========================================================================
189
190 bool IsRomLoaded() const override { return rom_ && rom_->is_loaded(); }
191 std::string GetRomStatus() const override {
192 if (!rom_)
193 return "No ROM loaded";
194 if (!rom_->is_loaded())
195 return "ROM failed to load";
196 return absl::StrFormat("ROM loaded: %s", rom_->title());
197 }
198
199 Rom* rom() const { return rom_; }
200
202 void set_current_map(int map_id) {
203 if (map_id >= 0 && map_id < zelda3::kNumOverworldMaps) {
204 // Finalize any pending paint operation before switching maps
206 current_map_ = map_id;
208 map_id / 0x40; // Calculate which world the map belongs to
211 }
212 }
213
214 void set_current_tile16(int tile_id) { current_tile16_ = tile_id; }
215
216 // ===========================================================================
217 // Graphics Loading
218 // ===========================================================================
219
227 absl::Status LoadGraphics();
228
229 // ===========================================================================
230 // Entity System - Insertion and Editing
231 // ===========================================================================
232 // Entity operations are delegated to entity_operations.cc helper functions.
233 // Entity rendering is handled by OverworldEntityRenderer.
234 // Entity interaction (drag/drop) is in overworld_entity_interaction.cc.
235
238 void HandleEntityInsertion(const std::string& entity_type);
239
244
247 void HandleTile16Edit();
248
249 // ===========================================================================
250 // Keyboard Shortcuts
251 // ===========================================================================
252
253 void ToggleBrushTool() { if (tile_painting_) tile_painting_->ToggleBrushTool(); }
254 void ActivateFillTool() { if (tile_painting_) tile_painting_->ActivateFillTool(); }
255 void CycleTileSelection(int delta);
257 return tile_painting_ && tile_painting_->PickTile16FromHoveredCanvas();
258 }
259
265
266 // ===========================================================================
267 // Panel Drawing Methods
268 // ===========================================================================
269 // These are called by the panel wrapper classes in the panels/ subdirectory.
270 // Drawing is delegated to OverworldCanvasRenderer.
271
272 absl::Status DrawAreaGraphics() {
273 if (!canvas_renderer_) return absl::FailedPreconditionError("Renderer not initialized");
274 return canvas_renderer_->DrawAreaGraphics();
275 }
276 absl::Status DrawTile16Selector() {
277 if (!canvas_renderer_) return absl::FailedPreconditionError("Renderer not initialized");
278 return canvas_renderer_->DrawTile16Selector();
279 }
280 void DrawMapProperties() { if (canvas_renderer_) canvas_renderer_->DrawMapProperties(); }
281
285 void InvalidateGraphicsCache(int map_id = -1) {
286 if (map_refresh_) map_refresh_->InvalidateGraphicsCache(map_id);
287 }
288 absl::Status DrawScratchSpace();
289 void DrawTile8Selector() { if (canvas_renderer_) canvas_renderer_->DrawTile8Selector(); }
290 absl::Status UpdateGfxGroupEditor();
291 void DrawV3Settings() { if (canvas_renderer_) canvas_renderer_->DrawV3Settings(); }
292
295
298
301
303 void DrawOverworldCanvas() { if (canvas_renderer_) canvas_renderer_->DrawOverworldCanvas(); }
304
305 private:
306 // ===========================================================================
307 // Entity Interaction System
308 // ===========================================================================
309 // Handles mouse interactions with entities in MOUSE mode.
310
313
317
319 void HandleEntityContextMenus(zelda3::GameEntity* hovered_entity);
320
322 void HandleEntityDoubleClick(zelda3::GameEntity* hovered_entity);
323
326
327 // ===========================================================================
328 // Map Refresh System (delegated to MapRefreshCoordinator)
329 // ===========================================================================
330
333
334 // Convenience delegation methods for internal use
335 void RefreshChildMap(int map_index) {
336 if (map_refresh_) map_refresh_->RefreshChildMap(map_index);
337 }
339 if (map_refresh_) map_refresh_->RefreshOverworldMap();
340 }
341 void RefreshOverworldMapOnDemand(int map_index) {
342 if (map_refresh_) map_refresh_->RefreshOverworldMapOnDemand(map_index);
343 }
344 void RefreshChildMapOnDemand(int map_index) {
345 if (map_refresh_) map_refresh_->RefreshChildMapOnDemand(map_index);
346 }
347 absl::Status RefreshMapPalette() {
348 if (map_refresh_) return map_refresh_->RefreshMapPalette();
349 return absl::FailedPreconditionError("MapRefreshCoordinator not initialized");
350 }
352 if (map_refresh_) map_refresh_->RefreshMapProperties();
353 }
354 absl::Status RefreshTile16Blockset() {
355 if (map_refresh_) return map_refresh_->RefreshTile16Blockset();
356 return absl::FailedPreconditionError("MapRefreshCoordinator not initialized");
357 }
359 if (map_refresh_) map_refresh_->UpdateBlocksetWithPendingTileChanges();
360 }
361 void ForceRefreshGraphics(int map_index) {
362 if (map_refresh_) map_refresh_->ForceRefreshGraphics(map_index);
363 }
364 void RefreshSiblingMapGraphics(int map_index, bool include_self = false) {
365 if (map_refresh_) map_refresh_->RefreshSiblingMapGraphics(map_index, include_self);
366 }
367
368 // ===========================================================================
369 // Tile Editing System
370 // ===========================================================================
371 // Handles tile painting and selection on the main canvas.
372
375
378
380 absl::Status CheckForCurrentMap();
381
384
387
390
391 // Canvas navigation (delegated to CanvasNavigationManager)
392 void HandleOverworldPan();
393 void HandleOverworldZoom();
394 void ZoomIn();
395 void ZoomOut();
397 void ResetOverworldView();
398 void CenterOverworldView();
399
400 // ===========================================================================
401 // Texture and Graphics Loading
402 // ===========================================================================
403
404 absl::Status LoadSpriteGraphics();
405
408
410 void EnsureMapTexture(int map_index);
411
412 // ===========================================================================
413 // Canvas Navigation (delegated to CanvasNavigationManager)
414 // ===========================================================================
415
416 // ===========================================================================
417 // Canvas Automation API
418 // ===========================================================================
419 // Integration with automation testing system.
420
423 bool AutomationSetTile(int x, int y, int tile_id);
424 int AutomationGetTile(int x, int y);
425
426 // ===========================================================================
427 // Scratch Space System
428 // ===========================================================================
429 // Workspace for planning tile layouts before placing them on the map.
430
431 absl::Status SaveCurrentSelectionToScratch();
432 absl::Status LoadScratchToSelection();
433 absl::Status ClearScratchSpace();
437 void UpdateScratchBitmapTile(int tile_x, int tile_y, int tile_id);
438
439 // ===========================================================================
440 // Undo/Redo System
441 // ===========================================================================
442 // Tracks tile paint operations for undo/redo functionality.
443 // Operations within 500ms are batched to reduce undo point count.
444
446 int map_id = 0;
447 int world = 0; // 0=Light, 1=Dark, 2=Special
448 std::vector<std::pair<std::pair<int, int>, int>>
449 tile_changes; // ((x,y), old_tile_id)
450 std::chrono::steady_clock::time_point timestamp;
451 };
452
453 void CreateUndoPoint(int map_id, int world, int x, int y, int old_tile_id);
455 auto& GetWorldTiles(int world);
456
457 // ===========================================================================
458 // Editing Mode State
459 // ===========================================================================
460
464
470
471 // ===========================================================================
472 // Current Selection State
473 // ===========================================================================
474
475 int current_world_ = 0; // 0=Light, 1=Dark, 2=Special
476 int current_map_ = 0; // Current map index (0-159)
477 int current_parent_ = 0; // Parent map for multi-area
483 int game_state_ = 1; // 0=Beginning, 1=Pendants, 2=Crystals
484 int current_tile16_ = 0; // Selected tile16 for painting
487
488 // Selected tile IDs for rectangle selection
489 std::vector<int> selected_tile16_ids_;
490
491 // ===========================================================================
492 // Loading State
493 // ===========================================================================
494
495 bool all_gfx_loaded_ = false;
497
498 // ===========================================================================
499 // Canvas Interaction State
500 // ===========================================================================
501
505 bool current_map_lock_ = false;
506
507 // ===========================================================================
508 // Property Windows (Standalone, Not PanelManager)
509 // ===========================================================================
510
515
516 // ===========================================================================
517 // UI Subsystem Components
518 // ===========================================================================
519
520 std::unique_ptr<OverworldCanvasRenderer> canvas_renderer_;
521 std::unique_ptr<CanvasNavigationManager> canvas_nav_;
522 std::unique_ptr<TilePaintingManager> tile_painting_;
523 std::unique_ptr<MapRefreshCoordinator> map_refresh_;
524 std::unique_ptr<MapPropertiesSystem> map_properties_system_;
525 std::unique_ptr<OverworldSidebar> sidebar_;
526 std::unique_ptr<OverworldEntityRenderer> entity_renderer_;
527 std::unique_ptr<OverworldToolbar> toolbar_;
528
529 // ===========================================================================
530 // Scratch Space System (Unified Single Workspace)
531 // ===========================================================================
532
535 std::array<std::array<int, 32>, 32> tile_data{};
536 bool in_use = false;
537 std::string name = "Scratch Space";
538 int width = 16;
539 int height = 16;
540 std::vector<ImVec2> selected_tiles;
541 std::vector<ImVec2> selected_points;
542 bool select_rect_active = false;
543 };
545
546 // ===========================================================================
547 // Core Data References
548 // ===========================================================================
549
551
555
556 // Sub-editors
560
561 // ===========================================================================
562 // Graphics Data
563 // ===========================================================================
564
570 std::array<gfx::Bitmap, zelda3::kNumOverworldMaps> maps_bmp_;
572 std::vector<gfx::Bitmap> sprite_previews_;
573
574 // ===========================================================================
575 // Overworld Data Layer
576 // ===========================================================================
577
580
581 // ===========================================================================
582 // Entity State
583 // ===========================================================================
584
590
593
594 // Deferred entity insertion (needed for popup flow from context menu)
598
599 // ===========================================================================
600 // Canvas Components
601 // ===========================================================================
602
609 std::unique_ptr<gui::TileSelectorWidget> blockset_selector_;
613 gui::Canvas scratch_canvas_{"ScratchSpace", ImVec2(320, 480),
615
616 // ===========================================================================
617 // Panel Cards
618 // ===========================================================================
619
620 std::unique_ptr<UsageStatisticsCard> usage_stats_card_;
621 std::unique_ptr<DebugWindowCard> debug_window_card_;
622
623 absl::Status status_;
624
625 // ===========================================================================
626 // Undo/Redo State
627 // ===========================================================================
628
629 std::optional<OverworldUndoPoint> current_paint_operation_;
630 std::chrono::steady_clock::time_point last_paint_time_;
631 static constexpr auto kPaintBatchTimeout = std::chrono::milliseconds(500);
632
633 // ===========================================================================
634 // Event Listeners
635 // ===========================================================================
636
638};
639} // namespace editor
640} // namespace yaze
641
642#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
bool is_loaded() const
Definition rom.h:132
auto title() const
Definition rom.h:137
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
Manage graphics group configurations in a Rom.
void SetGameData(zelda3::GameData *data)
Handles all canvas drawing and panel rendering for the overworld editor.
Main UI class for editing overworld maps in A Link to the Past.
std::unique_ptr< UsageStatisticsCard > usage_stats_card_
absl::Status Clear() override
std::unique_ptr< MapPropertiesSystem > map_properties_system_
std::unique_ptr< OverworldCanvasRenderer > canvas_renderer_
zelda3::OverworldItem current_item_
void HandleEntityInteraction()
Handle entity interaction in MOUSE mode Includes: right-click context menus, double-click navigation,...
zelda3::OverworldEntranceTileTypes entrance_tiletypes_
zelda3::OverworldEntrance current_entrance_
void HandleTile16Edit()
Handle tile16 editing from context menu (MOUSE mode) Gets the tile16 under the cursor and opens the T...
absl::Status ApplyZSCustomOverworldASM(int target_version)
Apply ZSCustomOverworld ASM patch to upgrade ROM version.
std::optional< OverworldUndoPoint > current_paint_operation_
absl::Status CheckForCurrentMap()
Check for map changes and refresh if needed.
void CreateUndoPoint(int map_id, int world, int x, int y, int old_tile_id)
absl::Status Cut() override
void ForceRefreshGraphics(int map_index)
std::vector< int > selected_tile16_ids_
void ProcessPendingEntityInsertion()
Process any pending entity insertion request Called from Update() - needed because ImGui::OpenPopup()...
Definition automation.cc:88
void InitCanvasNavigationManager()
Initialize the canvas navigation manager (called during Initialize)
void InitMapRefreshCoordinator()
Initialize the map refresh coordinator (called during Initialize)
zelda3::OverworldBlockset refresh_blockset_
absl::Status Undo() override
void HandleEntityInsertion(const std::string &entity_type)
Handle entity insertion from context menu.
Definition automation.cc:75
zelda3::GameEntity * dragged_entity_
std::unique_ptr< MapRefreshCoordinator > map_refresh_
std::array< gfx::Bitmap, zelda3::kNumOverworldMaps > maps_bmp_
void CheckForOverworldEdits()
Check for tile edits - delegates to TilePaintingManager.
absl::Status UpdateROMVersionMarkers(int target_version)
Update ROM version markers and feature flags after ASM patching.
void UpdateScratchBitmapTile(int tile_x, int tile_y, int tile_id)
zelda3::OverworldExit current_exit_
UsageStatisticsCard * usage_stats_card()
Access usage statistics card for panel.
void SetGameData(zelda3::GameData *game_data) override
void RefreshSiblingMapGraphics(int map_index, bool include_self=false)
void set_current_map(int map_id)
Set the current map for editing (also updates world)
void RefreshOverworldMapOnDemand(int map_index)
std::unique_ptr< OverworldSidebar > sidebar_
std::chrono::steady_clock::time_point last_paint_time_
void InvalidateGraphicsCache(int map_id=-1)
Invalidate cached graphics for a specific map or all maps.
DebugWindowCard * debug_window_card()
Access debug window card for panel.
void HandleEntityDoubleClick(zelda3::GameEntity *hovered_entity)
Handle double-click actions on entities (e.g., jump to room)
void HandleEntityContextMenus(zelda3::GameEntity *hovered_entity)
Handle right-click context menus for entities.
absl::Status SaveCurrentSelectionToScratch()
bool AutomationSetTile(int x, int y, int tile_id)
Definition automation.cc:29
void DrawOverworldCanvas()
Draw the main overworld canvas.
zelda3::Overworld & overworld()
Access the underlying Overworld data.
void RefreshChildMap(int map_index)
std::unique_ptr< OverworldEntityRenderer > entity_renderer_
absl::Status Load() override
void EnsureMapTexture(int map_index)
Ensure a specific map has its texture created.
std::vector< gfx::Bitmap > sprite_previews_
std::unique_ptr< CanvasNavigationManager > canvas_nav_
OverworldEditor(Rom *rom, const EditorDependencies &deps)
absl::Status Copy() override
std::string GetRomStatus() const override
std::unique_ptr< OverworldToolbar > toolbar_
bool IsRomLoaded() const override
Tile16Editor & tile16_editor()
Access the Tile16 Editor for panel integration.
void ProcessDeferredTextures()
Create textures for deferred map bitmaps on demand.
std::unique_ptr< gui::TileSelectorWidget > blockset_selector_
void DrawEntityEditorPopups()
Draw entity editor popups and update entity data.
absl::Status Paste() override
void ScrollBlocksetCanvasToCurrentTile()
Scroll the blockset canvas to show the current selected tile16.
static constexpr auto kPaintBatchTimeout
std::unique_ptr< TilePaintingManager > tile_painting_
absl::Status LoadGraphics()
Load the Bitmap objects for each OverworldMap.
void RefreshChildMapOnDemand(int map_index)
absl::Status Find() override
absl::Status Save() override
std::unique_ptr< DebugWindowCard > debug_window_card_
zelda3::GameEntity * current_entity_
void HandleKeyboardShortcuts()
Handle keyboard shortcuts for the Overworld Editor Shortcuts: 1-2 (modes), 3-8 (entities),...
int AutomationGetTile(int x, int y)
Definition automation.cc:58
void InitTilePaintingManager()
Initialize the tile painting manager (called after graphics load)
Allows the user to view and edit in game palettes.
Popup window to edit Tile16 data.
void SetGameData(zelda3::GameData *game_data)
Represents a bitmap image optimized for SNES ROM hacking.
Definition bitmap.h:67
Shared graphical context across editors.
Defines an abstract interface for all rendering operations.
Definition irenderer.h:60
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
Modern, robust canvas for drawing and manipulating graphics.
Definition canvas.h:150
Base class for all overworld and dungeon entities.
Definition common.h:31
Represents an overworld exit that transitions from dungeon to overworld.
Represents the full Overworld data, light and dark world.
Definition overworld.h:261
void set_current_world(int world)
Definition overworld.h:590
void SetGameData(GameData *game_data)
Definition overworld.h:266
void set_current_map(int i)
Definition overworld.h:589
A class for managing sprites in the overworld and underworld.
Definition sprite.h:35
constexpr ImVec2 kOverworldCanvasSize(kOverworldMapSize *8, kOverworldMapSize *8)
constexpr absl::string_view kOWMapTable
constexpr ImGuiTableFlags kOWMapFlags
constexpr unsigned int kOverworldMapSize
constexpr absl::string_view kWorldList
constexpr absl::string_view kGamePartComboString
constexpr unsigned int kNumSheetsToLoad
constexpr ImVec2 kCurrentGfxCanvasSize(0x100+1, 0x10 *0x40+1)
constexpr ImVec2 kBlocksetCanvasSize(0x100+1, 0x4000+1)
constexpr ImVec2 kGraphicsBinCanvasSize(0x100+1, kNumSheetsToLoad *0x40+1)
constexpr unsigned int kByteSize
constexpr unsigned int k4BPP
constexpr unsigned int kMessageIdSize
std::unordered_map< int, std::unique_ptr< gfx::Bitmap > > BitmapTable
Definition bitmap.h:497
constexpr int kNumOverworldMaps
Definition common.h:85
std::vector< std::vector< uint16_t > > OverworldBlockset
Represents tile32 data for the overworld.
Unified dependency container for all editor types.
Definition editor.h:163
std::vector< std::pair< std::pair< int, int >, int > > tile_changes
std::chrono::steady_clock::time_point timestamp
std::array< std::array< int, 32 >, 32 > tile_data
Tilemap structure for SNES tile-based graphics management.
Definition tilemap.h:118