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 <memory>
6#include <string>
7
8#include "absl/strings/str_format.h"
11#include "app/gfx/core/bitmap.h"
16#include "app/gui/core/color.h"
17#include "app/gui/core/icons.h"
18#include "app/gui/core/input.h"
20#include "imgui/imgui.h"
21#include "util/file_util.h"
22#include "util/hex.h"
23#include "util/macro.h"
24
25namespace yaze {
26namespace editor {
27
30 return;
31 auto* window_manager = dependencies_.window_manager;
32
33 window_manager->RegisterPanel(
34 {.card_id = "screen.dungeon_maps",
35 .display_name = "Dungeon Maps",
36 .window_title = " Dungeon Map Editor",
37 .icon = ICON_MD_MAP,
38 .category = "Screen",
39 .shortcut_hint = "Alt+1",
40 .priority = 10,
41 .enabled_condition = [this]() { return rom()->is_loaded(); },
42 .disabled_tooltip = "Load a ROM first"});
43 window_manager->RegisterPanel(
44 {.card_id = "screen.inventory_menu",
45 .display_name = "Inventory Menu",
46 .window_title = " Inventory Menu",
47 .icon = ICON_MD_INVENTORY,
48 .category = "Screen",
49 .shortcut_hint = "Alt+2",
50 .priority = 20,
51 .enabled_condition = [this]() { return rom()->is_loaded(); },
52 .disabled_tooltip = "Load a ROM first"});
53 window_manager->RegisterPanel(
54 {.card_id = "screen.overworld_map",
55 .display_name = "Overworld Map",
56 .window_title = " Overworld Map",
57 .icon = ICON_MD_PUBLIC,
58 .category = "Screen",
59 .shortcut_hint = "Alt+3",
60 .priority = 30,
61 .enabled_condition = [this]() { return rom()->is_loaded(); },
62 .disabled_tooltip = "Load a ROM first"});
63 window_manager->RegisterPanel(
64 {.card_id = "screen.title_screen",
65 .display_name = "Title Screen",
66 .window_title = " Title Screen",
67 .icon = ICON_MD_TITLE,
68 .category = "Screen",
69 .shortcut_hint = "Alt+4",
70 .priority = 40,
71 .enabled_condition = [this]() { return rom()->is_loaded(); },
72 .disabled_tooltip = "Load a ROM first"});
73 window_manager->RegisterPanel(
74 {.card_id = "screen.naming_screen",
75 .display_name = "Naming Screen",
76 .window_title = " Naming Screen",
77 .icon = ICON_MD_EDIT,
78 .category = "Screen",
79 .shortcut_hint = "Alt+5",
80 .priority = 50,
81 .enabled_condition = [this]() { return rom()->is_loaded(); },
82 .disabled_tooltip = "Load a ROM first"});
83
84 // Register WindowContent implementations
85 window_manager->RegisterWindowContent(std::make_unique<DungeonMapsPanel>(
86 [this]() { DrawDungeonMapsEditor(); }));
87 window_manager->RegisterWindowContent(std::make_unique<InventoryMenuPanel>(
88 [this]() { DrawInventoryMenuEditor(); }));
89 window_manager->RegisterWindowContent(std::make_unique<OverworldMapScreenPanel>(
90 [this]() { DrawOverworldMapEditor(); }));
91 window_manager->RegisterWindowContent(std::make_unique<TitleScreenPanel>(
92 [this]() { DrawTitleScreenEditor(); }));
93 window_manager->RegisterWindowContent(std::make_unique<NamingScreenPanel>(
94 [this]() { DrawNamingScreenEditor(); }));
95
96 // Show title screen by default
97 window_manager->OpenWindow("screen.title_screen");
98}
99
100absl::Status ScreenEditor::Load() {
101 gfx::ScopedTimer timer("ScreenEditor::Load");
102 inventory_loaded_ = false;
103
108 game_data()->graphics_buffer, false));
109
110 // Load graphics sheets and apply dungeon palette
111 sheets_[0] =
112 std::make_unique<gfx::Bitmap>(gfx::Arena::Get().gfx_sheets()[212]);
113 sheets_[1] =
114 std::make_unique<gfx::Bitmap>(gfx::Arena::Get().gfx_sheets()[213]);
115 sheets_[2] =
116 std::make_unique<gfx::Bitmap>(gfx::Arena::Get().gfx_sheets()[214]);
117 sheets_[3] =
118 std::make_unique<gfx::Bitmap>(gfx::Arena::Get().gfx_sheets()[215]);
119
120 // Apply dungeon palette to all sheets
121 for (int i = 0; i < 4; i++) {
122 sheets_[i]->SetPalette(
123 *game_data()->palette_groups.dungeon_main.mutable_palette(3));
126 }
127
128 // Create a single tilemap for tile8 graphics with on-demand texture creation
129 // Combine all 4 sheets (128x32 each) into one bitmap (128x128)
130 // This gives us 16 tiles per row × 16 rows = 256 tiles total
131 const int tile8_width = 128;
132 const int tile8_height = 128; // 4 sheets × 32 pixels each
133 std::vector<uint8_t> tile8_data(tile8_width * tile8_height);
134
135 // Copy data from all 4 sheets into the combined bitmap
136 for (int sheet_idx = 0; sheet_idx < 4; sheet_idx++) {
137 const auto& sheet = *sheets_[sheet_idx];
138 int dest_y_offset = sheet_idx * 32; // Each sheet is 32 pixels tall
139
140 for (int y = 0; y < 32; y++) {
141 for (int x = 0; x < 128; x++) {
142 int src_index = y * 128 + x;
143 int dest_index = (dest_y_offset + y) * 128 + x;
144
145 if (src_index < sheet.size() && dest_index < tile8_data.size()) {
146 tile8_data[dest_index] = sheet.data()[src_index];
147 }
148 }
149 }
150 }
151
152 // Create tilemap with 8x8 tile size
153 tile8_tilemap_.tile_size = {8, 8};
154 tile8_tilemap_.map_size = {256, 256}; // Logical size for tile count
155 tile8_tilemap_.atlas.Create(tile8_width, tile8_height, 8, tile8_data);
158
159 // Queue single texture creation for the atlas (not individual tiles)
162 return absl::OkStatus();
163}
164
165absl::Status ScreenEditor::Save() {
166 if (core::FeatureFlags::get().kSaveDungeonMaps) {
168 }
169 // Title screen and overworld maps are currently saved via their respective
170 // 'Save' buttons in the UI, but we could also trigger them here for a full
171 // save.
172 return absl::OkStatus();
173}
174
175absl::Status ScreenEditor::Update() {
176 // Panel drawing is handled centrally by WorkspaceWindowManager::DrawAllVisiblePanels()
177 // via the WindowContent implementations registered in Initialize().
178 // No local drawing needed here - this fixes duplicate panel rendering.
179 return status_;
180}
181
183 // Sidebar is now drawn by EditorManager for card-based editors
184 // This method kept for compatibility but sidebar handles card toggles
185}
186
188 if (!inventory_loaded_ && rom()->is_loaded() && game_data()) {
190 if (status_.ok()) {
192 inventory_loaded_ = true;
193 } else {
194 const auto& theme = AgentUI::GetTheme();
195 ImGui::TextColored(theme.text_error_red, "Error loading inventory: %s",
196 status_.message().data());
197 return;
198 }
199 }
200
202
203 if (ImGui::BeginTable("InventoryScreen", 4, ImGuiTableFlags_Resizable)) {
204 ImGui::TableSetupColumn("Canvas");
205 ImGui::TableSetupColumn("Tilesheet");
206 ImGui::TableSetupColumn("Item Icons");
207 ImGui::TableSetupColumn("Palette");
208 ImGui::TableHeadersRow();
209
210 ImGui::TableNextColumn();
211 {
212 gui::CanvasFrameOptions frame_opts;
213 frame_opts.draw_grid = true;
214 frame_opts.grid_step = 32.0f;
215 frame_opts.render_popups = true;
216 auto runtime = gui::BeginCanvas(screen_canvas_, frame_opts);
217 gui::DrawBitmap(runtime, inventory_.bitmap(), 2,
218 inventory_loaded_ ? 1.0f : 0.0f);
219 gui::EndCanvas(screen_canvas_, runtime, frame_opts);
220 }
221
222 ImGui::TableNextColumn();
223 {
224 gui::CanvasFrameOptions frame_opts;
225 frame_opts.canvas_size = ImVec2(128 * 2 + 2, (192 * 2) + 4);
226 frame_opts.draw_grid = true;
227 frame_opts.grid_step = 16.0f;
228 frame_opts.render_popups = true;
229 auto runtime = gui::BeginCanvas(tilesheet_canvas_, frame_opts);
231 inventory_loaded_ ? 1.0f : 0.0f);
232 gui::EndCanvas(tilesheet_canvas_, runtime, frame_opts);
233 }
234
235 ImGui::TableNextColumn();
237
238 ImGui::TableNextColumn();
240
241 ImGui::EndTable();
242 }
243 ImGui::Separator();
244
245 // TODO(scawful): Future Oracle of Secrets menu editor integration
246 // - Full inventory screen layout editor
247 // - Item slot assignment and positioning
248 // - Heart container and magic meter editor
249 // - Equipment display customization
250 // - A/B button equipment quick-select editor
251}
252
254 if (ImGui::BeginTable("InventoryToolset", 8, ImGuiTableFlags_SizingFixedFit,
255 ImVec2(0, 0))) {
256 ImGui::TableSetupColumn("#drawTool");
257 ImGui::TableSetupColumn("#sep1");
258 ImGui::TableSetupColumn("#zoomOut");
259 ImGui::TableSetupColumn("#zoomIN");
260 ImGui::TableSetupColumn("#sep2");
261 ImGui::TableSetupColumn("#bg2Tool");
262 ImGui::TableSetupColumn("#bg3Tool");
263 ImGui::TableSetupColumn("#itemTool");
264
265 ImGui::TableNextColumn();
266 ImGui::BeginDisabled(!undo_manager_.CanUndo());
268 status_ = Undo();
269 }
270 ImGui::EndDisabled();
271 ImGui::TableNextColumn();
272 ImGui::BeginDisabled(!undo_manager_.CanRedo());
274 status_ = Redo();
275 }
276 ImGui::EndDisabled();
277 ImGui::TableNextColumn();
278 ImGui::Text(ICON_MD_MORE_VERT);
279 ImGui::TableNextColumn();
280 if (gui::ToolbarIconButton(ICON_MD_ZOOM_OUT, "Zoom Out")) {
282 }
283 ImGui::TableNextColumn();
284 if (gui::ToolbarIconButton(ICON_MD_ZOOM_IN, "Zoom In")) {
286 }
287 ImGui::TableNextColumn();
288 ImGui::Text(ICON_MD_MORE_VERT);
289 ImGui::TableNextColumn();
290 if (gui::ToolbarIconButton(ICON_MD_DRAW, "Draw Mode")) {
292 }
293 ImGui::TableNextColumn();
294 if (gui::ToolbarIconButton(ICON_MD_BUILD, "Build Mode")) {
295 // current_mode_ = EditingMode::BUILD;
296 }
297
298 ImGui::EndTable();
299 }
300}
301
303 if (ImGui::BeginChild("##ItemIconsList", ImVec2(0, 0), true,
304 ImGuiWindowFlags_HorizontalScrollbar)) {
305 ImGui::Text("Item Icons (2x2 tiles each)");
306 ImGui::Separator();
307
308 auto& icons = inventory_.item_icons();
309 if (icons.empty()) {
310 ImGui::TextWrapped(
311 "No item icons loaded. Icons will be loaded when the "
312 "inventory is initialized.");
313 ImGui::EndChild();
314 return;
315 }
316
317 // Display icons in a table format
318 if (ImGui::BeginTable("##IconsTable", 2,
319 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
320 ImGui::TableSetupColumn("Icon Name");
321 ImGui::TableSetupColumn("Tile Data");
322 ImGui::TableHeadersRow();
323
324 for (size_t i = 0; i < icons.size(); i++) {
325 const auto& icon = icons[i];
326
327 ImGui::TableNextRow();
328 ImGui::TableNextColumn();
329
330 // Display icon name with selectable row
331 if (ImGui::Selectable(icon.name.c_str(), false,
332 ImGuiSelectableFlags_SpanAllColumns)) {
333 // TODO: Select this icon for editing
334 }
335
336 ImGui::TableNextColumn();
337 // Display tile word data in hex format
338 ImGui::Text("TL:%04X TR:%04X", icon.tile_tl, icon.tile_tr);
339 ImGui::SameLine();
340 ImGui::Text("BL:%04X BR:%04X", icon.tile_bl, icon.tile_br);
341 }
342
343 ImGui::EndTable();
344 }
345
346 ImGui::Separator();
347 ImGui::TextWrapped(
348 "NOTE: Individual icon editing will be implemented in the future "
349 "Oracle of Secrets menu editor. Each icon is composed of 4 tile words "
350 "representing a 2x2 arrangement of 8x8 tiles in SNES tile format "
351 "(vhopppcc cccccccc).");
352 }
353 ImGui::EndChild();
354}
355
357 gfx::ScopedTimer timer("screen_editor_draw_dungeon_map_screen");
358
359 const auto& theme = AgentUI::GetTheme();
360 auto& current_dungeon = dungeon_maps_[selected_dungeon];
361
362 floor_number = i;
363 screen_canvas_.DrawBackground(ImVec2(325, 325));
365
366 auto boss_room = current_dungeon.boss_room;
367
368 // Pre-allocate vectors for batch operations
369 std::vector<int> tile_ids_to_render;
370 std::vector<ImVec2> tile_positions;
371 tile_ids_to_render.reserve(zelda3::kNumRooms);
372 tile_positions.reserve(zelda3::kNumRooms);
373
374 for (int j = 0; j < zelda3::kNumRooms; j++) {
375 if (current_dungeon.floor_rooms[floor_number][j] != 0x0F) {
376 int tile16_id = current_dungeon.floor_gfx[floor_number][j];
377 int posX = ((j % 5) * 32);
378 int posY = ((j / 5) * 32);
379
380 // Batch tile rendering
381 tile_ids_to_render.push_back(tile16_id);
382 tile_positions.emplace_back(posX * 2, posY * 2);
383 }
384 }
385
386 // Batch render all tiles
387 for (size_t idx = 0; idx < tile_ids_to_render.size(); ++idx) {
388 int tile16_id = tile_ids_to_render[idx];
389 ImVec2 pos = tile_positions[idx];
390
391 // Extract tile data from the atlas directly
392 const int tiles_per_row = tile16_blockset_.atlas.width() / 16;
393 const int tile_x = (tile16_id % tiles_per_row) * 16;
394 const int tile_y = (tile16_id / tiles_per_row) * 16;
395
396 std::vector<uint8_t> tile_data(16 * 16);
397 int tile_data_offset = 0;
398 tile16_blockset_.atlas.Get16x16Tile(tile_x, tile_y, tile_data,
399 tile_data_offset);
400
401 // Create or update cached tile
402 auto* cached_tile = tile16_blockset_.tile_cache.GetTile(tile16_id);
403 if (!cached_tile) {
404 // Create new cached tile
405 gfx::Bitmap new_tile(16, 16, 8, tile_data);
407 tile16_blockset_.tile_cache.CacheTile(tile16_id, std::move(new_tile));
408 cached_tile = tile16_blockset_.tile_cache.GetTile(tile16_id);
409 } else {
410 // Update existing cached tile data
411 cached_tile->set_data(tile_data);
412 }
413
414 if (cached_tile && cached_tile->is_active()) {
415 // Ensure the cached tile has a valid texture
416 if (!cached_tile->texture()) {
417 // Queue texture creation via Arena's deferred system
420 }
421 screen_canvas_.DrawBitmap(*cached_tile, pos.x, pos.y, 4.0F, 255);
422 }
423 }
424
425 // Draw overlays and labels
426 for (int j = 0; j < zelda3::kNumRooms; j++) {
427 if (current_dungeon.floor_rooms[floor_number][j] != 0x0F) {
428 int posX = ((j % 5) * 32);
429 int posY = ((j / 5) * 32);
430
431 if (current_dungeon.floor_rooms[floor_number][j] == boss_room) {
432 screen_canvas_.DrawOutlineWithColor((posX * 2), (posY * 2), 64, 64,
433 theme.status_error);
434 }
435
436 std::string label =
438 screen_canvas_.DrawText(label, (posX * 2), (posY * 2));
439 std::string gfx_id =
440 util::HexByte(current_dungeon.floor_gfx[floor_number][j]);
441 screen_canvas_.DrawText(gfx_id, (posX * 2), (posY * 2) + 16);
442 }
443 }
444
445 screen_canvas_.DrawGrid(64.f, 5);
447
448 if (!screen_canvas_.points().empty()) {
449 int x = screen_canvas_.points().front().x / 64;
450 int y = screen_canvas_.points().front().y / 64;
451 selected_room = x + (y * 5);
452 }
453}
454
456 auto& current_dungeon = dungeon_maps_[selected_dungeon];
457 if (gui::BeginThemedTabBar("##DungeonMapTabs")) {
458 auto nbr_floors =
459 current_dungeon.nbr_of_floor + current_dungeon.nbr_of_basement;
460 for (int i = 0; i < nbr_floors; i++) {
461 int basement_num = current_dungeon.nbr_of_basement - i;
462 std::string tab_name = absl::StrFormat("Basement %d", basement_num);
463 if (i >= current_dungeon.nbr_of_basement) {
464 tab_name = absl::StrFormat("Floor %d",
465 i - current_dungeon.nbr_of_basement + 1);
466 }
467 if (ImGui::BeginTabItem(tab_name.data())) {
469 ImGui::EndTabItem();
470 }
471 }
473 }
474
475 {
476 auto room_before = CaptureDungeonMapSnapshot();
478 "Selected Room",
479 &current_dungeon.floor_rooms[floor_number].at(selected_room))) {
480 auto after = CaptureDungeonMapSnapshot();
481 undo_manager_.Push(std::make_unique<ScreenEditAction>(
482 room_before, after,
483 [this](const ScreenSnapshot& s) { RestoreFromSnapshot(s); },
484 "Edit room assignment"));
485 }
486 }
487
488 {
489 auto boss_before = CaptureDungeonMapSnapshot();
490 if (gui::InputHexWord("Boss Room", &current_dungeon.boss_room)) {
491 auto after = CaptureDungeonMapSnapshot();
492 undo_manager_.Push(std::make_unique<ScreenEditAction>(
493 boss_before, after,
494 [this](const ScreenSnapshot& s) { RestoreFromSnapshot(s); },
495 "Edit boss room"));
496 }
497 }
498
499 const auto button_size = ImVec2(130, 0);
500
501 if (ImGui::Button("Add Floor", button_size) &&
502 current_dungeon.nbr_of_floor < 8) {
503 SaveDungeonMapUndoState("Add floor");
504 current_dungeon.nbr_of_floor++;
507 }
508 ImGui::SameLine();
509 if (ImGui::Button("Remove Floor", button_size) &&
510 current_dungeon.nbr_of_floor > 0) {
511 SaveDungeonMapUndoState("Remove floor");
512 current_dungeon.nbr_of_floor--;
515 }
516
517 if (ImGui::Button("Add Basement", button_size) &&
518 current_dungeon.nbr_of_basement < 8) {
519 SaveDungeonMapUndoState("Add basement");
520 current_dungeon.nbr_of_basement++;
523 }
524 ImGui::SameLine();
525 if (ImGui::Button("Remove Basement", button_size) &&
526 current_dungeon.nbr_of_basement > 0) {
527 SaveDungeonMapUndoState("Remove basement");
528 current_dungeon.nbr_of_basement--;
531 }
532
533 if (ImGui::Button("Copy Floor", button_size)) {
534 copy_button_pressed = true;
535 }
536 ImGui::SameLine();
537 if (ImGui::Button("Paste Floor", button_size)) {
539 }
540}
541
559 gfx::ScopedTimer timer("screen_editor_draw_dungeon_maps_room_gfx");
560
561 if (ImGui::BeginChild("##DungeonMapTiles", ImVec2(0, 0), true)) {
562 // Enhanced tilesheet canvas with BeginCanvas/EndCanvas pattern
563 {
564 gui::CanvasFrameOptions tilesheet_opts;
565 tilesheet_opts.canvas_size = ImVec2((256 * 2) + 2, (192 * 2) + 4);
566 tilesheet_opts.draw_grid = true;
567 tilesheet_opts.grid_step = 32.0f;
568 tilesheet_opts.render_popups = true;
569
570 auto tilesheet_rt = gui::BeginCanvas(tilesheet_canvas_, tilesheet_opts);
571
572 // Interactive tile16 selector with grid snapping
573 ImVec2 selected_pos;
574 if (gui::DrawTileSelector(tilesheet_rt, 32, 0, &selected_pos)) {
575 // Double-click detected - handle tile confirmation if needed
576 }
577
578 // Check for single-click selection (legacy compatibility)
580 ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
581 if (!tilesheet_canvas_.points().empty()) {
582 selected_tile16_ = static_cast<int>(
583 tilesheet_canvas_.points().front().x / 32 +
584 (tilesheet_canvas_.points().front().y / 32) * 16);
585
586 // Render selected tile16 and cache tile metadata
589 current_tile16_info.begin());
590 }
591 }
592
593 // Use stateless bitmap rendering for tilesheet
594 gui::DrawBitmap(tilesheet_rt, tile16_blockset_.atlas, 1, 1, 2.0F, 255);
595
596 gui::EndCanvas(tilesheet_canvas_, tilesheet_rt, tilesheet_opts);
597 }
598
599 if (!tilesheet_canvas_.points().empty() &&
600 !screen_canvas_.points().empty()) {
601 SaveDungeonMapUndoState("Place tile on dungeon map");
606 }
607
608 ImGui::Separator();
609
610 // Current tile canvas with BeginCanvas/EndCanvas pattern
611 {
612 gui::CanvasFrameOptions current_tile_opts;
613 current_tile_opts.draw_grid = true;
614 current_tile_opts.grid_step = 16.0f;
615 current_tile_opts.render_popups = true;
616
617 auto current_tile_rt =
618 gui::BeginCanvas(current_tile_canvas_, current_tile_opts);
619
620 // Get tile8 from cache on-demand (only create texture when needed)
621 if (selected_tile8_ >= 0 && selected_tile8_ < 256) {
622 auto* cached_tile8 = tile8_tilemap_.tile_cache.GetTile(selected_tile8_);
623
624 if (!cached_tile8) {
625 // Extract tile from atlas and cache it
626 const int tiles_per_row =
627 tile8_tilemap_.atlas.width() / 8; // 128 / 8 = 16
628 const int tile_x = (selected_tile8_ % tiles_per_row) * 8;
629 const int tile_y = (selected_tile8_ / tiles_per_row) * 8;
630
631 // Extract 8x8 tile data from atlas
632 std::vector<uint8_t> tile_data(64);
633 for (int py = 0; py < 8; py++) {
634 for (int px = 0; px < 8; px++) {
635 int src_x = tile_x + px;
636 int src_y = tile_y + py;
637 int src_index = src_y * tile8_tilemap_.atlas.width() + src_x;
638 int dst_index = py * 8 + px;
639
640 if (src_index < tile8_tilemap_.atlas.size() && dst_index < 64) {
641 tile_data[dst_index] = tile8_tilemap_.atlas.data()[src_index];
642 }
643 }
644 }
645
646 gfx::Bitmap new_tile8(8, 8, 8, tile_data);
649 std::move(new_tile8));
651 }
652
653 if (cached_tile8 && cached_tile8->is_active()) {
654 // Create texture on-demand only when needed
655 if (!cached_tile8->texture()) {
658 }
659
660 // DrawTilePainter still uses member function (not yet migrated)
661 if (current_tile_canvas_.DrawTilePainter(*cached_tile8, 16)) {
662 // Modify the tile16 based on the selected tile and
663 // current_tile16_info
665 absl::StrFormat("Paint tile16 #%d", selected_tile16_));
666 gfx::ModifyTile16(tile16_blockset_, game_data()->graphics_buffer,
669 212, selected_tile16_);
672 }
673 }
674 }
675
676 // Get selected tile from cache and draw with stateless helper
677 auto* selected_tile =
679 if (selected_tile && selected_tile->is_active()) {
680 // Ensure the selected tile has a valid texture
681 if (!selected_tile->texture()) {
684 }
685 gui::DrawBitmap(current_tile_rt, *selected_tile, 2, 2, 4.0f, 255);
686 }
687
688 gui::EndCanvas(current_tile_canvas_, current_tile_rt, current_tile_opts);
689 }
690
692 ImGui::SameLine();
695 ImGui::SameLine();
697
698 if (ImGui::Button("Modify Tile16")) {
700 absl::StrFormat("Modify tile16 #%d", selected_tile16_));
701 gfx::ModifyTile16(tile16_blockset_, game_data()->graphics_buffer,
707 }
708 }
709 ImGui::EndChild();
710}
711
729 // Enhanced editing mode controls with visual feedback
730 if (gui::ToolbarIconButton(ICON_MD_DRAW, "Draw Mode")) {
732 }
733 ImGui::SameLine();
734 if (gui::ToolbarIconButton(ICON_MD_EDIT, "Edit Mode")) {
736 }
737 ImGui::SameLine();
738 if (gui::ToolbarIconButton(ICON_MD_SAVE, "Save dungeon map tiles")) {
740 }
741
742 static std::vector<std::string> dungeon_names = {
743 "Sewers/Sanctuary", "Hyrule Castle", "Eastern Palace",
744 "Desert Palace", "Tower of Hera", "Agahnim's Tower",
745 "Palace of Darkness", "Swamp Palace", "Skull Woods",
746 "Thieves' Town", "Ice Palace", "Misery Mire",
747 "Turtle Rock", "Ganon's Tower"};
748
749 if (ImGui::BeginTable("DungeonMapsTable", 4,
750 ImGuiTableFlags_Resizable |
751 ImGuiTableFlags_Reorderable |
752 ImGuiTableFlags_Hideable)) {
753 ImGui::TableSetupColumn("Dungeon");
754 ImGui::TableSetupColumn("Map");
755 ImGui::TableSetupColumn("Rooms Gfx");
756 ImGui::TableSetupColumn("Tiles Gfx");
757 ImGui::TableHeadersRow();
758
759 ImGui::TableNextColumn();
760 for (int i = 0; i < dungeon_names.size(); i++) {
762 selected_dungeon == i, "Dungeon Names", absl::StrFormat("%d", i),
763 dungeon_names[i]);
764 if (ImGui::IsItemClicked()) {
766 }
767 }
768
769 ImGui::TableNextColumn();
771
772 ImGui::TableNextColumn();
774
775 ImGui::TableNextColumn();
779 // Get the tile8 ID to use for the tile16 drawing above
781 }
785
786 ImGui::Text("Selected tile8: %d", selected_tile8_);
787 ImGui::Separator();
788 ImGui::Text("For use with custom inserted graphics assembly patches.");
789 if (ImGui::Button("Load GFX from BIN file"))
791
792 ImGui::EndTable();
793 }
794}
795
797 std::string bin_file = util::FileDialogWrapper::ShowOpenFileDialog();
798 if (!bin_file.empty()) {
799 std::ifstream file(bin_file, std::ios::binary);
800 if (file.is_open()) {
801 // Read the gfx data into a buffer
802 std::vector<uint8_t> bin_data((std::istreambuf_iterator<char>(file)),
803 std::istreambuf_iterator<char>());
804 if (auto converted_bin = gfx::SnesTo8bppSheet(bin_data, 4, 4);
806 converted_bin, true)
807 .ok()) {
808 sheets_.clear();
809 std::vector<std::vector<uint8_t>> gfx_sheets;
810 for (int i = 0; i < 4; i++) {
811 gfx_sheets.emplace_back(converted_bin.begin() + (i * 0x1000),
812 converted_bin.begin() + ((i + 1) * 0x1000));
813 sheets_[i] = std::make_unique<gfx::Bitmap>(128, 32, 8, gfx_sheets[i]);
814 sheets_[i]->SetPalette(
815 *game_data()->palette_groups.dungeon_main.mutable_palette(3));
816 // Queue texture creation via Arena's deferred system
819 }
820 binary_gfx_loaded_ = true;
821 } else {
822 status_ = absl::InternalError("Failed to load dungeon map tile16");
823 }
824 file.close();
825 }
826 }
827}
828
830 // Initialize title screen on first draw
831 if (!title_screen_loaded_ && rom()->is_loaded() && game_data()) {
833 if (!status_.ok()) {
834 const auto& theme = AgentUI::GetTheme();
835 ImGui::TextColored(theme.text_error_red, "Error loading title screen: %s",
836 status_.message().data());
837 return;
838 }
840 }
841
843 ImGui::Text("Title screen not loaded. Ensure ROM is loaded.");
844 return;
845 }
846
847 // Toolbar with mode controls
848 if (ImGui::Button(ICON_MD_DRAW)) {
850 }
851 ImGui::SameLine();
852 if (ImGui::Button(ICON_MD_SAVE)) {
854 if (status_.ok()) {
855 ImGui::OpenPopup("SaveSuccess");
856 }
857 }
858 ImGui::SameLine();
859 ImGui::Text("Selected Tile: %d", selected_title_tile16_);
860
861 // Save success popup
862 if (ImGui::BeginPopup("SaveSuccess")) {
863 ImGui::Text("Title screen saved successfully!");
864 ImGui::EndPopup();
865 }
866
867 // Layer visibility controls
868 bool prev_bg1 = show_title_bg1_;
869 bool prev_bg2 = show_title_bg2_;
870 ImGui::Checkbox("Show BG1", &show_title_bg1_);
871 ImGui::SameLine();
872 ImGui::Checkbox("Show BG2", &show_title_bg2_);
873
874 // Re-render composite if visibility changed
875 if (prev_bg1 != show_title_bg1_ || prev_bg2 != show_title_bg2_) {
876 status_ =
878 if (status_.ok()) {
882 }
883 }
884
885 // Layout: 2-column table (composite view + tile selector)
886 if (ImGui::BeginTable("TitleScreenTable", 2,
887 ImGuiTableFlags_Resizable | ImGuiTableFlags_Borders)) {
888 ImGui::TableSetupColumn("Title Screen (Composite)");
889 ImGui::TableSetupColumn("Tile Selector");
890 ImGui::TableHeadersRow();
891
892 // Column 1: Composite Canvas (BG1+BG2 stacked)
893 ImGui::TableNextColumn();
895
896 // Column 2: Blockset Selector
897 ImGui::TableNextColumn();
899
900 ImGui::EndTable();
901 }
902}
903
907
908 // Draw composite tilemap (BG1+BG2 stacked with transparency)
909 auto& composite_bitmap = title_screen_.composite_bitmap();
910 if (composite_bitmap.is_active()) {
911 title_bg1_canvas_.DrawBitmap(composite_bitmap, 0, 0, 2.0f, 255);
912 }
913
914 // Handle tile painting - always paint to BG1 layer
917 if (!title_bg1_canvas_.points().empty()) {
918 auto click_pos = title_bg1_canvas_.points().front();
919 int tile_x = static_cast<int>(click_pos.x) / 8;
920 int tile_y = static_cast<int>(click_pos.y) / 8;
921
922 if (tile_x >= 0 && tile_x < 32 && tile_y >= 0 && tile_y < 32) {
923 int tilemap_index = tile_y * 32 + tile_x;
924
925 // Create tile word: tile_id | (palette << 10) | h_flip | v_flip
926 uint16_t tile_word = selected_title_tile16_ & 0x3FF;
927 tile_word |= (title_palette_ & 0x07) << 10;
928 if (title_h_flip_)
929 tile_word |= 0x4000;
930 if (title_v_flip_)
931 tile_word |= 0x8000;
932
933 // Update BG1 buffer and re-render both layers and composite
934 title_screen_.mutable_bg1_buffer()[tilemap_index] = tile_word;
936 if (status_.ok()) {
937 // Update BG1 texture
941
942 // Re-render and update composite
945 if (status_.ok()) {
947 gfx::Arena::TextureCommandType::UPDATE, &composite_bitmap);
948 }
949 }
950 }
951 }
952 }
953 }
954
957}
958
962
963 // Draw BG1 tilemap
964 auto& bg1_bitmap = title_screen_.bg1_bitmap();
965 if (bg1_bitmap.is_active()) {
966 title_bg1_canvas_.DrawBitmap(bg1_bitmap, 0, 0, 2.0f, 255);
967 }
968
969 // Handle tile painting
972 if (!title_bg1_canvas_.points().empty()) {
973 auto click_pos = title_bg1_canvas_.points().front();
974 int tile_x = static_cast<int>(click_pos.x) / 8;
975 int tile_y = static_cast<int>(click_pos.y) / 8;
976
977 if (tile_x >= 0 && tile_x < 32 && tile_y >= 0 && tile_y < 32) {
978 int tilemap_index = tile_y * 32 + tile_x;
979
980 // Create tile word: tile_id | (palette << 10) | h_flip | v_flip
981 uint16_t tile_word = selected_title_tile16_ & 0x3FF;
982 tile_word |= (title_palette_ & 0x07) << 10;
983 if (title_h_flip_)
984 tile_word |= 0x4000;
985 if (title_v_flip_)
986 tile_word |= 0x8000;
987
988 // Update buffer and re-render
989 title_screen_.mutable_bg1_buffer()[tilemap_index] = tile_word;
991 if (status_.ok()) {
994 }
995 }
996 }
997 }
998 }
999
1002}
1003
1007
1008 // Draw BG2 tilemap
1009 auto& bg2_bitmap = title_screen_.bg2_bitmap();
1010 if (bg2_bitmap.is_active()) {
1011 title_bg2_canvas_.DrawBitmap(bg2_bitmap, 0, 0, 2.0f, 255);
1012 }
1013
1014 // Handle tile painting
1017 if (!title_bg2_canvas_.points().empty()) {
1018 auto click_pos = title_bg2_canvas_.points().front();
1019 int tile_x = static_cast<int>(click_pos.x) / 8;
1020 int tile_y = static_cast<int>(click_pos.y) / 8;
1021
1022 if (tile_x >= 0 && tile_x < 32 && tile_y >= 0 && tile_y < 32) {
1023 int tilemap_index = tile_y * 32 + tile_x;
1024
1025 // Create tile word: tile_id | (palette << 10) | h_flip | v_flip
1026 uint16_t tile_word = selected_title_tile16_ & 0x3FF;
1027 tile_word |= (title_palette_ & 0x07) << 10;
1028 if (title_h_flip_)
1029 tile_word |= 0x4000;
1030 if (title_v_flip_)
1031 tile_word |= 0x8000;
1032
1033 // Update buffer and re-render
1034 title_screen_.mutable_bg2_buffer()[tilemap_index] = tile_word;
1036 if (status_.ok()) {
1039 }
1040 }
1041 }
1042 }
1043 }
1044
1047}
1048
1052
1053 // Draw tile8 bitmap (8x8 tiles used to compose tile16)
1054 auto& tiles8_bitmap = title_screen_.tiles8_bitmap();
1055 if (tiles8_bitmap.is_active()) {
1056 title_blockset_canvas_.DrawBitmap(tiles8_bitmap, 0, 0, 2.0f, 255);
1057 }
1058
1059 // Handle tile selection (8x8 tiles)
1061 // Calculate selected tile ID from click position
1062 if (!title_blockset_canvas_.points().empty()) {
1063 auto click_pos = title_blockset_canvas_.points().front();
1064 int tile_x = static_cast<int>(click_pos.x) / 8;
1065 int tile_y = static_cast<int>(click_pos.y) / 8;
1066 int tiles_per_row = 128 / 8; // 16 tiles per row for 8x8 tiles
1067 selected_title_tile16_ = tile_x + (tile_y * tiles_per_row);
1068 }
1069 }
1070
1073
1074 // Show selected tile preview and controls
1075 if (selected_title_tile16_ >= 0) {
1076 ImGui::Text("Selected Tile: %d", selected_title_tile16_);
1077
1078 // Flip controls
1079 ImGui::Checkbox("H Flip", &title_h_flip_);
1080 ImGui::SameLine();
1081 ImGui::Checkbox("V Flip", &title_v_flip_);
1082
1083 // Palette selector (0-7 for 3BPP graphics)
1084 ImGui::SetNextItemWidth(100);
1085 ImGui::SliderInt("Palette", &title_palette_, 0, 7);
1086 }
1087}
1088
1090
1092 // Initialize overworld map on first draw
1093 if (!ow_map_loaded_ && rom()->is_loaded()) {
1095 if (!status_.ok()) {
1096 const auto& theme = AgentUI::GetTheme();
1097 ImGui::TextColored(theme.text_error_red,
1098 "Error loading overworld map: %s",
1099 status_.message().data());
1100 return;
1101 }
1102 ow_map_loaded_ = true;
1103 }
1104
1105 if (!ow_map_loaded_) {
1106 ImGui::Text("Overworld map not loaded. Ensure ROM is loaded.");
1107 return;
1108 }
1109
1110 // Toolbar with mode controls
1111 if (ImGui::Button(ICON_MD_DRAW)) {
1113 }
1114 ImGui::SameLine();
1115 if (ImGui::Button(ICON_MD_SAVE)) {
1117 if (status_.ok()) {
1118 ImGui::OpenPopup("OWSaveSuccess");
1119 }
1120 }
1121 ImGui::SameLine();
1122
1123 // World toggle
1124 if (ImGui::Button(ow_show_dark_world_ ? "Dark World" : "Light World")) {
1126 // Re-render map with new world
1128 if (status_.ok()) {
1131 }
1132 }
1133 ImGui::SameLine();
1134
1135 // Custom map load/save buttons
1136 if (ImGui::Button("Load Custom Map...")) {
1138 if (!path.empty()) {
1140 if (!status_.ok()) {
1141 ImGui::OpenPopup("CustomMapLoadError");
1142 }
1143 }
1144 }
1145 ImGui::SameLine();
1146 if (ImGui::Button("Save Custom Map...")) {
1148 if (!path.empty()) {
1150 if (status_.ok()) {
1151 ImGui::OpenPopup("CustomMapSaveSuccess");
1152 }
1153 }
1154 }
1155
1156 ImGui::SameLine();
1157 ImGui::Text("Selected Tile: %d", selected_ow_tile_);
1158
1159 // Custom map error/success popups
1160 if (ImGui::BeginPopup("CustomMapLoadError")) {
1161 ImGui::Text("Error loading custom map: %s", status_.message().data());
1162 ImGui::EndPopup();
1163 }
1164 if (ImGui::BeginPopup("CustomMapSaveSuccess")) {
1165 ImGui::Text("Custom map saved successfully!");
1166 ImGui::EndPopup();
1167 }
1168
1169 // Save success popup
1170 if (ImGui::BeginPopup("OWSaveSuccess")) {
1171 ImGui::Text("Overworld map saved successfully!");
1172 ImGui::EndPopup();
1173 }
1174
1175 // Layout: 3-column table
1176 if (ImGui::BeginTable("OWMapTable", 3,
1177 ImGuiTableFlags_Resizable | ImGuiTableFlags_Borders)) {
1178 ImGui::TableSetupColumn("Map Canvas");
1179 ImGui::TableSetupColumn("Tileset");
1180 ImGui::TableSetupColumn("Palette");
1181 ImGui::TableHeadersRow();
1182
1183 // Column 1: Map Canvas
1184 ImGui::TableNextColumn();
1187
1188 auto& map_bitmap = ow_map_screen_.map_bitmap();
1189 if (map_bitmap.is_active()) {
1190 ow_map_canvas_.DrawBitmap(map_bitmap, 0, 0, 1.0f, 255);
1191 }
1192
1193 // Handle tile painting
1195 if (ow_map_canvas_.DrawTileSelector(8.0f)) {
1196 if (!ow_map_canvas_.points().empty()) {
1197 auto click_pos = ow_map_canvas_.points().front();
1198 int tile_x = static_cast<int>(click_pos.x) / 8;
1199 int tile_y = static_cast<int>(click_pos.y) / 8;
1200
1201 if (tile_x >= 0 && tile_x < 64 && tile_y >= 0 && tile_y < 64) {
1202 int tile_index = tile_x + (tile_y * 64);
1203
1204 // Update appropriate world's tile data
1205 if (ow_show_dark_world_) {
1207 } else {
1209 }
1210
1211 // Re-render map
1213 if (status_.ok()) {
1216 }
1217 }
1218 }
1219 }
1220 }
1221
1224
1225 // Column 2: Tileset Selector
1226 ImGui::TableNextColumn();
1229
1230 auto& tiles8_bitmap = ow_map_screen_.tiles8_bitmap();
1231 if (tiles8_bitmap.is_active()) {
1232 ow_tileset_canvas_.DrawBitmap(tiles8_bitmap, 0, 0, 2.0f, 255);
1233 }
1234
1235 // Handle tile selection
1237 if (!ow_tileset_canvas_.points().empty()) {
1238 auto click_pos = ow_tileset_canvas_.points().front();
1239 int tile_x = static_cast<int>(click_pos.x) / 8;
1240 int tile_y = static_cast<int>(click_pos.y) / 8;
1241 selected_ow_tile_ = tile_x + (tile_y * 16); // 16 tiles per row
1242 }
1243 }
1244
1247
1248 // Column 3: Palette Display
1249 ImGui::TableNextColumn();
1252 // Use inline palette editor for full 128-color palette
1253 gui::InlinePaletteEditor(palette, "Overworld Map Palette");
1254
1255 ImGui::EndTable();
1256 }
1257}
1258
1260 static bool show_bg1 = true;
1261 static bool show_bg2 = true;
1262 static bool show_bg3 = true;
1263
1264 static bool drawing_bg1 = true;
1265 static bool drawing_bg2 = false;
1266 static bool drawing_bg3 = false;
1267
1268 ImGui::Checkbox("Show BG1", &show_bg1);
1269 ImGui::SameLine();
1270 ImGui::Checkbox("Show BG2", &show_bg2);
1271
1272 ImGui::Checkbox("Draw BG1", &drawing_bg1);
1273 ImGui::SameLine();
1274 ImGui::Checkbox("Draw BG2", &drawing_bg2);
1275 ImGui::SameLine();
1276 ImGui::Checkbox("Draw BG3", &drawing_bg3);
1277}
1278
1279// ---------------------------------------------------------------------------
1280// Undo/redo helpers
1281// ---------------------------------------------------------------------------
1282
1294
1302
1303void ScreenEditor::SaveDungeonMapUndoState(const std::string& description) {
1305 pending_dungeon_desc_ = description;
1307}
1308
1309void ScreenEditor::SaveTile16CompUndoState(const std::string& description) {
1311 pending_tile16_desc_ = description;
1313}
1314
1317 return;
1319
1320 auto after = CaptureDungeonMapSnapshot();
1321 undo_manager_.Push(std::make_unique<ScreenEditAction>(
1323 [this](const ScreenSnapshot& snap) { RestoreFromSnapshot(snap); },
1325}
1326
1329 return;
1331
1332 auto after = CaptureTile16CompSnapshot();
1333 undo_manager_.Push(std::make_unique<ScreenEditAction>(
1335 [this](const ScreenSnapshot& snap) { RestoreFromSnapshot(snap); },
1337}
1338
1340 switch (snapshot.edit_type) {
1342 int idx = snapshot.dungeon_map.dungeon_index;
1343 if (idx >= 0 && idx < static_cast<int>(dungeon_maps_.size())) {
1344 dungeon_maps_[idx] = snapshot.dungeon_map.map_data;
1345 dungeon_map_labels_[idx] = snapshot.dungeon_map.labels;
1346 selected_dungeon = idx;
1347 }
1348 break;
1349 }
1353 // Re-apply tile16 composition to the blockset
1354 if (game_data()) {
1355 gfx::ModifyTile16(tile16_blockset_, game_data()->graphics_buffer,
1360 }
1361 break;
1362 }
1363 }
1364}
1365
1366} // namespace editor
1367} // 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
UndoManager undo_manager_
Definition editor.h:317
zelda3::GameData * game_data() const
Definition editor.h:307
EditorDependencies dependencies_
Definition editor.h:316
void DrawDungeonMapsRoomGfx()
Draw dungeon room graphics editor with enhanced tile16 editing.
absl::Status Undo() override
ScreenSnapshot CaptureTile16CompSnapshot() const
ScreenSnapshot pending_dungeon_before_
std::array< gfx::TileInfo, 4 > current_tile16_info
absl::Status Save() override
absl::Status Load() override
void SaveDungeonMapUndoState(const std::string &description)
absl::Status Update() override
void RestoreFromSnapshot(const ScreenSnapshot &snapshot)
zelda3::OverworldMapScreen ow_map_screen_
ScreenSnapshot pending_tile16_before_
ScreenSnapshot CaptureDungeonMapSnapshot() const
void DrawDungeonMapsEditor()
Draw dungeon maps editor with enhanced ROM hacking features.
absl::Status Redo() override
void SaveTile16CompUndoState(const std::string &description)
zelda3::TitleScreen title_screen_
zelda3::Inventory inventory_
zelda3::DungeonMapLabels dungeon_map_labels_
std::vector< zelda3::DungeonMap > dungeon_maps_
void Push(std::unique_ptr< UndoAction > action)
void RegisterPanel(size_t session_id, const WindowDescriptor &base_info)
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:75
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:221
void UpdateTile16(IRenderer *renderer, Tilemap &tilemap, int tile_id)
Definition tilemap.cc:113
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:354
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:569
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:380
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
std::vector< std::array< std::string, zelda3::kNumRooms > > labels
WorkspaceWindowManager * window_manager
Definition editor.h:176
Unified screen editor snapshot.
std::array< gfx::TileInfo, 4 > tile_info
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:2168
gfx::PaletteGroupMap palette_groups
Definition game_data.h:91