10#include "absl/strings/str_format.h"
27#include "imgui/imgui.h"
41 {.card_id =
"screen.dungeon_maps",
42 .display_name =
"Dungeon Maps",
43 .window_title =
" Dungeon Map Editor",
46 .shortcut_hint =
"Alt+1",
48 .enabled_condition = [
this]() {
return rom()->
is_loaded(); },
49 .disabled_tooltip =
"Load a ROM first"});
50 window_manager->RegisterPanel(
51 {.card_id =
"screen.inventory_menu",
52 .display_name =
"Inventory Menu",
53 .window_title =
" Inventory Menu",
56 .shortcut_hint =
"Alt+2",
58 .enabled_condition = [
this]() {
return rom()->
is_loaded(); },
59 .disabled_tooltip =
"Load a ROM first"});
60 window_manager->RegisterPanel(
61 {.card_id =
"screen.overworld_map",
62 .display_name =
"Overworld Map",
63 .window_title =
" Overworld Map",
66 .shortcut_hint =
"Alt+3",
68 .enabled_condition = [
this]() {
return rom()->
is_loaded(); },
69 .disabled_tooltip =
"Load a ROM first"});
70 window_manager->RegisterPanel(
71 {.card_id =
"screen.title_screen",
72 .display_name =
"Title Screen",
73 .window_title =
" Title Screen",
76 .shortcut_hint =
"Alt+4",
78 .enabled_condition = [
this]() {
return rom()->
is_loaded(); },
79 .disabled_tooltip =
"Load a ROM first"});
80 window_manager->RegisterPanel(
81 {.card_id =
"screen.naming_screen",
82 .display_name =
"Naming Screen",
83 .window_title =
" Naming Screen",
86 .shortcut_hint =
"Alt+5",
88 .enabled_condition = [
this]() {
return rom()->
is_loaded(); },
89 .disabled_tooltip =
"Load a ROM first"});
98 window_manager->RegisterWindowContent(std::make_unique<DungeonMapsPanel>(
100 window_manager->RegisterWindowContent(std::make_unique<InventoryMenuPanel>(
102 window_manager->RegisterWindowContent(
103 std::make_unique<OverworldMapScreenPanel>(
105 window_manager->RegisterWindowContent(std::make_unique<TitleScreenPanel>(
107 window_manager->RegisterWindowContent(std::make_unique<NamingScreenPanel>(
109 window_manager->RegisterWindowContent(std::make_unique<MenuTilemapPanel>(
114 window_manager->OpenWindow(
"screen.title_screen");
121 return absl::FailedPreconditionError(
122 "Screen Editor requires a loaded ROM and GameData");
124 if (
game_data()->palette_groups.dungeon_main.size() <= 3) {
125 return absl::FailedPreconditionError(
126 "Screen Editor requires dungeon palette 3");
138 for (
int i = 0; i < 4; i++) {
140 if (pixels.size() != 128 * 32) {
141 return absl::FailedPreconditionError(absl::StrFormat(
142 "Screen Dungeon Map sheet %d is unavailable in this ROM session",
146 sheet->Create(128, 32, 8,
147 std::vector<uint8_t>(pixels.begin(), pixels.end()));
149 *
game_data()->palette_groups.dungeon_main.mutable_palette(3));
157 const int tile8_width = 128;
158 const int tile8_height = 128;
159 std::vector<uint8_t> tile8_data(tile8_width * tile8_height);
162 for (
int sheet_idx = 0; sheet_idx < 4; sheet_idx++) {
163 const auto& sheet = *
sheets_[sheet_idx];
164 int dest_y_offset = sheet_idx * 32;
166 for (
int y = 0; y < 32; y++) {
167 for (
int x = 0; x < 128; x++) {
168 int src_index = y * 128 + x;
169 int dest_index = (dest_y_offset + y) * 128 + x;
171 if (src_index < sheet.size() && dest_index < tile8_data.size()) {
172 tile8_data[dest_index] = sheet.data()[src_index];
193 return absl::OkStatus();
202 std::vector<uint8_t> atlas_pixels(128 * 128);
203 for (
int slot = 0; slot < 4; ++slot) {
205 if (pixels.size() != 128 * 32) {
208 std::copy(pixels.begin(), pixels.end(),
209 atlas_pixels.begin() + slot * 128 * 32);
211 sheet->Create(128, 32, 8,
212 std::vector<uint8_t>(pixels.begin(), pixels.end()));
214 *
game_data()->palette_groups.dungeon_main.mutable_palette(3));
220 *
game_data()->palette_groups.dungeon_main.mutable_palette(3));
231 info[1], info[2], info[3], 212,
232 static_cast<int>(tile_id));
241 return absl::FailedPreconditionError(
242 "Screen Dungeon Map graphics are not loaded");
250 if (manager ==
nullptr) {
251 return absl::FailedPreconditionError(
"Editor manager is unavailable");
253 auto* editor_set = manager->GetCurrentEditorSet();
254 if (editor_set ==
nullptr || editor_set->GetGraphicsEditor() ==
nullptr) {
255 return absl::FailedPreconditionError(
"Graphics editor is unavailable");
257 context->return_to_source = [
this, manager] {
266 editor_set->GetGraphicsEditor()->OpenInContext(std::move(*
context)));
268 return absl::OkStatus();
271absl::StatusOr<zelda3::DungeonMapWritePlan>
275 return absl::FailedPreconditionError(
276 "Save blocked: Screen Editor edits are pending in unsupported domains. "
277 "Dungeon-map Tile16 artwork, title-screen, and overworld pause-map "
278 "writes remain disabled; undo or discard those edits before saving.");
281 return absl::FailedPreconditionError(
282 "Screen Editor ROM-backed state is invalid; reload Screen assets "
286 return absl::FailedPreconditionError(
287 "Finish the current Screen Editor edit before saving");
293 return absl::FailedPreconditionError(
294 "Save blocked: Screen Editor edits are pending, but Save Dungeon "
295 "Maps is disabled. Enable it or undo the map edits before saving.");
299 return absl::FailedPreconditionError(
300 "Dungeon maps do not belong to the current ROM session; reload "
301 "Screen assets before saving");
308 return absl::FailedPreconditionError(
"Screen save is already in progress");
312 return absl::OkStatus();
331 if (plan.writes.empty()) {
332 return absl::OkStatus();
338 return absl::OkStatus();
356 tr(
"Screen assets are unavailable. Reload the active ROM before "
357 "drawing or editing the inventory screen."));
367 ImGui::TextColored(theme.text_error_red,
368 tr(
"Error loading inventory: %s"),
376 if (ImGui::BeginTable(
"InventoryScreen", 4, ImGuiTableFlags_Resizable)) {
377 ImGui::TableSetupColumn(
"Canvas");
378 ImGui::TableSetupColumn(
"Tilesheet");
379 ImGui::TableSetupColumn(
"Item Icons");
380 ImGui::TableSetupColumn(
"Palette");
381 ImGui::TableHeadersRow();
383 ImGui::TableNextColumn();
395 ImGui::TableNextColumn();
398 frame_opts.
canvas_size = ImVec2(128 * 2 + 2, (192 * 2) + 4);
408 ImGui::TableNextColumn();
411 ImGui::TableNextColumn();
427 if (ImGui::BeginTable(
"InventoryToolset", 8, ImGuiTableFlags_SizingFixedFit,
429 ImGui::TableSetupColumn(
"#drawTool");
430 ImGui::TableSetupColumn(
"#sep1");
431 ImGui::TableSetupColumn(
"#zoomOut");
432 ImGui::TableSetupColumn(
"#zoomIN");
433 ImGui::TableSetupColumn(
"#sep2");
434 ImGui::TableSetupColumn(
"#bg2Tool");
435 ImGui::TableSetupColumn(
"#bg3Tool");
436 ImGui::TableSetupColumn(
"#itemTool");
438 ImGui::TableNextColumn();
443 ImGui::EndDisabled();
444 ImGui::TableNextColumn();
449 ImGui::EndDisabled();
450 ImGui::TableNextColumn();
452 ImGui::TableNextColumn();
456 ImGui::TableNextColumn();
460 ImGui::TableNextColumn();
462 ImGui::TableNextColumn();
466 ImGui::TableNextColumn();
476 if (ImGui::BeginChild(
"##ItemIconsList", ImVec2(0, 0),
true,
477 ImGuiWindowFlags_HorizontalScrollbar)) {
478 ImGui::Text(tr(
"Item Icons (2x2 tiles each)"));
484 tr(
"No item icons loaded. Icons will be loaded when the "
485 "inventory is initialized."));
491 if (ImGui::BeginTable(
"##IconsTable", 2,
492 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
493 ImGui::TableSetupColumn(
"Icon Name");
494 ImGui::TableSetupColumn(
"Tile Data");
495 ImGui::TableHeadersRow();
497 for (
size_t i = 0; i < icons.size(); i++) {
498 const auto& icon = icons[i];
500 ImGui::TableNextRow();
501 ImGui::TableNextColumn();
504 if (ImGui::Selectable(icon.name.c_str(),
false,
505 ImGuiSelectableFlags_SpanAllColumns)) {
509 ImGui::TableNextColumn();
511 ImGui::Text(tr(
"TL:%04X TR:%04X"), icon.tile_tl, icon.tile_tr);
513 ImGui::Text(tr(
"BL:%04X BR:%04X"), icon.tile_bl, icon.tile_br);
520 ImGui::TextWrapped(tr(
521 "NOTE: Individual icon editing will be implemented in the future "
522 "Oracle of Secrets menu editor. Each icon is composed of 4 tile words "
523 "representing a 2x2 arrangement of 8x8 tiles in SNES tile format "
524 "(vhopppcc cccccccc)."));
533 ImGui::TextWrapped(tr(
"No valid dungeon-map floor is loaded."));
547 auto boss_room = current_dungeon.boss_room;
550 std::vector<int> tile_ids_to_render;
551 std::vector<ImVec2> tile_positions;
556 if (current_dungeon.floor_rooms[
floor_number][j] != 0x0F) {
557 int tile16_id = current_dungeon.floor_gfx[
floor_number][j];
558 int posX = ((j % 5) * 32);
559 int posY = ((j / 5) * 32);
562 tile_ids_to_render.push_back(tile16_id);
563 tile_positions.emplace_back(posX * 2, posY * 2);
568 for (
size_t idx = 0; idx < tile_ids_to_render.size(); ++idx) {
569 int tile16_id = tile_ids_to_render[idx];
570 ImVec2 pos = tile_positions[idx];
575 if (tiles_per_row <= 0 || tiles_per_column <= 0 || tile16_id < 0 ||
576 tile16_id >= tiles_per_row * tiles_per_column) {
579 const int tile_x = (tile16_id % tiles_per_row) * 16;
580 const int tile_y = (tile16_id / tiles_per_row) * 16;
582 std::vector<uint8_t> tile_data(16 * 16);
583 int tile_data_offset = 0;
600 if (cached_tile && cached_tile->is_active()) {
602 if (!cached_tile->texture()) {
613 if (current_dungeon.floor_rooms[
floor_number][j] != 0x0F) {
614 int posX = ((j % 5) * 32);
615 int posY = ((j / 5) * 32);
617 if (current_dungeon.floor_rooms[
floor_number][j] == boss_room) {
637 const int room = x + (y * 5);
647 ImGui::TextWrapped(tr(
"No valid dungeon map is loaded."));
651 const int floor_count =
652 current_dungeon.nbr_of_floor + current_dungeon.nbr_of_basement;
653 if (floor_count <= 0 ||
654 floor_count >
static_cast<int>(current_dungeon.floor_rooms.size()) ||
655 floor_count >
static_cast<int>(current_dungeon.floor_gfx.size()) ||
658 ImGui::TextWrapped(tr(
"This dungeon has no drawable map floors."));
669 for (
int i = 0; i < floor_count; i++) {
670 int basement_num = current_dungeon.nbr_of_basement - i;
671 std::string tab_name = absl::StrFormat(
"Basement %d", basement_num);
672 if (i >= current_dungeon.nbr_of_basement) {
673 tab_name = absl::StrFormat(
"Floor %d",
674 i - current_dungeon.nbr_of_basement + 1);
677 ? ImGuiTabItemFlags_SetSelected
678 : ImGuiTabItemFlags_None;
679 if (ImGui::BeginTabItem(tab_name.data(),
nullptr, flags)) {
700 "Edit room assignment"));
717 const auto button_size = ImVec2(130, 0);
769 ImGui::TextWrapped(tr(
"Dungeon-map graphics are not ready to draw."));
773 if (ImGui::BeginChild(
"##DungeonMapTiles", ImVec2(0, 0),
true)) {
777 tilesheet_opts.
canvas_size = ImVec2((256 * 2) + 2, (192 * 2) + 4);
793 ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
795 const int selected_tile16 =
static_cast<int>(
799 if (selected_tile16 >= 0 &&
836 auto current_tile_rt =
845 const int tiles_per_row =
851 std::vector<uint8_t> tile_data(64);
852 for (
int py = 0; py < 8; py++) {
853 for (
int px = 0; px < 8; px++) {
854 int src_x = tile_x + px;
855 int src_y = tile_y + py;
857 int dst_index = py * 8 + px;
868 std::move(new_tile8));
872 if (cached_tile8 && cached_tile8->is_active()) {
874 if (!cached_tile8->texture()) {
896 auto* selected_tile =
898 if (selected_tile && selected_tile->is_active()) {
900 if (!selected_tile->texture()) {
952 tr(
"Screen assets are unavailable. Reload the active ROM before "
953 "drawing or editing dungeon maps."));
957 ImGui::TextWrapped(tr(
"No valid dungeon-map model is loaded."));
970 ImGui::BeginDisabled();
972 ImGui::EndDisabled();
973 if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
975 tr(
"Dungeon-map Tile16 saving is disabled until it can participate in "
976 "the coordinated ROM save transaction."));
979 static std::vector<std::string> dungeon_names = {
980 "Sewers/Sanctuary",
"Hyrule Castle",
"Eastern Palace",
981 "Desert Palace",
"Tower of Hera",
"Agahnim's Tower",
982 "Palace of Darkness",
"Swamp Palace",
"Skull Woods",
983 "Thieves' Town",
"Ice Palace",
"Misery Mire",
984 "Turtle Rock",
"Ganon's Tower"};
986 if (ImGui::BeginTable(
"DungeonMapsTable", 4,
987 ImGuiTableFlags_Resizable |
988 ImGuiTableFlags_Reorderable |
989 ImGuiTableFlags_Hideable)) {
990 ImGui::TableSetupColumn(
"Dungeon");
991 ImGui::TableSetupColumn(
"Map");
992 ImGui::TableSetupColumn(
"Rooms Gfx");
993 ImGui::TableSetupColumn(
"Tiles Gfx");
994 ImGui::TableHeadersRow();
996 ImGui::TableNextColumn();
997 const size_t dungeon_count =
999 for (
size_t i = 0; i < dungeon_count; i++) {
1002 absl::StrFormat(
"%d", i), dungeon_names[i]);
1003 if (ImGui::IsItemClicked()) {
1008 ImGui::TableNextColumn();
1011 ImGui::TableNextColumn();
1014 ImGui::TableNextColumn();
1020 ImGui::IsMouseClicked(ImGuiMouseButton_Left) &&
1026 const int x =
static_cast<int>(point.x) / 16;
1027 const int y =
static_cast<int>(point.y) / 16;
1028 if (x >= 0 && x < 16 && y >= 0 && y < 16) {
1038 if (ImGui::Button(tr(
"Edit selected graphics tile"))) {
1042 ImGui::TextWrapped(
"%s", std::string(
status_.message()).c_str());
1045 ImGui::Text(tr(
"For use with custom inserted graphics assembly patches."));
1046 if (ImGui::Button(tr(
"Load GFX from BIN file")))
1055 if (!bin_file.empty()) {
1056 std::ifstream file(bin_file, std::ios::binary);
1057 if (file.is_open()) {
1059 std::vector<uint8_t> bin_data((std::istreambuf_iterator<char>(file)),
1060 std::istreambuf_iterator<char>());
1063 converted_bin,
true)
1065 std::vector<std::vector<uint8_t>> gfx_sheets;
1066 for (
int i = 0; i < 4; i++) {
1067 gfx_sheets.emplace_back(converted_bin.begin() + (i * 0x1000),
1068 converted_bin.begin() + ((i + 1) * 0x1000));
1070 sheet->Create(128, 32, 8, gfx_sheets[i]);
1072 *
game_data()->palette_groups.dungeon_main.mutable_palette(3));
1081 status_ = absl::InternalError(
"Failed to load dungeon map tile16");
1091 tr(
"Screen assets are unavailable. Reload the active ROM before "
1092 "drawing or editing the title screen."));
1100 ImGui::TextColored(theme.text_error_red,
1101 tr(
"Error loading title screen: %s"),
1109 ImGui::Text(tr(
"Title screen not loaded. Ensure ROM is loaded."));
1118 ImGui::BeginDisabled();
1122 ImGui::EndDisabled();
1123 if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
1125 tr(
"Title-screen ROM saving is disabled until write/reopen/readback "
1126 "verification exists."));
1150 if (ImGui::BeginTable(
"TitleScreenTable", 2,
1151 ImGuiTableFlags_Resizable | ImGuiTableFlags_Borders)) {
1152 ImGui::TableSetupColumn(
"Title Screen (Composite)");
1153 ImGui::TableSetupColumn(
"Tile Selector");
1154 ImGui::TableHeadersRow();
1157 ImGui::TableNextColumn();
1161 ImGui::TableNextColumn();
1186 if (composite_bitmap.is_active()) {
1195 int tile_x =
static_cast<int>(click_pos.x) / 8;
1196 int tile_y =
static_cast<int>(click_pos.y) / 8;
1198 if (tile_x >= 0 && tile_x < 32 && tile_y >= 0 && tile_y < 32) {
1199 int tilemap_index = tile_y * 32 + tile_x;
1205 tile_word |= 0x4000;
1207 tile_word |= 0x8000;
1242 if (bg1_bitmap.is_active()) {
1251 int tile_x =
static_cast<int>(click_pos.x) / 8;
1252 int tile_y =
static_cast<int>(click_pos.y) / 8;
1254 if (tile_x >= 0 && tile_x < 32 && tile_y >= 0 && tile_y < 32) {
1255 int tilemap_index = tile_y * 32 + tile_x;
1261 tile_word |= 0x4000;
1263 tile_word |= 0x8000;
1289 if (bg2_bitmap.is_active()) {
1298 int tile_x =
static_cast<int>(click_pos.x) / 8;
1299 int tile_y =
static_cast<int>(click_pos.y) / 8;
1301 if (tile_x >= 0 && tile_x < 32 && tile_y >= 0 && tile_y < 32) {
1302 int tilemap_index = tile_y * 32 + tile_x;
1308 tile_word |= 0x4000;
1310 tile_word |= 0x8000;
1336 if (tiles8_bitmap.is_active()) {
1345 int tile_x =
static_cast<int>(click_pos.x) / 8;
1346 int tile_y =
static_cast<int>(click_pos.y) / 8;
1347 int tiles_per_row = 128 / 8;
1365 ImGui::SetNextItemWidth(100);
1380 {.id =
"menu_tilemap_file",
1381 .label =
"Menu tilemap",
1394 tr(
"Screen assets are unavailable. Reload the active ROM before "
1395 "drawing or editing the pause-map screen."));
1403 ImGui::TextColored(theme.text_error_red,
1404 tr(
"Error loading overworld map: %s"),
1412 ImGui::Text(tr(
"Overworld map not loaded. Ensure ROM is loaded."));
1419 if (ImGui::Button(tr(
"Paint Mode"))) {
1422 if (ImGui::IsItemHovered()) {
1423 ImGui::SetTooltip(tr(
1424 "Paint the pause-menu world map: choose an 8x8 tile in Tileset, then "
1425 "click Map Canvas."));
1428 ImGui::BeginDisabled();
1429 if (ImGui::Button(tr(
"Save World Map"))) {
1432 ImGui::EndDisabled();
1433 if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
1434 ImGui::SetTooltip(tr(
1435 "Pause-map ROM saving is disabled until Light World, Dark World, and "
1436 "palette writes pass write/reopen/readback verification."));
1453 if (ImGui::Button(tr(
"Load Custom Map..."))) {
1455 if (!path.empty()) {
1458 ImGui::OpenPopup(
"CustomMapLoadError");
1465 if (ImGui::Button(tr(
"Save Custom Map..."))) {
1467 if (!path.empty()) {
1470 ImGui::OpenPopup(
"CustomMapSaveSuccess");
1478 ImGui::TextWrapped(tr(
1479 "This edits the pause-menu world map art, not the 160 playable "
1480 "overworld areas. For area-map painting, entrances, exits, items, and "
1481 "sprites, use Overworld Editor. Paint flow here: pick an 8x8 tile from "
1482 "Tileset, then click Map Canvas; use Light/Dark World to choose the "
1487 if (ImGui::BeginPopup(
"CustomMapLoadError")) {
1488 ImGui::Text(tr(
"Error loading custom map: %s"),
status_.message().data());
1491 if (ImGui::BeginPopup(
"CustomMapSaveSuccess")) {
1492 ImGui::Text(tr(
"Custom map saved successfully!"));
1497 if (ImGui::BeginTable(
"OWMapTable", 3,
1498 ImGuiTableFlags_Resizable | ImGuiTableFlags_Borders)) {
1499 ImGui::TableSetupColumn(
"Map Canvas");
1500 ImGui::TableSetupColumn(
"Tileset");
1501 ImGui::TableSetupColumn(
"Palette");
1502 ImGui::TableHeadersRow();
1505 ImGui::TableNextColumn();
1511 if (map_bitmap.is_active()) {
1520 int tile_x =
static_cast<int>(click_pos.x) / 8;
1521 int tile_y =
static_cast<int>(click_pos.y) / 8;
1523 if (tile_x >= 0 && tile_x < 64 && tile_y >= 0 && tile_y < 64) {
1524 int tile_index = tile_x + (tile_y * 64);
1549 ImGui::TableNextColumn();
1555 if (tiles8_bitmap.is_active()) {
1563 int tile_x =
static_cast<int>(click_pos.x) / 8;
1564 int tile_y =
static_cast<int>(click_pos.y) / 8;
1573 ImGui::TableNextColumn();
1577 const auto palette_before = palette;
1579 if (palette != palette_before) {
1588 static bool show_bg1 =
true;
1589 static bool show_bg2 =
true;
1590 static bool show_bg3 =
true;
1592 static bool drawing_bg1 =
true;
1593 static bool drawing_bg2 =
false;
1594 static bool drawing_bg3 =
false;
1596 ImGui::Checkbox(tr(
"Show BG1"), &show_bg1);
1598 ImGui::Checkbox(tr(
"Show BG2"), &show_bg2);
1600 ImGui::Checkbox(tr(
"Draw BG1"), &drawing_bg1);
1602 ImGui::Checkbox(tr(
"Draw BG2"), &drawing_bg2);
1604 ImGui::Checkbox(tr(
"Draw BG3"), &drawing_bg3);
1614 return absl::FailedPreconditionError(
"Dungeon map edit is unavailable");
1618 const size_t total = map.nbr_of_basement + map.nbr_of_floor;
1619 const int count = basement ? map.nbr_of_basement : map.nbr_of_floor;
1620 if (map.floor_rooms.size() != total || map.floor_gfx.size() != total ||
1621 labels.size() != total) {
1622 return absl::InvalidArgumentError(
1623 "Dungeon map floor vectors are inconsistent");
1625 if ((add && count >= 8) || (!add && (count == 0 || total <= 1))) {
1626 return absl::FailedPreconditionError(
1627 "Keep at least one map floor and at most eight floors of each type");
1629 const size_t index = basement ? 0 : (add ? total : total - 1);
1630 if (!add && map.boss_room != 0x000F && map.boss_room != 0xFFFF) {
1631 const auto& rooms = map.floor_rooms[index];
1632 if (std::find(rooms.begin(), rooms.end(), map.boss_room) != rooms.end()) {
1633 return absl::FailedPreconditionError(
1634 "Move or clear the boss before removing its map floor");
1640 add ? (basement ?
"Add basement" :
"Add floor")
1641 : (basement ?
"Remove basement" :
"Remove floor"));
1643 std::array<uint8_t, zelda3::kNumRooms> rooms, graphics;
1644 std::array<std::string, zelda3::kNumRooms> row_labels;
1646 graphics.fill(0xFF);
1648 map.floor_rooms.insert(map.floor_rooms.begin() + index, rooms);
1649 map.floor_gfx.insert(map.floor_gfx.begin() + index, graphics);
1650 labels.insert(labels.begin() + index, row_labels);
1651 if (basement && total > 0)
1654 map.floor_rooms.erase(map.floor_rooms.begin() + index);
1655 map.floor_gfx.erase(map.floor_gfx.begin() + index);
1656 labels.erase(labels.begin() + index);
1660 auto& changed_count = basement ? map.nbr_of_basement : map.nbr_of_floor;
1661 changed_count += add ? 1 : -1;
1663 std::clamp(
floor_number, 0,
static_cast<int>(map.floor_rooms.size()) - 1);
1666 return absl::OkStatus();
1732 if (idx >= 0 && idx <
static_cast<int>(
dungeon_maps_.size())) {
1740 static_cast<int>(
dungeon_maps_[idx].floor_rooms.size()) - 1));
1764 return absl::FailedPreconditionError(
1765 "Title-screen ROM saving is disabled until write/reopen/readback "
1766 "verification exists");
1770 return absl::FailedPreconditionError(
1771 "Pause-map ROM saving is disabled until Light World, Dark World, and "
1772 "palette writes pass write/reopen/readback verification");
1786 sheet = std::make_unique<gfx::Bitmap>();
1809 selected_dungeon < static_cast<int>(
dungeon_maps_.size()) &&
1818 return floor < static_cast<int>(dungeon.floor_rooms.size()) &&
1819 floor < static_cast<int>(dungeon.floor_gfx.size()) &&
1882 canvas->ClearSelection();
project::ResourceLabelManager * resource_label()
The EditorManager controls the main editor window and manages the various editor classes.
EditorContext context() const
UndoManager undo_manager_
project::YazeProject * project() const
zelda3::GameData * game_data() const
EditorDependencies dependencies_
virtual EditorContextSnapshot BuildContextSnapshot() const
void DrawTitleScreenEditor()
void DrawDungeonMapsRoomGfx()
Draw dungeon room graphics editor with enhanced tile16 editing.
absl::Status RefreshRomBackedState()
gfx::SnesPalette palette_
void RollbackSaveTransaction() override
absl::Status Undo() override
void DrawTitleScreenBlocksetSelector()
void MarkOverworldMapModified()
bool title_screen_loaded_
ScreenSnapshot CaptureTile16CompSnapshot() const
gui::Canvas ow_map_canvas_
absl::Status SaveOverworldMapToRom()
gfx::Bitmap * GetOrCreateSheet(int index)
absl::Status ChangeDungeonMapFloorCount(bool basement, bool add)
gfx::Tilemap tile16_blockset_
ScreenSnapshot pending_dungeon_before_
bool pending_overworld_map_palette_changes_
void DrawTitleScreenBG2Canvas()
bool HasPendingOverworldMapChanges() const
absl::Status OpenDungeonMapTileGraphics()
bool pending_dungeon_map_tile16_changes_
bool HasPendingDungeonMapChanges() const
gui::Canvas tilemap_canvas_
bool map_save_transaction_active_
MenuTilemapEditorUI menu_tilemap_ui_
void DrawInventoryItemIcons()
std::array< gfx::TileInfo, 4 > current_tile16_info
absl::Status Save() override
absl::Status Load() override
void SaveDungeonMapUndoState(const std::string &description)
void InvalidateRomBackedState()
bool pending_dungeon_map_changes_
absl::Status Update() override
gui::Canvas screen_canvas_
std::string pending_tile16_desc_
void DrawTitleScreenBG1Canvas()
bool HasValidDungeonFloorSelection(int floor) const
gui::Canvas title_bg1_canvas_
void RestoreFromSnapshot(const ScreenSnapshot &snapshot)
void DrawMenuTilemapEditor()
zelda3::OverworldMapScreen ow_map_screen_
void MarkTitleScreenModified()
ScreenSnapshot pending_tile16_before_
void Initialize() override
bool HasValidDungeonSelection() const
int selected_title_tile16_
void RefreshDungeonMapGraphicsFromSession()
ScreenSnapshot CaptureDungeonMapSnapshot() const
bool pending_title_screen_changes_
gui::Canvas current_tile_canvas_
void DrawDungeonMapsEditor()
Draw dungeon maps editor with enhanced ROM hacking features.
absl::StatusOr< zelda3::DungeonMapWritePlan > PrepareDungeonMapSave() const
zelda3::GameData * loaded_game_data_
gui::Canvas title_bg2_canvas_
absl::Status Redo() override
absl::Status BeginSaveTransaction() override
void SaveTile16CompUndoState(const std::string &description)
void DrawInventoryToolset()
bool HasPendingDungeonMapTile16Changes() const
void MarkOverworldMapPaletteModified()
void CommitTile16CompUndo()
gfx::Tilemap tile8_tilemap_
bool has_pending_dungeon_undo_
void DrawTitleScreenCompositeCanvas()
absl::Status SaveTitleScreenToRom()
void ResetRomBackedStateForLoad()
bool HasPendingTitleScreenChanges() const
zelda3::TitleScreen title_screen_
void DrawOverworldMapEditor()
EditingMode current_mode_
std::unique_ptr< zelda3::Inventory > inventory_
zelda3::DungeonMapLabels dungeon_map_labels_
bool paste_button_pressed
gui::Canvas ow_tileset_canvas_
gui::Canvas title_blockset_canvas_
bool rom_backed_state_valid_
void DrawDungeonMapToolset()
gui::Canvas tilesheet_canvas_
EditorContextSnapshot BuildContextSnapshot() const override
void DrawNamingScreenEditor()
void MarkDungeonMapModified()
bool map_save_serialized_
void DrawDungeonMapScreen(int i)
bool has_pending_tile16_undo_
void DrawDungeonMapsTabs()
void RefreshTileCacheForReload(gfx::Tilemap &tilemap)
void CommitSaveTransaction() override
std::vector< zelda3::DungeonMap > dungeon_maps_
std::string pending_dungeon_desc_
void CommitDungeonMapUndo()
void DrawInventoryMenuEditor()
bool pending_overworld_map_changes_
void MarkDungeonMapTile16Modified()
void Push(std::unique_ptr< UndoAction > action)
void RegisterPanel(size_t session_id, const WindowDescriptor &base_info)
bool OpenWindow(size_t session_id, const std::string &base_window_id)
void QueueTextureCommand(TextureCommandType type, Bitmap *bitmap)
Represents a bitmap image optimized for SNES ROM hacking.
const uint8_t * data() const
const SnesPalette & palette() const
void Create(int width, int height, int depth, std::span< uint8_t > data)
Create a bitmap with the given dimensions and data.
void set_data(const std::vector< uint8_t > &data)
void SetPalette(const SnesPalette &palette)
Set the palette for the bitmap using SNES palette format.
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)
RAII timer for automatic timing management.
void DrawBitmap(Bitmap &bitmap, int border_offset, float scale)
void DrawOutlineWithColor(int x, int y, int w, int h, ImVec4 color)
ImVector< ImVec2 > * mutable_points()
bool DrawTileSelector(int size, int size_y=0)
bool DrawTilePainter(const Bitmap &bitmap, int size, float scale=1.0f)
CanvasConfig & GetConfig()
bool IsMouseHovering() const
void DrawBackground(ImVec2 canvas_size=ImVec2(0, 0), std::optional< ImVec2 > viewport_size=std::nullopt)
void DrawBitmapTable(const BitmapTable &gfx_bin)
const ImVector< ImVec2 > & points() const
void DrawGrid(float grid_step=64.0f, int tile_id_offset=8)
void DrawText(const std::string &text, int x, int y)
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::Span< const uint8_t > Sheet(uint16_t sheet) const
The sheet's 4096 pixels, or an empty span when it is not held.
auto & mutable_lw_tiles()
absl::Status SaveCustomMap(const std::string &file_path, bool use_dark_world)
Save map data to external binary file.
auto & mutable_dw_tiles()
absl::Status LoadCustomMap(const std::string &file_path)
Load custom map from external binary file.
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.
auto & composite_bitmap()
auto & mutable_bg1_buffer()
absl::Status Create(Rom *rom, GameData *game_data=nullptr)
Initialize and load title screen data from ROM.
auto & mutable_bg2_buffer()
absl::Status RenderCompositeLayer(bool show_bg1, bool show_bg2)
Render composite layer with BG1 on top of BG2 with transparency.
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_MORE_VERT
#define ICON_MD_INVENTORY
#define ASSIGN_OR_RETURN(type_variable_name, expression)
const AgentUITheme & GetTheme()
void EnsureCompositeBitmapTextureQueued(gfx::Bitmap &composite)
absl::StatusOr< GraphicsEditContext > MakeDungeonMapTilesGfxEditContext(const zelda3::GameData *owner, int selected_tile8, bool binary_gfx_loaded)
void RenderTile16(IRenderer *renderer, Tilemap &tilemap, int tile_id)
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)
void UpdateTile16(IRenderer *renderer, Tilemap &tilemap, int tile_id)
std::vector< uint8_t > GetTilemapData(Tilemap &tilemap, int tile_id)
std::vector< uint8_t > SnesTo8bppSheet(std::span< const uint8_t > sheet, int bpp, int num_sheets)
void EndCanvas(Canvas &canvas)
bool InputHexWord(const char *label, uint16_t *data, float input_width, bool no_step)
bool DrawTileSelector(const CanvasRuntime &rt, int size, int size_y, ImVec2 *out_selected_pos)
void BeginCanvas(Canvas &canvas, ImVec2 child_size)
void DrawBitmap(const CanvasRuntime &rt, gfx::Bitmap &bitmap, int border_offset=2, float scale=1.0f)
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.
bool InputTileInfo(const char *label, gfx::TileInfo *tile_info)
bool AutoButton(const char *label, const ImVec2 &size=ImVec2(0, 0))
IMGUI_API bool DisplayPalette(gfx::SnesPalette &palette, bool loaded)
bool ToolbarIconButton(const char *icon, const char *tooltip, bool is_active)
Convenience wrapper for toolbar-sized icon buttons.
bool InputHexByte(const char *label, uint8_t *data, float input_width, bool no_step)
std::string HexByte(uint8_t byte, HexStringParams params)
std::string GetFileName(const std::string &filename)
Gets the filename from a full path.
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.
absl::StatusOr< DungeonMapWritePlan > BuildDungeonMapWritePlan(const std::vector< DungeonMap > &dungeon_maps, size_t rom_size, bool relocated_tile16)
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)
zelda3::DungeonMap map_data
std::vector< std::array< std::string, zelda3::kNumRooms > > labels
std::string pending_label
std::vector< EditorContextValue > metadata
WorkspaceWindowManager * window_manager
Unified screen editor snapshot.
DungeonMapSnapshot dungeon_map
Tile16CompSnapshot tile16_comp
std::array< gfx::TileInfo, 4 > tile_info
int y
Y coordinate or height.
int x
X coordinate or width.
PaletteGroup dungeon_main
auto mutable_palette(int i)
std::unordered_map< int, std::unique_ptr< Bitmap > > cache_
void CacheTile(int tile_id, const Bitmap &bitmap)
Cache a tile bitmap by copying it.
Bitmap * GetTile(int tile_id)
Get a cached tile by ID.
Tilemap structure for SNES tile-based graphics management.
Pair tile_size
Size of individual tiles (8x8 or 16x16)
TileCache tile_cache
Smart tile cache with LRU eviction.
Pair map_size
Size of tilemap in tiles.
Bitmap atlas
Master bitmap containing all tiles.
std::vector< std::array< gfx::TileInfo, 4 > > tile_info
Tile metadata (4 tiles per 16x16)
std::optional< float > grid_step
void SelectableLabelWithNameEdit(bool selected, const std::string &type, const std::string &key, const std::string &defaultValue)
gfx::PaletteGroupMap palette_groups
GraphicsSheetStore sheet_store