yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
screen_editor.cc
Go to the documentation of this file.
1#include "screen_editor.h"
2
3#include <fstream>
4#include <iostream>
5#include <string>
6
7#include "absl/strings/str_format.h"
10#include "app/gfx/core/bitmap.h"
15#include "app/gui/core/color.h"
16#include "app/gui/core/icons.h"
17#include "app/gui/core/input.h"
19#include "imgui/imgui.h"
20#include "util/file_util.h"
21#include "util/hex.h"
22#include "util/macro.h"
23
24namespace yaze {
25namespace editor {
26
29 return;
30 auto* panel_manager = dependencies_.panel_manager;
31
32 panel_manager->RegisterPanel(
33 {.card_id = "screen.dungeon_maps",
34 .display_name = "Dungeon Maps",
35 .window_title = " Dungeon Map Editor",
36 .icon = ICON_MD_MAP,
37 .category = "Screen",
38 .shortcut_hint = "Alt+1",
39 .priority = 10,
40 .enabled_condition = [this]() { return rom()->is_loaded(); },
41 .disabled_tooltip = "Load a ROM first"});
42 panel_manager->RegisterPanel(
43 {.card_id = "screen.inventory_menu",
44 .display_name = "Inventory Menu",
45 .window_title = " Inventory Menu",
46 .icon = ICON_MD_INVENTORY,
47 .category = "Screen",
48 .shortcut_hint = "Alt+2",
49 .priority = 20,
50 .enabled_condition = [this]() { return rom()->is_loaded(); },
51 .disabled_tooltip = "Load a ROM first"});
52 panel_manager->RegisterPanel(
53 {.card_id = "screen.overworld_map",
54 .display_name = "Overworld Map",
55 .window_title = " Overworld Map",
56 .icon = ICON_MD_PUBLIC,
57 .category = "Screen",
58 .shortcut_hint = "Alt+3",
59 .priority = 30,
60 .enabled_condition = [this]() { return rom()->is_loaded(); },
61 .disabled_tooltip = "Load a ROM first"});
62 panel_manager->RegisterPanel(
63 {.card_id = "screen.title_screen",
64 .display_name = "Title Screen",
65 .window_title = " Title Screen",
66 .icon = ICON_MD_TITLE,
67 .category = "Screen",
68 .shortcut_hint = "Alt+4",
69 .priority = 40,
70 .enabled_condition = [this]() { return rom()->is_loaded(); },
71 .disabled_tooltip = "Load a ROM first"});
72 panel_manager->RegisterPanel(
73 {.card_id = "screen.naming_screen",
74 .display_name = "Naming Screen",
75 .window_title = " Naming Screen",
76 .icon = ICON_MD_EDIT,
77 .category = "Screen",
78 .shortcut_hint = "Alt+5",
79 .priority = 50,
80 .enabled_condition = [this]() { return rom()->is_loaded(); },
81 .disabled_tooltip = "Load a ROM first"});
82
83 // Register EditorPanel implementations
84 panel_manager->RegisterEditorPanel(std::make_unique<DungeonMapsPanel>(
85 [this]() { DrawDungeonMapsEditor(); }));
86 panel_manager->RegisterEditorPanel(std::make_unique<InventoryMenuPanel>(
87 [this]() { DrawInventoryMenuEditor(); }));
88 panel_manager->RegisterEditorPanel(std::make_unique<OverworldMapScreenPanel>(
89 [this]() { DrawOverworldMapEditor(); }));
90 panel_manager->RegisterEditorPanel(std::make_unique<TitleScreenPanel>(
91 [this]() { DrawTitleScreenEditor(); }));
92 panel_manager->RegisterEditorPanel(std::make_unique<NamingScreenPanel>(
93 [this]() { DrawNamingScreenEditor(); }));
94
95 // Show title screen by default
96 panel_manager->ShowPanel("screen.title_screen");
97}
98
99absl::Status ScreenEditor::Load() {
100 gfx::ScopedTimer timer("ScreenEditor::Load");
101 inventory_loaded_ = false;
102
107 game_data()->graphics_buffer, false));
108
109 // Load graphics sheets and apply dungeon palette
110 sheets_[0] =
111 std::make_unique<gfx::Bitmap>(gfx::Arena::Get().gfx_sheets()[212]);
112 sheets_[1] =
113 std::make_unique<gfx::Bitmap>(gfx::Arena::Get().gfx_sheets()[213]);
114 sheets_[2] =
115 std::make_unique<gfx::Bitmap>(gfx::Arena::Get().gfx_sheets()[214]);
116 sheets_[3] =
117 std::make_unique<gfx::Bitmap>(gfx::Arena::Get().gfx_sheets()[215]);
118
119 // Apply dungeon palette to all sheets
120 for (int i = 0; i < 4; i++) {
121 sheets_[i]->SetPalette(
122 *game_data()->palette_groups.dungeon_main.mutable_palette(3));
125 }
126
127 // Create a single tilemap for tile8 graphics with on-demand texture creation
128 // Combine all 4 sheets (128x32 each) into one bitmap (128x128)
129 // This gives us 16 tiles per row × 16 rows = 256 tiles total
130 const int tile8_width = 128;
131 const int tile8_height = 128; // 4 sheets × 32 pixels each
132 std::vector<uint8_t> tile8_data(tile8_width * tile8_height);
133
134 // Copy data from all 4 sheets into the combined bitmap
135 for (int sheet_idx = 0; sheet_idx < 4; sheet_idx++) {
136 const auto& sheet = *sheets_[sheet_idx];
137 int dest_y_offset = sheet_idx * 32; // Each sheet is 32 pixels tall
138
139 for (int y = 0; y < 32; y++) {
140 for (int x = 0; x < 128; x++) {
141 int src_index = y * 128 + x;
142 int dest_index = (dest_y_offset + y) * 128 + x;
143
144 if (src_index < sheet.size() && dest_index < tile8_data.size()) {
145 tile8_data[dest_index] = sheet.data()[src_index];
146 }
147 }
148 }
149 }
150
151 // Create tilemap with 8x8 tile size
152 tile8_tilemap_.tile_size = {8, 8};
153 tile8_tilemap_.map_size = {256, 256}; // Logical size for tile count
154 tile8_tilemap_.atlas.Create(tile8_width, tile8_height, 8, tile8_data);
157
158 // Queue single texture creation for the atlas (not individual tiles)
161 return absl::OkStatus();
162}
163
164absl::Status ScreenEditor::Save() {
165 if (core::FeatureFlags::get().kSaveDungeonMaps) {
167 }
168 // Title screen and overworld maps are currently saved via their respective
169 // 'Save' buttons in the UI, but we could also trigger them here for a full
170 // save.
171 return absl::OkStatus();
172}
173
174absl::Status ScreenEditor::Update() {
175 // Panel drawing is handled centrally by PanelManager::DrawAllVisiblePanels()
176 // via the EditorPanel implementations registered in Initialize().
177 // No local drawing needed here - this fixes duplicate panel rendering.
178 return status_;
179}
180
182 // Sidebar is now drawn by EditorManager for card-based editors
183 // This method kept for compatibility but sidebar handles card toggles
184}
185
187 if (!inventory_loaded_ && rom()->is_loaded() && game_data()) {
189 if (status_.ok()) {
191 inventory_loaded_ = true;
192 } else {
193 const auto& theme = AgentUI::GetTheme();
194 ImGui::TextColored(theme.text_error_red, "Error loading inventory: %s",
195 status_.message().data());
196 return;
197 }
198 }
199
201
202 if (ImGui::BeginTable("InventoryScreen", 4, ImGuiTableFlags_Resizable)) {
203 ImGui::TableSetupColumn("Canvas");
204 ImGui::TableSetupColumn("Tilesheet");
205 ImGui::TableSetupColumn("Item Icons");
206 ImGui::TableSetupColumn("Palette");
207 ImGui::TableHeadersRow();
208
209 ImGui::TableNextColumn();
210 {
211 gui::CanvasFrameOptions frame_opts;
212 frame_opts.draw_grid = true;
213 frame_opts.grid_step = 32.0f;
214 frame_opts.render_popups = true;
215 auto runtime = gui::BeginCanvas(screen_canvas_, frame_opts);
216 gui::DrawBitmap(runtime, inventory_.bitmap(), 2,
217 inventory_loaded_ ? 1.0f : 0.0f);
218 gui::EndCanvas(screen_canvas_, runtime, frame_opts);
219 }
220
221 ImGui::TableNextColumn();
222 {
223 gui::CanvasFrameOptions frame_opts;
224 frame_opts.canvas_size = ImVec2(128 * 2 + 2, (192 * 2) + 4);
225 frame_opts.draw_grid = true;
226 frame_opts.grid_step = 16.0f;
227 frame_opts.render_popups = true;
228 auto runtime = gui::BeginCanvas(tilesheet_canvas_, frame_opts);
230 inventory_loaded_ ? 1.0f : 0.0f);
231 gui::EndCanvas(tilesheet_canvas_, runtime, frame_opts);
232 }
233
234 ImGui::TableNextColumn();
236
237 ImGui::TableNextColumn();
239
240 ImGui::EndTable();
241 }
242 ImGui::Separator();
243
244 // TODO(scawful): Future Oracle of Secrets menu editor integration
245 // - Full inventory screen layout editor
246 // - Item slot assignment and positioning
247 // - Heart container and magic meter editor
248 // - Equipment display customization
249 // - A/B button equipment quick-select editor
250}
251
253 if (ImGui::BeginTable("InventoryToolset", 8, ImGuiTableFlags_SizingFixedFit,
254 ImVec2(0, 0))) {
255 ImGui::TableSetupColumn("#drawTool");
256 ImGui::TableSetupColumn("#sep1");
257 ImGui::TableSetupColumn("#zoomOut");
258 ImGui::TableSetupColumn("#zoomIN");
259 ImGui::TableSetupColumn("#sep2");
260 ImGui::TableSetupColumn("#bg2Tool");
261 ImGui::TableSetupColumn("#bg3Tool");
262 ImGui::TableSetupColumn("#itemTool");
263
264 ImGui::TableNextColumn();
266 // status_ = inventory_.Undo();
267 }
268 ImGui::TableNextColumn();
270 // status_ = inventory_.Redo();
271 }
272 ImGui::TableNextColumn();
273 ImGui::Text(ICON_MD_MORE_VERT);
274 ImGui::TableNextColumn();
275 if (gui::ToolbarIconButton(ICON_MD_ZOOM_OUT, "Zoom Out")) {
277 }
278 ImGui::TableNextColumn();
279 if (gui::ToolbarIconButton(ICON_MD_ZOOM_IN, "Zoom In")) {
281 }
282 ImGui::TableNextColumn();
283 ImGui::Text(ICON_MD_MORE_VERT);
284 ImGui::TableNextColumn();
285 if (gui::ToolbarIconButton(ICON_MD_DRAW, "Draw Mode")) {
287 }
288 ImGui::TableNextColumn();
289 if (gui::ToolbarIconButton(ICON_MD_BUILD, "Build Mode")) {
290 // current_mode_ = EditingMode::BUILD;
291 }
292
293 ImGui::EndTable();
294 }
295}
296
298 if (ImGui::BeginChild("##ItemIconsList", ImVec2(0, 0), true,
299 ImGuiWindowFlags_HorizontalScrollbar)) {
300 ImGui::Text("Item Icons (2x2 tiles each)");
301 ImGui::Separator();
302
303 auto& icons = inventory_.item_icons();
304 if (icons.empty()) {
305 ImGui::TextWrapped(
306 "No item icons loaded. Icons will be loaded when the "
307 "inventory is initialized.");
308 ImGui::EndChild();
309 return;
310 }
311
312 // Display icons in a table format
313 if (ImGui::BeginTable("##IconsTable", 2,
314 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
315 ImGui::TableSetupColumn("Icon Name");
316 ImGui::TableSetupColumn("Tile Data");
317 ImGui::TableHeadersRow();
318
319 for (size_t i = 0; i < icons.size(); i++) {
320 const auto& icon = icons[i];
321
322 ImGui::TableNextRow();
323 ImGui::TableNextColumn();
324
325 // Display icon name with selectable row
326 if (ImGui::Selectable(icon.name.c_str(), false,
327 ImGuiSelectableFlags_SpanAllColumns)) {
328 // TODO: Select this icon for editing
329 }
330
331 ImGui::TableNextColumn();
332 // Display tile word data in hex format
333 ImGui::Text("TL:%04X TR:%04X", icon.tile_tl, icon.tile_tr);
334 ImGui::SameLine();
335 ImGui::Text("BL:%04X BR:%04X", icon.tile_bl, icon.tile_br);
336 }
337
338 ImGui::EndTable();
339 }
340
341 ImGui::Separator();
342 ImGui::TextWrapped(
343 "NOTE: Individual icon editing will be implemented in the future "
344 "Oracle of Secrets menu editor. Each icon is composed of 4 tile words "
345 "representing a 2x2 arrangement of 8x8 tiles in SNES tile format "
346 "(vhopppcc cccccccc).");
347 }
348 ImGui::EndChild();
349}
350
352 gfx::ScopedTimer timer("screen_editor_draw_dungeon_map_screen");
353
354 const auto& theme = AgentUI::GetTheme();
355 auto& current_dungeon = dungeon_maps_[selected_dungeon];
356
357 floor_number = i;
358 screen_canvas_.DrawBackground(ImVec2(325, 325));
360
361 auto boss_room = current_dungeon.boss_room;
362
363 // Pre-allocate vectors for batch operations
364 std::vector<int> tile_ids_to_render;
365 std::vector<ImVec2> tile_positions;
366 tile_ids_to_render.reserve(zelda3::kNumRooms);
367 tile_positions.reserve(zelda3::kNumRooms);
368
369 for (int j = 0; j < zelda3::kNumRooms; j++) {
370 if (current_dungeon.floor_rooms[floor_number][j] != 0x0F) {
371 int tile16_id = current_dungeon.floor_gfx[floor_number][j];
372 int posX = ((j % 5) * 32);
373 int posY = ((j / 5) * 32);
374
375 // Batch tile rendering
376 tile_ids_to_render.push_back(tile16_id);
377 tile_positions.emplace_back(posX * 2, posY * 2);
378 }
379 }
380
381 // Batch render all tiles
382 for (size_t idx = 0; idx < tile_ids_to_render.size(); ++idx) {
383 int tile16_id = tile_ids_to_render[idx];
384 ImVec2 pos = tile_positions[idx];
385
386 // Extract tile data from the atlas directly
387 const int tiles_per_row = tile16_blockset_.atlas.width() / 16;
388 const int tile_x = (tile16_id % tiles_per_row) * 16;
389 const int tile_y = (tile16_id / tiles_per_row) * 16;
390
391 std::vector<uint8_t> tile_data(16 * 16);
392 int tile_data_offset = 0;
393 tile16_blockset_.atlas.Get16x16Tile(tile_x, tile_y, tile_data,
394 tile_data_offset);
395
396 // Create or update cached tile
397 auto* cached_tile = tile16_blockset_.tile_cache.GetTile(tile16_id);
398 if (!cached_tile) {
399 // Create new cached tile
400 gfx::Bitmap new_tile(16, 16, 8, tile_data);
402 tile16_blockset_.tile_cache.CacheTile(tile16_id, std::move(new_tile));
403 cached_tile = tile16_blockset_.tile_cache.GetTile(tile16_id);
404 } else {
405 // Update existing cached tile data
406 cached_tile->set_data(tile_data);
407 }
408
409 if (cached_tile && cached_tile->is_active()) {
410 // Ensure the cached tile has a valid texture
411 if (!cached_tile->texture()) {
412 // Queue texture creation via Arena's deferred system
415 }
416 screen_canvas_.DrawBitmap(*cached_tile, pos.x, pos.y, 4.0F, 255);
417 }
418 }
419
420 // Draw overlays and labels
421 for (int j = 0; j < zelda3::kNumRooms; j++) {
422 if (current_dungeon.floor_rooms[floor_number][j] != 0x0F) {
423 int posX = ((j % 5) * 32);
424 int posY = ((j / 5) * 32);
425
426 if (current_dungeon.floor_rooms[floor_number][j] == boss_room) {
427 screen_canvas_.DrawOutlineWithColor((posX * 2), (posY * 2), 64, 64,
428 theme.status_error);
429 }
430
431 std::string label =
433 screen_canvas_.DrawText(label, (posX * 2), (posY * 2));
434 std::string gfx_id =
435 util::HexByte(current_dungeon.floor_gfx[floor_number][j]);
436 screen_canvas_.DrawText(gfx_id, (posX * 2), (posY * 2) + 16);
437 }
438 }
439
440 screen_canvas_.DrawGrid(64.f, 5);
442
443 if (!screen_canvas_.points().empty()) {
444 int x = screen_canvas_.points().front().x / 64;
445 int y = screen_canvas_.points().front().y / 64;
446 selected_room = x + (y * 5);
447 }
448}
449
451 auto& current_dungeon = dungeon_maps_[selected_dungeon];
452 if (gui::BeginThemedTabBar("##DungeonMapTabs")) {
453 auto nbr_floors =
454 current_dungeon.nbr_of_floor + current_dungeon.nbr_of_basement;
455 for (int i = 0; i < nbr_floors; i++) {
456 int basement_num = current_dungeon.nbr_of_basement - i;
457 std::string tab_name = absl::StrFormat("Basement %d", basement_num);
458 if (i >= current_dungeon.nbr_of_basement) {
459 tab_name = absl::StrFormat("Floor %d",
460 i - current_dungeon.nbr_of_basement + 1);
461 }
462 if (ImGui::BeginTabItem(tab_name.data())) {
464 ImGui::EndTabItem();
465 }
466 }
468 }
469
471 "Selected Room",
472 &current_dungeon.floor_rooms[floor_number].at(selected_room));
473
474 gui::InputHexWord("Boss Room", &current_dungeon.boss_room);
475
476 const auto button_size = ImVec2(130, 0);
477
478 if (ImGui::Button("Add Floor", button_size) &&
479 current_dungeon.nbr_of_floor < 8) {
480 current_dungeon.nbr_of_floor++;
482 }
483 ImGui::SameLine();
484 if (ImGui::Button("Remove Floor", button_size) &&
485 current_dungeon.nbr_of_floor > 0) {
486 current_dungeon.nbr_of_floor--;
488 }
489
490 if (ImGui::Button("Add Basement", button_size) &&
491 current_dungeon.nbr_of_basement < 8) {
492 current_dungeon.nbr_of_basement++;
494 }
495 ImGui::SameLine();
496 if (ImGui::Button("Remove Basement", button_size) &&
497 current_dungeon.nbr_of_basement > 0) {
498 current_dungeon.nbr_of_basement--;
500 }
501
502 if (ImGui::Button("Copy Floor", button_size)) {
503 copy_button_pressed = true;
504 }
505 ImGui::SameLine();
506 if (ImGui::Button("Paste Floor", button_size)) {
508 }
509}
510
528 gfx::ScopedTimer timer("screen_editor_draw_dungeon_maps_room_gfx");
529
530 if (ImGui::BeginChild("##DungeonMapTiles", ImVec2(0, 0), true)) {
531 // Enhanced tilesheet canvas with BeginCanvas/EndCanvas pattern
532 {
533 gui::CanvasFrameOptions tilesheet_opts;
534 tilesheet_opts.canvas_size = ImVec2((256 * 2) + 2, (192 * 2) + 4);
535 tilesheet_opts.draw_grid = true;
536 tilesheet_opts.grid_step = 32.0f;
537 tilesheet_opts.render_popups = true;
538
539 auto tilesheet_rt = gui::BeginCanvas(tilesheet_canvas_, tilesheet_opts);
540
541 // Interactive tile16 selector with grid snapping
542 ImVec2 selected_pos;
543 if (gui::DrawTileSelector(tilesheet_rt, 32, 0, &selected_pos)) {
544 // Double-click detected - handle tile confirmation if needed
545 }
546
547 // Check for single-click selection (legacy compatibility)
549 ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
550 if (!tilesheet_canvas_.points().empty()) {
551 selected_tile16_ = static_cast<int>(
552 tilesheet_canvas_.points().front().x / 32 +
553 (tilesheet_canvas_.points().front().y / 32) * 16);
554
555 // Render selected tile16 and cache tile metadata
558 current_tile16_info.begin());
559 }
560 }
561
562 // Use stateless bitmap rendering for tilesheet
563 gui::DrawBitmap(tilesheet_rt, tile16_blockset_.atlas, 1, 1, 2.0F, 255);
564
565 gui::EndCanvas(tilesheet_canvas_, tilesheet_rt, tilesheet_opts);
566 }
567
568 if (!tilesheet_canvas_.points().empty() &&
569 !screen_canvas_.points().empty()) {
573 }
574
575 ImGui::Separator();
576
577 // Current tile canvas with BeginCanvas/EndCanvas pattern
578 {
579 gui::CanvasFrameOptions current_tile_opts;
580 current_tile_opts.draw_grid = true;
581 current_tile_opts.grid_step = 16.0f;
582 current_tile_opts.render_popups = true;
583
584 auto current_tile_rt =
585 gui::BeginCanvas(current_tile_canvas_, current_tile_opts);
586
587 // Get tile8 from cache on-demand (only create texture when needed)
588 if (selected_tile8_ >= 0 && selected_tile8_ < 256) {
589 auto* cached_tile8 = tile8_tilemap_.tile_cache.GetTile(selected_tile8_);
590
591 if (!cached_tile8) {
592 // Extract tile from atlas and cache it
593 const int tiles_per_row =
594 tile8_tilemap_.atlas.width() / 8; // 128 / 8 = 16
595 const int tile_x = (selected_tile8_ % tiles_per_row) * 8;
596 const int tile_y = (selected_tile8_ / tiles_per_row) * 8;
597
598 // Extract 8x8 tile data from atlas
599 std::vector<uint8_t> tile_data(64);
600 for (int py = 0; py < 8; py++) {
601 for (int px = 0; px < 8; px++) {
602 int src_x = tile_x + px;
603 int src_y = tile_y + py;
604 int src_index = src_y * tile8_tilemap_.atlas.width() + src_x;
605 int dst_index = py * 8 + px;
606
607 if (src_index < tile8_tilemap_.atlas.size() && dst_index < 64) {
608 tile_data[dst_index] = tile8_tilemap_.atlas.data()[src_index];
609 }
610 }
611 }
612
613 gfx::Bitmap new_tile8(8, 8, 8, tile_data);
616 std::move(new_tile8));
618 }
619
620 if (cached_tile8 && cached_tile8->is_active()) {
621 // Create texture on-demand only when needed
622 if (!cached_tile8->texture()) {
625 }
626
627 // DrawTilePainter still uses member function (not yet migrated)
628 if (current_tile_canvas_.DrawTilePainter(*cached_tile8, 16)) {
629 // Modify the tile16 based on the selected tile and
630 // current_tile16_info
631 gfx::ModifyTile16(tile16_blockset_, game_data()->graphics_buffer,
634 212, selected_tile16_);
636 }
637 }
638 }
639
640 // Get selected tile from cache and draw with stateless helper
641 auto* selected_tile =
643 if (selected_tile && selected_tile->is_active()) {
644 // Ensure the selected tile has a valid texture
645 if (!selected_tile->texture()) {
648 }
649 gui::DrawBitmap(current_tile_rt, *selected_tile, 2, 2, 4.0f, 255);
650 }
651
652 gui::EndCanvas(current_tile_canvas_, current_tile_rt, current_tile_opts);
653 }
654
656 ImGui::SameLine();
659 ImGui::SameLine();
661
662 if (ImGui::Button("Modify Tile16")) {
663 gfx::ModifyTile16(tile16_blockset_, game_data()->graphics_buffer,
668 }
669 }
670 ImGui::EndChild();
671}
672
690 // Enhanced editing mode controls with visual feedback
691 if (gui::ToolbarIconButton(ICON_MD_DRAW, "Draw Mode")) {
693 }
694 ImGui::SameLine();
695 if (gui::ToolbarIconButton(ICON_MD_EDIT, "Edit Mode")) {
697 }
698 ImGui::SameLine();
699 if (gui::ToolbarIconButton(ICON_MD_SAVE, "Save dungeon map tiles")) {
701 }
702
703 static std::vector<std::string> dungeon_names = {
704 "Sewers/Sanctuary", "Hyrule Castle", "Eastern Palace",
705 "Desert Palace", "Tower of Hera", "Agahnim's Tower",
706 "Palace of Darkness", "Swamp Palace", "Skull Woods",
707 "Thieves' Town", "Ice Palace", "Misery Mire",
708 "Turtle Rock", "Ganon's Tower"};
709
710 if (ImGui::BeginTable("DungeonMapsTable", 4,
711 ImGuiTableFlags_Resizable |
712 ImGuiTableFlags_Reorderable |
713 ImGuiTableFlags_Hideable)) {
714 ImGui::TableSetupColumn("Dungeon");
715 ImGui::TableSetupColumn("Map");
716 ImGui::TableSetupColumn("Rooms Gfx");
717 ImGui::TableSetupColumn("Tiles Gfx");
718 ImGui::TableHeadersRow();
719
720 ImGui::TableNextColumn();
721 for (int i = 0; i < dungeon_names.size(); i++) {
723 selected_dungeon == i, "Dungeon Names", absl::StrFormat("%d", i),
724 dungeon_names[i]);
725 if (ImGui::IsItemClicked()) {
727 }
728 }
729
730 ImGui::TableNextColumn();
732
733 ImGui::TableNextColumn();
735
736 ImGui::TableNextColumn();
740 // Get the tile8 ID to use for the tile16 drawing above
742 }
746
747 ImGui::Text("Selected tile8: %d", selected_tile8_);
748 ImGui::Separator();
749 ImGui::Text("For use with custom inserted graphics assembly patches.");
750 if (ImGui::Button("Load GFX from BIN file"))
752
753 ImGui::EndTable();
754 }
755}
756
758 std::string bin_file = util::FileDialogWrapper::ShowOpenFileDialog();
759 if (!bin_file.empty()) {
760 std::ifstream file(bin_file, std::ios::binary);
761 if (file.is_open()) {
762 // Read the gfx data into a buffer
763 std::vector<uint8_t> bin_data((std::istreambuf_iterator<char>(file)),
764 std::istreambuf_iterator<char>());
765 if (auto converted_bin = gfx::SnesTo8bppSheet(bin_data, 4, 4);
767 converted_bin, true)
768 .ok()) {
769 sheets_.clear();
770 std::vector<std::vector<uint8_t>> gfx_sheets;
771 for (int i = 0; i < 4; i++) {
772 gfx_sheets.emplace_back(converted_bin.begin() + (i * 0x1000),
773 converted_bin.begin() + ((i + 1) * 0x1000));
774 sheets_[i] = std::make_unique<gfx::Bitmap>(128, 32, 8, gfx_sheets[i]);
775 sheets_[i]->SetPalette(
776 *game_data()->palette_groups.dungeon_main.mutable_palette(3));
777 // Queue texture creation via Arena's deferred system
780 }
781 binary_gfx_loaded_ = true;
782 } else {
783 status_ = absl::InternalError("Failed to load dungeon map tile16");
784 }
785 file.close();
786 }
787 }
788}
789
791 // Initialize title screen on first draw
792 if (!title_screen_loaded_ && rom()->is_loaded() && game_data()) {
794 if (!status_.ok()) {
795 const auto& theme = AgentUI::GetTheme();
796 ImGui::TextColored(theme.text_error_red, "Error loading title screen: %s",
797 status_.message().data());
798 return;
799 }
801 }
802
804 ImGui::Text("Title screen not loaded. Ensure ROM is loaded.");
805 return;
806 }
807
808 // Toolbar with mode controls
809 if (ImGui::Button(ICON_MD_DRAW)) {
811 }
812 ImGui::SameLine();
813 if (ImGui::Button(ICON_MD_SAVE)) {
815 if (status_.ok()) {
816 ImGui::OpenPopup("SaveSuccess");
817 }
818 }
819 ImGui::SameLine();
820 ImGui::Text("Selected Tile: %d", selected_title_tile16_);
821
822 // Save success popup
823 if (ImGui::BeginPopup("SaveSuccess")) {
824 ImGui::Text("Title screen saved successfully!");
825 ImGui::EndPopup();
826 }
827
828 // Layer visibility controls
829 bool prev_bg1 = show_title_bg1_;
830 bool prev_bg2 = show_title_bg2_;
831 ImGui::Checkbox("Show BG1", &show_title_bg1_);
832 ImGui::SameLine();
833 ImGui::Checkbox("Show BG2", &show_title_bg2_);
834
835 // Re-render composite if visibility changed
836 if (prev_bg1 != show_title_bg1_ || prev_bg2 != show_title_bg2_) {
837 status_ =
839 if (status_.ok()) {
843 }
844 }
845
846 // Layout: 2-column table (composite view + tile selector)
847 if (ImGui::BeginTable("TitleScreenTable", 2,
848 ImGuiTableFlags_Resizable | ImGuiTableFlags_Borders)) {
849 ImGui::TableSetupColumn("Title Screen (Composite)");
850 ImGui::TableSetupColumn("Tile Selector");
851 ImGui::TableHeadersRow();
852
853 // Column 1: Composite Canvas (BG1+BG2 stacked)
854 ImGui::TableNextColumn();
856
857 // Column 2: Blockset Selector
858 ImGui::TableNextColumn();
860
861 ImGui::EndTable();
862 }
863}
864
868
869 // Draw composite tilemap (BG1+BG2 stacked with transparency)
870 auto& composite_bitmap = title_screen_.composite_bitmap();
871 if (composite_bitmap.is_active()) {
872 title_bg1_canvas_.DrawBitmap(composite_bitmap, 0, 0, 2.0f, 255);
873 }
874
875 // Handle tile painting - always paint to BG1 layer
878 if (!title_bg1_canvas_.points().empty()) {
879 auto click_pos = title_bg1_canvas_.points().front();
880 int tile_x = static_cast<int>(click_pos.x) / 8;
881 int tile_y = static_cast<int>(click_pos.y) / 8;
882
883 if (tile_x >= 0 && tile_x < 32 && tile_y >= 0 && tile_y < 32) {
884 int tilemap_index = tile_y * 32 + tile_x;
885
886 // Create tile word: tile_id | (palette << 10) | h_flip | v_flip
887 uint16_t tile_word = selected_title_tile16_ & 0x3FF;
888 tile_word |= (title_palette_ & 0x07) << 10;
889 if (title_h_flip_)
890 tile_word |= 0x4000;
891 if (title_v_flip_)
892 tile_word |= 0x8000;
893
894 // Update BG1 buffer and re-render both layers and composite
895 title_screen_.mutable_bg1_buffer()[tilemap_index] = tile_word;
897 if (status_.ok()) {
898 // Update BG1 texture
902
903 // Re-render and update composite
906 if (status_.ok()) {
908 gfx::Arena::TextureCommandType::UPDATE, &composite_bitmap);
909 }
910 }
911 }
912 }
913 }
914 }
915
918}
919
923
924 // Draw BG1 tilemap
925 auto& bg1_bitmap = title_screen_.bg1_bitmap();
926 if (bg1_bitmap.is_active()) {
927 title_bg1_canvas_.DrawBitmap(bg1_bitmap, 0, 0, 2.0f, 255);
928 }
929
930 // Handle tile painting
933 if (!title_bg1_canvas_.points().empty()) {
934 auto click_pos = title_bg1_canvas_.points().front();
935 int tile_x = static_cast<int>(click_pos.x) / 8;
936 int tile_y = static_cast<int>(click_pos.y) / 8;
937
938 if (tile_x >= 0 && tile_x < 32 && tile_y >= 0 && tile_y < 32) {
939 int tilemap_index = tile_y * 32 + tile_x;
940
941 // Create tile word: tile_id | (palette << 10) | h_flip | v_flip
942 uint16_t tile_word = selected_title_tile16_ & 0x3FF;
943 tile_word |= (title_palette_ & 0x07) << 10;
944 if (title_h_flip_)
945 tile_word |= 0x4000;
946 if (title_v_flip_)
947 tile_word |= 0x8000;
948
949 // Update buffer and re-render
950 title_screen_.mutable_bg1_buffer()[tilemap_index] = tile_word;
952 if (status_.ok()) {
955 }
956 }
957 }
958 }
959 }
960
963}
964
968
969 // Draw BG2 tilemap
970 auto& bg2_bitmap = title_screen_.bg2_bitmap();
971 if (bg2_bitmap.is_active()) {
972 title_bg2_canvas_.DrawBitmap(bg2_bitmap, 0, 0, 2.0f, 255);
973 }
974
975 // Handle tile painting
978 if (!title_bg2_canvas_.points().empty()) {
979 auto click_pos = title_bg2_canvas_.points().front();
980 int tile_x = static_cast<int>(click_pos.x) / 8;
981 int tile_y = static_cast<int>(click_pos.y) / 8;
982
983 if (tile_x >= 0 && tile_x < 32 && tile_y >= 0 && tile_y < 32) {
984 int tilemap_index = tile_y * 32 + tile_x;
985
986 // Create tile word: tile_id | (palette << 10) | h_flip | v_flip
987 uint16_t tile_word = selected_title_tile16_ & 0x3FF;
988 tile_word |= (title_palette_ & 0x07) << 10;
989 if (title_h_flip_)
990 tile_word |= 0x4000;
991 if (title_v_flip_)
992 tile_word |= 0x8000;
993
994 // Update buffer and re-render
995 title_screen_.mutable_bg2_buffer()[tilemap_index] = tile_word;
997 if (status_.ok()) {
1000 }
1001 }
1002 }
1003 }
1004 }
1005
1008}
1009
1013
1014 // Draw tile8 bitmap (8x8 tiles used to compose tile16)
1015 auto& tiles8_bitmap = title_screen_.tiles8_bitmap();
1016 if (tiles8_bitmap.is_active()) {
1017 title_blockset_canvas_.DrawBitmap(tiles8_bitmap, 0, 0, 2.0f, 255);
1018 }
1019
1020 // Handle tile selection (8x8 tiles)
1022 // Calculate selected tile ID from click position
1023 if (!title_blockset_canvas_.points().empty()) {
1024 auto click_pos = title_blockset_canvas_.points().front();
1025 int tile_x = static_cast<int>(click_pos.x) / 8;
1026 int tile_y = static_cast<int>(click_pos.y) / 8;
1027 int tiles_per_row = 128 / 8; // 16 tiles per row for 8x8 tiles
1028 selected_title_tile16_ = tile_x + (tile_y * tiles_per_row);
1029 }
1030 }
1031
1034
1035 // Show selected tile preview and controls
1036 if (selected_title_tile16_ >= 0) {
1037 ImGui::Text("Selected Tile: %d", selected_title_tile16_);
1038
1039 // Flip controls
1040 ImGui::Checkbox("H Flip", &title_h_flip_);
1041 ImGui::SameLine();
1042 ImGui::Checkbox("V Flip", &title_v_flip_);
1043
1044 // Palette selector (0-7 for 3BPP graphics)
1045 ImGui::SetNextItemWidth(100);
1046 ImGui::SliderInt("Palette", &title_palette_, 0, 7);
1047 }
1048}
1049
1051
1053 // Initialize overworld map on first draw
1054 if (!ow_map_loaded_ && rom()->is_loaded()) {
1056 if (!status_.ok()) {
1057 const auto& theme = AgentUI::GetTheme();
1058 ImGui::TextColored(theme.text_error_red,
1059 "Error loading overworld map: %s",
1060 status_.message().data());
1061 return;
1062 }
1063 ow_map_loaded_ = true;
1064 }
1065
1066 if (!ow_map_loaded_) {
1067 ImGui::Text("Overworld map not loaded. Ensure ROM is loaded.");
1068 return;
1069 }
1070
1071 // Toolbar with mode controls
1072 if (ImGui::Button(ICON_MD_DRAW)) {
1074 }
1075 ImGui::SameLine();
1076 if (ImGui::Button(ICON_MD_SAVE)) {
1078 if (status_.ok()) {
1079 ImGui::OpenPopup("OWSaveSuccess");
1080 }
1081 }
1082 ImGui::SameLine();
1083
1084 // World toggle
1085 if (ImGui::Button(ow_show_dark_world_ ? "Dark World" : "Light World")) {
1087 // Re-render map with new world
1089 if (status_.ok()) {
1092 }
1093 }
1094 ImGui::SameLine();
1095
1096 // Custom map load/save buttons
1097 if (ImGui::Button("Load Custom Map...")) {
1099 if (!path.empty()) {
1101 if (!status_.ok()) {
1102 ImGui::OpenPopup("CustomMapLoadError");
1103 }
1104 }
1105 }
1106 ImGui::SameLine();
1107 if (ImGui::Button("Save Custom Map...")) {
1109 if (!path.empty()) {
1111 if (status_.ok()) {
1112 ImGui::OpenPopup("CustomMapSaveSuccess");
1113 }
1114 }
1115 }
1116
1117 ImGui::SameLine();
1118 ImGui::Text("Selected Tile: %d", selected_ow_tile_);
1119
1120 // Custom map error/success popups
1121 if (ImGui::BeginPopup("CustomMapLoadError")) {
1122 ImGui::Text("Error loading custom map: %s", status_.message().data());
1123 ImGui::EndPopup();
1124 }
1125 if (ImGui::BeginPopup("CustomMapSaveSuccess")) {
1126 ImGui::Text("Custom map saved successfully!");
1127 ImGui::EndPopup();
1128 }
1129
1130 // Save success popup
1131 if (ImGui::BeginPopup("OWSaveSuccess")) {
1132 ImGui::Text("Overworld map saved successfully!");
1133 ImGui::EndPopup();
1134 }
1135
1136 // Layout: 3-column table
1137 if (ImGui::BeginTable("OWMapTable", 3,
1138 ImGuiTableFlags_Resizable | ImGuiTableFlags_Borders)) {
1139 ImGui::TableSetupColumn("Map Canvas");
1140 ImGui::TableSetupColumn("Tileset");
1141 ImGui::TableSetupColumn("Palette");
1142 ImGui::TableHeadersRow();
1143
1144 // Column 1: Map Canvas
1145 ImGui::TableNextColumn();
1148
1149 auto& map_bitmap = ow_map_screen_.map_bitmap();
1150 if (map_bitmap.is_active()) {
1151 ow_map_canvas_.DrawBitmap(map_bitmap, 0, 0, 1.0f, 255);
1152 }
1153
1154 // Handle tile painting
1156 if (ow_map_canvas_.DrawTileSelector(8.0f)) {
1157 if (!ow_map_canvas_.points().empty()) {
1158 auto click_pos = ow_map_canvas_.points().front();
1159 int tile_x = static_cast<int>(click_pos.x) / 8;
1160 int tile_y = static_cast<int>(click_pos.y) / 8;
1161
1162 if (tile_x >= 0 && tile_x < 64 && tile_y >= 0 && tile_y < 64) {
1163 int tile_index = tile_x + (tile_y * 64);
1164
1165 // Update appropriate world's tile data
1166 if (ow_show_dark_world_) {
1168 } else {
1170 }
1171
1172 // Re-render map
1174 if (status_.ok()) {
1177 }
1178 }
1179 }
1180 }
1181 }
1182
1185
1186 // Column 2: Tileset Selector
1187 ImGui::TableNextColumn();
1190
1191 auto& tiles8_bitmap = ow_map_screen_.tiles8_bitmap();
1192 if (tiles8_bitmap.is_active()) {
1193 ow_tileset_canvas_.DrawBitmap(tiles8_bitmap, 0, 0, 2.0f, 255);
1194 }
1195
1196 // Handle tile selection
1198 if (!ow_tileset_canvas_.points().empty()) {
1199 auto click_pos = ow_tileset_canvas_.points().front();
1200 int tile_x = static_cast<int>(click_pos.x) / 8;
1201 int tile_y = static_cast<int>(click_pos.y) / 8;
1202 selected_ow_tile_ = tile_x + (tile_y * 16); // 16 tiles per row
1203 }
1204 }
1205
1208
1209 // Column 3: Palette Display
1210 ImGui::TableNextColumn();
1213 // Use inline palette editor for full 128-color palette
1214 gui::InlinePaletteEditor(palette, "Overworld Map Palette");
1215
1216 ImGui::EndTable();
1217 }
1218}
1219
1221 static bool show_bg1 = true;
1222 static bool show_bg2 = true;
1223 static bool show_bg3 = true;
1224
1225 static bool drawing_bg1 = true;
1226 static bool drawing_bg2 = false;
1227 static bool drawing_bg3 = false;
1228
1229 ImGui::Checkbox("Show BG1", &show_bg1);
1230 ImGui::SameLine();
1231 ImGui::Checkbox("Show BG2", &show_bg2);
1232
1233 ImGui::Checkbox("Draw BG1", &drawing_bg1);
1234 ImGui::SameLine();
1235 ImGui::Checkbox("Draw BG2", &drawing_bg2);
1236 ImGui::SameLine();
1237 ImGui::Checkbox("Draw BG3", &drawing_bg3);
1238}
1239
1240} // namespace editor
1241} // namespace yaze
project::ResourceLabelManager * resource_label()
Definition rom.h:150
bool is_loaded() const
Definition rom.h:132
static Flags & get()
Definition features.h:118
zelda3::GameData * game_data() const
Definition editor.h:297
EditorDependencies dependencies_
Definition editor.h:306
void RegisterPanel(size_t session_id, const PanelDescriptor &base_info)
void DrawDungeonMapsRoomGfx()
Draw dungeon room graphics editor with enhanced tile16 editing.
std::array< gfx::TileInfo, 4 > current_tile16_info
absl::Status Save() override
absl::Status Load() override
absl::Status Update() override
zelda3::OverworldMapScreen ow_map_screen_
void DrawDungeonMapsEditor()
Draw dungeon maps editor with enhanced ROM hacking features.
zelda3::TitleScreen title_screen_
zelda3::Inventory inventory_
zelda3::DungeonMapLabels dungeon_map_labels_
std::vector< zelda3::DungeonMap > dungeon_maps_
void QueueTextureCommand(TextureCommandType type, Bitmap *bitmap)
Definition arena.cc:36
static Arena & Get()
Definition arena.cc:21
Represents a bitmap image optimized for SNES ROM hacking.
Definition bitmap.h:67
const uint8_t * data() const
Definition bitmap.h:377
const SnesPalette & palette() const
Definition bitmap.h:368
void Create(int width, int height, int depth, std::span< uint8_t > data)
Create a bitmap with the given dimensions and data.
Definition bitmap.cc:201
auto size() const
Definition bitmap.h:376
void set_data(const std::vector< uint8_t > &data)
Definition bitmap.cc:853
void SetPalette(const SnesPalette &palette)
Set the palette for the bitmap using SNES palette format.
Definition bitmap.cc:384
int width() const
Definition bitmap.h:373
void Get16x16Tile(int tile_x, int tile_y, std::vector< uint8_t > &tile_data, int &tile_data_offset)
Extract a 16x16 tile from the bitmap (SNES metatile size)
Definition bitmap.cc:692
RAII timer for automatic timing management.
void DrawBitmap(Bitmap &bitmap, int border_offset, float scale)
Definition canvas.cc:1157
void DrawOutlineWithColor(int x, int y, int w, int h, ImVec4 color)
Definition canvas.cc:1226
ImVector< ImVec2 > * mutable_points()
Definition canvas.h:440
void DrawContextMenu()
Definition canvas.cc:684
int GetTileIdFromMousePos()
Definition canvas.h:412
bool DrawTileSelector(int size, int size_y=0)
Definition canvas.cc:1093
bool DrawTilePainter(const Bitmap &bitmap, int size, float scale=1.0f)
Definition canvas.cc:934
bool IsMouseHovering() const
Definition canvas.h:433
void DrawBitmapTable(const BitmapTable &gfx_bin)
Definition canvas.cc:1204
void DrawBackground(ImVec2 canvas_size=ImVec2(0, 0))
Definition canvas.cc:590
const ImVector< ImVec2 > & points() const
Definition canvas.h:439
void DrawGrid(float grid_step=64.0f, int tile_id_offset=8)
Definition canvas.cc:1480
void DrawText(const std::string &text, int x, int y)
Definition canvas.cc:1428
static std::string ShowSaveFileDialog(const std::string &default_name="", const std::string &default_extension="")
ShowSaveFileDialog opens a save file dialog and returns the selected filepath. Uses global feature fl...
static std::string ShowOpenFileDialog()
ShowOpenFileDialog opens a file dialog and returns the selected filepath. Uses global feature flag to...
absl::Status Create(Rom *rom, GameData *game_data=nullptr)
Initialize and load inventory screen data from ROM.
Definition inventory.cc:14
absl::Status SaveCustomMap(const std::string &file_path, bool use_dark_world)
Save map data to external binary file.
absl::Status LoadCustomMap(const std::string &file_path)
Load custom map from external binary file.
absl::Status Save(Rom *rom)
Save changes back to ROM.
absl::Status RenderMapLayer(bool use_dark_world)
Render map tiles into bitmap.
absl::Status Create(Rom *rom)
Initialize and load overworld map data from ROM.
absl::Status Create(Rom *rom, GameData *game_data=nullptr)
Initialize and load title screen data from ROM.
absl::Status RenderCompositeLayer(bool show_bg1, bool show_bg2)
Render composite layer with BG1 on top of BG2 with transparency.
absl::Status Save(Rom *rom)
absl::Status RenderBG2Layer()
Render BG2 tilemap into bitmap pixels Converts tile IDs from tiles_bg2_buffer_ into pixel data.
absl::Status RenderBG1Layer()
Render BG1 tilemap into bitmap pixels Converts tile IDs from tiles_bg1_buffer_ into pixel data.
#define ICON_MD_TITLE
Definition icons.h:1990
#define ICON_MD_MORE_VERT
Definition icons.h:1243
#define ICON_MD_DRAW
Definition icons.h:625
#define ICON_MD_ZOOM_OUT
Definition icons.h:2196
#define ICON_MD_MAP
Definition icons.h:1173
#define ICON_MD_REDO
Definition icons.h:1570
#define ICON_MD_EDIT
Definition icons.h:645
#define ICON_MD_PUBLIC
Definition icons.h:1524
#define ICON_MD_INVENTORY
Definition icons.h:1011
#define ICON_MD_BUILD
Definition icons.h:328
#define ICON_MD_ZOOM_IN
Definition icons.h:2194
#define ICON_MD_SAVE
Definition icons.h:1644
#define ICON_MD_UNDO
Definition icons.h:2039
#define PRINT_IF_ERROR(expression)
Definition macro.h:28
#define ASSIGN_OR_RETURN(type_variable_name, expression)
Definition macro.h:62
const AgentUITheme & GetTheme()
void RenderTile16(IRenderer *renderer, Tilemap &tilemap, int tile_id)
Definition tilemap.cc:76
void ModifyTile16(Tilemap &tilemap, const std::vector< uint8_t > &data, const TileInfo &top_left, const TileInfo &top_right, const TileInfo &bottom_left, const TileInfo &bottom_right, int sheet_offset, int tile_id)
Definition tilemap.cc:223
void UpdateTile16(IRenderer *renderer, Tilemap &tilemap, int tile_id)
Definition tilemap.cc:115
std::vector< uint8_t > SnesTo8bppSheet(std::span< const uint8_t > sheet, int bpp, int num_sheets)
Definition snes_tile.cc:132
void EndCanvas(Canvas &canvas)
Definition canvas.cc:1591
bool InputHexWord(const char *label, uint16_t *data, float input_width, bool no_step)
Definition input.cc:344
bool DrawTileSelector(const CanvasRuntime &rt, int size, int size_y, ImVec2 *out_selected_pos)
Definition canvas.cc:2354
void BeginCanvas(Canvas &canvas, ImVec2 child_size)
Definition canvas.cc:1568
bool BeginThemedTabBar(const char *id, ImGuiTabBarFlags flags)
A stylized tab bar with "Mission Control" branding.
IMGUI_API absl::Status InlinePaletteEditor(gfx::SnesPalette &palette, const std::string &title, ImGuiColorEditFlags flags)
Full inline palette editor with color picker and copy options.
Definition color.cc:121
void EndThemedTabBar()
bool InputTileInfo(const char *label, gfx::TileInfo *tile_info)
Definition input.cc:559
IMGUI_API bool DisplayPalette(gfx::SnesPalette &palette, bool loaded)
Definition color.cc:238
bool ToolbarIconButton(const char *icon, const char *tooltip, bool is_active)
Convenience wrapper for toolbar-sized icon buttons.
void DrawBitmap(const CanvasRuntime &rt, gfx::Bitmap &bitmap, int border_offset, float scale)
Definition canvas.cc:2169
bool InputHexByte(const char *label, uint8_t *data, float input_width, bool no_step)
Definition input.cc:370
std::string HexByte(uint8_t byte, HexStringParams params)
Definition hex.cc:30
absl::Status LoadDungeonMapTile16(gfx::Tilemap &tile16_blockset, Rom &rom, GameData *game_data, const std::vector< uint8_t > &gfx_data, bool bin_mode)
Load the dungeon map tile16 from the ROM.
constexpr int kNumRooms
Definition dungeon_map.h:48
absl::Status SaveDungeonMapTile16(gfx::Tilemap &tile16_blockset, Rom &rom)
Save the dungeon map tile16 to the ROM.
absl::StatusOr< std::vector< DungeonMap > > LoadDungeonMaps(Rom &rom, DungeonMapLabels &dungeon_map_labels)
Load the dungeon maps from the ROM.
absl::Status SaveDungeonMaps(Rom &rom, std::vector< DungeonMap > &dungeon_maps)
Save the dungeon maps to the ROM.
#define RETURN_IF_ERROR(expr)
Definition snes.cc:22
void CacheTile(int tile_id, const Bitmap &bitmap)
Cache a tile bitmap by copying it.
Definition tilemap.h:67
Bitmap * GetTile(int tile_id)
Get a cached tile by ID.
Definition tilemap.h:50
Pair tile_size
Size of individual tiles (8x8 or 16x16)
Definition tilemap.h:123
TileCache tile_cache
Smart tile cache with LRU eviction.
Definition tilemap.h:120
Pair map_size
Size of tilemap in tiles.
Definition tilemap.h:124
Bitmap atlas
Master bitmap containing all tiles.
Definition tilemap.h:119
std::vector< std::array< gfx::TileInfo, 4 > > tile_info
Tile metadata (4 tiles per 16x16)
Definition tilemap.h:122
std::optional< float > grid_step
Definition canvas.h:70
void SelectableLabelWithNameEdit(bool selected, const std::string &type, const std::string &key, const std::string &defaultValue)
Definition project.cc:1941
gfx::PaletteGroupMap palette_groups
Definition game_data.h:89