4#define IM_PI 3.14159265358979323846f
19#include <unordered_map>
23#include "absl/status/status.h"
24#include "absl/strings/str_format.h"
47#include "imgui/imgui.h"
48#include "imgui_memory_editor.h"
73 .display_name =
"Overworld Canvas",
75 .category =
"Overworld",
76 .shortcut_hint =
"Ctrl+Shift+O",
81 card_registry->RegisterCard(
82 {.card_id =
MakeCardId(
"overworld.tile16_selector"),
83 .display_name =
"Tile16 Selector",
85 .category =
"Overworld",
86 .shortcut_hint =
"Ctrl+Alt+1",
90 card_registry->RegisterCard(
91 {.card_id =
MakeCardId(
"overworld.tile8_selector"),
92 .display_name =
"Tile8 Selector",
94 .category =
"Overworld",
95 .shortcut_hint =
"Ctrl+Alt+2",
99 card_registry->RegisterCard({.card_id =
MakeCardId(
"overworld.area_graphics"),
100 .display_name =
"Area Graphics",
102 .category =
"Overworld",
103 .shortcut_hint =
"Ctrl+Alt+3",
107 card_registry->RegisterCard({.card_id =
MakeCardId(
"overworld.scratch"),
108 .display_name =
"Scratch Workspace",
110 .category =
"Overworld",
111 .shortcut_hint =
"Ctrl+Alt+4",
115 card_registry->RegisterCard({.card_id =
MakeCardId(
"overworld.gfx_groups"),
116 .display_name =
"GFX Groups",
118 .category =
"Overworld",
119 .shortcut_hint =
"Ctrl+Alt+5",
123 card_registry->RegisterCard({.card_id =
MakeCardId(
"overworld.usage_stats"),
124 .display_name =
"Usage Statistics",
126 .category =
"Overworld",
127 .shortcut_hint =
"Ctrl+Alt+6",
131 card_registry->RegisterCard({.card_id =
MakeCardId(
"overworld.v3_settings"),
132 .display_name =
"v3 Settings",
134 .category =
"Overworld",
135 .shortcut_hint =
"Ctrl+Alt+7",
164 LOG_DEBUG(
"OverworldEditor",
"Loading overworld.");
166 return absl::FailedPreconditionError(
"ROM not loaded");
187 "Overworld editor refreshed after Tile16 changes");
188 return absl::OkStatus();
194 [
this](
const std::string& entity_type) {
201 return absl::OkStatus();
236 static bool cards_configured =
false;
237 if (!cards_configured) {
266 cards_configured =
true;
275 overworld_canvas_card.
End();
320 tile16_editor_card.
End();
332 gfx_groups_card.
End();
344 usage_stats_card.
End();
349 ImGui::SetNextWindowSize(ImVec2(650, 750), ImGuiCond_FirstUseEver);
350 if (ImGui::Begin(
ICON_MD_TUNE " Area Configuration###AreaConfig",
363 ImGui::SetNextWindowSize(ImVec2(400, 500), ImGuiCond_FirstUseEver);
377 ImGui::SetNextWindowSize(ImVec2(500, 450), ImGuiCond_FirstUseEver);
378 if (ImGui::Begin(
ICON_MD_LAYERS " Visual Effects Editor###OverlayEditor",
395 if (ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
396 if (hovered_entity) {
398 switch (hovered_entity->entity_type_) {
402 ImGui::OpenPopup(
"Exit editor");
407 ImGui::OpenPopup(
"Entrance Editor");
412 ImGui::OpenPopup(
"Item editor");
416 ImGui::OpenPopup(
"Sprite editor");
425 if (hovered_entity && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
426 if (hovered_entity->entity_type_ ==
430 }
else if (hovered_entity->entity_type_ ==
474 static bool use_work_area =
true;
475 static ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration |
476 ImGuiWindowFlags_NoMove |
477 ImGuiWindowFlags_NoSavedSettings;
478 const ImGuiViewport* viewport = ImGui::GetMainViewport();
479 ImGui::SetNextWindowPos(use_work_area ? viewport->WorkPos : viewport->Pos);
480 ImGui::SetNextWindowSize(use_work_area ? viewport->WorkSize : viewport->Size);
507 "Mouse Mode (1)\nNavigate, pan, and manage entities")) {
515 "Tile Paint Mode (2)\nDraw tiles on the map")) {
527 const char* entity_label =
"";
528 const char* entity_icon =
"";
532 entity_label =
"Entrances";
536 entity_label =
"Exits";
540 entity_label =
"Items";
544 entity_label =
"Sprites";
548 entity_label =
"Transports";
552 entity_label =
"Music";
557 ImGui::TextColored(ImVec4(0.4f, 0.8f, 1.0f, 1.0f),
"%s Editing: %s",
558 entity_icon, entity_label);
563 [
this]() { ImGui::OpenPopup(
"UpgradeROMVersion"); });
568 ->mutable_area_graphics(),
573 RefreshMapProperties();
576 maps_bmp_[current_map_].set_modified(true);
577 RefreshChildMapOnDemand(current_map_);
578 RefreshSiblingMapGraphics(current_map_);
581 RefreshTile16Blockset();
588 ->mutable_area_palette(),
591 RefreshSiblingMapGraphics(current_map_);
592 RefreshMapProperties();
593 status_ = RefreshMapPalette();
594 RefreshOverworldMap();
611 "Fullscreen (F11)")) {
655 if (ImGui::BeginPopupModal(
"UpgradeROMVersion",
nullptr,
656 ImGuiWindowFlags_AlwaysAutoResize)) {
660 "This will apply the ZSCustomOverworld ASM patch to your ROM,\n"
661 "enabling advanced features like custom tile graphics, animated GFX,\n"
662 "wide/tall areas, and more.");
666 ImGui::Text(
"Current Version: %s",
667 current_version == 0xFF
669 : absl::StrFormat(
"v%d", current_version).c_str());
671 static int target_version = 3;
672 ImGui::RadioButton(
"v2 (Basic features)", &target_version, 2);
674 ImGui::RadioButton(
"v3 (All features)", &target_version, 3);
678 if (ImGui::Button(
ICON_MD_CHECK " Apply Upgrade", ImVec2(150, 0))) {
684 ImGui::CloseCurrentPopup();
686 LOG_ERROR(
"OverworldEditor",
"Upgrade failed: %s",
687 status.message().data());
692 ImGui::CloseCurrentPopup();
710 if (!ImGui::IsAnyItemActive()) {
716 if (ImGui::IsKeyDown(ImGuiKey_1)) {
718 }
else if (ImGui::IsKeyDown(ImGuiKey_2)) {
732 if (ImGui::IsKeyDown(ImGuiKey_3)) {
736 }
else if (ImGui::IsKeyDown(ImGuiKey_4)) {
740 }
else if (ImGui::IsKeyDown(ImGuiKey_5)) {
744 }
else if (ImGui::IsKeyDown(ImGuiKey_6)) {
748 }
else if (ImGui::IsKeyDown(ImGuiKey_7)) {
752 }
else if (ImGui::IsKeyDown(ImGuiKey_8)) {
759 if (ImGui::IsKeyDown(ImGuiKey_F11)) {
764 if (ImGui::IsKeyDown(ImGuiKey_L) && ImGui::IsKeyDown(ImGuiKey_LeftCtrl)) {
769 if (ImGui::IsKeyDown(ImGuiKey_T) && ImGui::IsKeyDown(ImGuiKey_LeftCtrl)) {
778 for (
int i = 0; i < 0x40; i++) {
782 if (world_index < 0 || world_index >=
static_cast<int>(
maps_bmp_.size())) {
802 ImDrawList* draw_list = ImGui::GetWindowDrawList();
804 ImVec2 placeholder_pos =
805 ImVec2(canvas_pos.x + map_x, canvas_pos.y + map_y);
809 draw_list->AddRectFilled(
811 ImVec2(placeholder_pos.x + placeholder_size.x,
812 placeholder_pos.y + placeholder_size.y),
813 IM_COL32(32, 32, 32, 128));
816 ImVec2 spinner_pos = ImVec2(placeholder_pos.x + placeholder_size.x / 2,
817 placeholder_pos.y + placeholder_size.y / 2);
819 const float spinner_radius = 8.0f;
820 const float rotation =
static_cast<float>(ImGui::GetTime()) * 3.0f;
821 const float start_angle = rotation;
822 const float end_angle = rotation +
IM_PI * 1.5f;
824 draw_list->PathArcTo(spinner_pos, spinner_radius, start_angle, end_angle,
826 draw_list->PathStroke(IM_COL32(100, 180, 100, 255), 0, 2.5f);
851 if (current_map_ < 0 || current_map_ >=
static_cast<int>(
maps_bmp_.size())) {
860 "Error: tile16_blockset_ is not properly initialized (active: %s, "
874 int mouse_x = mouse_position.x;
875 int mouse_y = mouse_position.y;
881 auto& selected_world =
886 int index_x = superX * 32 + tile16_x;
887 int index_y = superY * 32 + tile16_y;
893 const ImVec2& click_position,
const std::vector<uint8_t>& tile_data) {
896 if (current_map_ < 0 || current_map_ >=
static_cast<int>(
maps_bmp_.size())) {
898 "ERROR: RenderUpdatedMapBitmap - Invalid current_map_ %d "
899 "(maps_bmp_.size()=%zu)",
911 ImVec2 start_position;
912 start_position.x =
static_cast<float>(tile_index_x *
kTile16Size);
913 start_position.y =
static_cast<float>(tile_index_y *
kTile16Size);
919 if (!current_bitmap.
is_active() || current_bitmap.
size() == 0) {
922 "ERROR: RenderUpdatedMapBitmap - Bitmap %d is not active or has no "
923 "data (active=%s, size=%zu)",
925 current_bitmap.
size());
935 if (pixel_index < 0 ||
936 pixel_index >=
static_cast<int>(current_bitmap.
size())) {
939 "ERROR: RenderUpdatedMapBitmap - pixel_index %d out of bounds "
941 pixel_index, current_bitmap.
size());
947 if (tile_data_index < 0 ||
948 tile_data_index >=
static_cast<int>(tile_data.size())) {
951 "ERROR: RenderUpdatedMapBitmap - tile_data_index %d out of bounds "
952 "(tile_data size=%zu)",
953 tile_data_index, tile_data.size());
957 current_bitmap.
WriteToPixel(pixel_index, tile_data[tile_data_index]);
969 LOG_DEBUG(
"OverworldEditor",
"CheckForOverworldEdits: Frame %d",
970 ImGui::GetFrameCount());
983 if (ImGui::IsMouseClicked(ImGuiMouseButton_Left) ||
984 ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
986 "CheckForOverworldEdits: About to apply rectangle selection");
988 auto& selected_world =
1003 if (start_x > end_x)
1004 std::swap(start_x, end_x);
1005 if (start_y > end_y)
1006 std::swap(start_y, end_y);
1008 constexpr int local_map_size = 512;
1010 constexpr int tiles_per_local_map = local_map_size /
kTile16Size;
1013 "CheckForOverworldEdits: About to fill rectangle with "
1014 "current_tile16_=%d",
1021 for (
int y = start_y;
1024 for (
int x = start_x;
1029 int local_map_x = x / local_map_size;
1030 int local_map_y = y / local_map_size;
1033 int tile16_x = (x % local_map_size) /
kTile16Size;
1034 int tile16_y = (y % local_map_size) /
kTile16Size;
1037 int index_x = local_map_x * tiles_per_local_map + tile16_x;
1038 int index_y = local_map_y * tiles_per_local_map + tile16_y;
1044 int rect_width = ((end_x - start_x) /
kTile16Size) + 1;
1045 int rect_height = ((end_y - start_y) /
kTile16Size) + 1;
1049 int start_local_map_x = start_x / local_map_size;
1050 int start_local_map_y = start_y / local_map_size;
1051 int end_local_map_x = end_x / local_map_size;
1052 int end_local_map_y = end_y / local_map_size;
1054 bool in_same_local_map = (start_local_map_x == end_local_map_x) &&
1055 (start_local_map_y == end_local_map_y);
1057 if (in_same_local_map && index_x >= 0 &&
1058 (index_x + rect_width - 1) < 0x200 && index_y >= 0 &&
1059 (index_y + rect_height - 1) < 0x200) {
1060 selected_world[index_x][index_y] = tile16_id;
1063 ImVec2 tile_position(x, y);
1065 if (!tile_data.empty()) {
1069 "CheckForOverworldEdits: Updated bitmap at position (%d,%d) "
1070 "with tile16_id=%d",
1074 "ERROR: Failed to get tile data for tile16_id=%d",
1088 "CheckForOverworldEdits: Rectangle selection applied and cleared");
1127 return absl::FailedPreconditionError(
"Clipboard unavailable");
1132 std::vector<int> ids;
1136 static_cast<int>(std::floor(std::min(start.x, end.x) / 16.0f));
1138 static_cast<int>(std::floor(std::max(start.x, end.x) / 16.0f));
1140 static_cast<int>(std::floor(std::min(start.y, end.y) / 16.0f));
1142 static_cast<int>(std::floor(std::max(start.y, end.y) / 16.0f));
1143 const int width = end_x - start_x + 1;
1144 const int height = end_y - start_y + 1;
1145 ids.reserve(width * height);
1148 for (
int y = start_y; y <= end_y; ++y) {
1149 for (
int x = start_x; x <= end_x; ++x) {
1158 return absl::OkStatus();
1166 return absl::OkStatus();
1168 return absl::FailedPreconditionError(
"Nothing selected to copy");
1173 return absl::FailedPreconditionError(
"Clipboard unavailable");
1176 return absl::FailedPreconditionError(
"Clipboard empty");
1180 return absl::FailedPreconditionError(
"No paste target");
1187 const int tile16_x =
1189 const int tile16_y =
1192 auto& selected_world =
1199 const int tiles_per_local_map = 512 /
kTile16Size;
1206 if (width * height !=
static_cast<int>(ids.size())) {
1207 return absl::InternalError(
"Clipboard dimensions mismatch");
1210 for (
int dy = 0; dy < height; ++dy) {
1211 for (
int dx = 0; dx < width; ++dx) {
1212 const int id = ids[dy * width + dx];
1213 const int gx = tile16_x + dx;
1214 const int gy = tile16_y + dy;
1216 const int global_x = superX * 32 + gx;
1217 const int global_y = superY * 32 + gy;
1218 if (global_x < 0 || global_x >= 256 || global_y < 0 || global_y >= 256)
1220 selected_world[global_x][global_y] = id;
1225 return absl::OkStatus();
1233 const int large_map_size = 1024;
1241 int hovered_map = map_x + map_y * 8;
1243 hovered_map += 0x40;
1245 hovered_map += 0x80;
1261 bool use_v3_area_sizes = (asm_version >= 3);
1264 if (use_v3_area_sizes) {
1267 const int highlight_parent =
1275 parent_map_x = highlight_parent % 8;
1276 parent_map_y = highlight_parent / 8;
1279 parent_map_x = (highlight_parent - 0x40) % 8;
1280 parent_map_y = (highlight_parent - 0x40) / 8;
1283 parent_map_x = (highlight_parent - 0x80) % 8;
1284 parent_map_y = (highlight_parent - 0x80) / 8;
1288 switch (area_size) {
1289 case AreaSizeEnum::LargeArea:
1293 large_map_size, large_map_size);
1295 case AreaSizeEnum::WideArea:
1301 case AreaSizeEnum::TallArea:
1307 case AreaSizeEnum::SmallArea:
1319 const int highlight_parent =
1330 parent_map_x = highlight_parent % 8;
1331 parent_map_y = highlight_parent / 8;
1334 parent_map_x = (highlight_parent - 0x40) % 8;
1335 parent_map_y = (highlight_parent - 0x40) / 8;
1338 parent_map_x = (highlight_parent - 0x80) % 8;
1339 parent_map_y = (highlight_parent - 0x80) / 8;
1344 large_map_size, large_map_size);
1351 current_map_x = current_highlighted_map % 8;
1352 current_map_y = current_highlighted_map / 8;
1355 current_map_x = (current_highlighted_map - 0x40) % 8;
1356 current_map_y = (current_highlighted_map - 0x40) / 8;
1360 current_map_x = (current_highlighted_map - 0x80) % 8;
1361 current_map_y = (current_highlighted_map - 0x80) / 8;
1388 if (ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
1393 if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Right)) {
1397 return absl::OkStatus();
1407 constexpr float kWorldSize = 512.0f * 8.0f;
1408 return ImVec2(kWorldSize * scale, kWorldSize * scale);
1413 ImVec2 visible_size) {
1415 float max_scroll_x = std::max(0.0f, content_size.x - visible_size.x);
1416 float max_scroll_y = std::max(0.0f, content_size.y - visible_size.y);
1420 float clamped_x = std::clamp(scroll.x, -max_scroll_x, 0.0f);
1421 float clamped_y = std::clamp(scroll.y, -max_scroll_y, 0.0f);
1423 return ImVec2(clamped_x, clamped_y);
1430 if (ImGui::IsMouseDragging(ImGuiMouseButton_Middle) &&
1431 ImGui::IsItemHovered()) {
1435 ImVec2 mouse_delta = ImGui::GetIO().MouseDelta;
1437 ImVec2 new_scroll = ImVec2(current_scroll.x + mouse_delta.x,
1438 current_scroll.y + mouse_delta.y);
1441 ImVec2 content_size =
1444 new_scroll = ClampScrollPosition(new_scroll, content_size, visible_size);
1449 if (ImGui::IsMouseReleased(ImGuiMouseButton_Middle) &&
1456 if (!ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows)) {
1460 const ImGuiIO& io = ImGui::GetIO();
1463 if (io.MouseWheel != 0.0f && io.KeyCtrl) {
1465 float zoom_delta = io.MouseWheel * 0.1f;
1466 float new_scale = current_scale + zoom_delta;
1469 new_scale = std::clamp(new_scale, 0.25f, 2.0f);
1471 if (new_scale != current_scale) {
1473 ImVec2 mouse_pos_canvas =
1479 ImVec2 content_pos_before =
1480 ImVec2((mouse_pos_canvas.x - scroll.x) / current_scale,
1481 (mouse_pos_canvas.y - scroll.y) / current_scale);
1488 ImVec2(mouse_pos_canvas.x - (content_pos_before.x * new_scale),
1489 mouse_pos_canvas.y - (content_pos_before.y * new_scale));
1492 ImVec2 content_size = CalculateOverworldContentSize(new_scale);
1494 new_scroll = ClampScrollPosition(new_scroll, content_size, visible_size);
1508 ImVec2 content_size = CalculateOverworldContentSize(scale);
1512 ImVec2 centered_scroll = ImVec2(-(content_size.x - visible_size.x) / 2.0f,
1513 -(content_size.y - visible_size.y) / 2.0f);
1597 ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
1608 ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
1609 ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
1610 ImVec2 mouse_delta = ImGui::GetIO().MouseDelta;
1620 ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
1644 ImGui::BeginGroup();
1658 "OwBlocksetSelector", selector_config);
1668 if (result.selection_changed) {
1682 if (result.tile_double_clicked) {
1688 return absl::OkStatus();
1697 int offset = 0x40 * (key + 1);
1702 auto texture = value.texture();
1704 (ImTextureID)(intptr_t)texture,
1731 ImGui::BeginGroup();
1748 return absl::OkStatus();
1771 return absl::OkStatus();
1777 LOG_DEBUG(
"OverworldEditor",
"Loading overworld.");
1785 LOG_DEBUG(
"OverworldEditor",
"Loading overworld graphics (optimized).");
1798 "Loading overworld tileset (deferred textures).");
1810 LOG_DEBUG(
"OverworldEditor",
"Loading overworld tile16 graphics.");
1828 constexpr int kEssentialMapsPerWorld = 8;
1829 constexpr int kLightWorldEssential = kEssentialMapsPerWorld;
1830 constexpr int kDarkWorldEssential =
1832 constexpr int kSpecialWorldEssential =
1837 "Creating bitmaps for essential maps only (first %d maps per world)",
1838 kEssentialMapsPerWorld);
1840 std::vector<gfx::Bitmap*> maps_to_texture;
1841 maps_to_texture.reserve(kEssentialMapsPerWorld *
1847 bool is_essential =
false;
1850 if (i < kLightWorldEssential) {
1851 is_essential =
true;
1853 is_essential =
true;
1855 i < kSpecialWorldEssential) {
1856 is_essential =
true;
1867 maps_to_texture.push_back(&
maps_bmp_[i]);
1868 }
catch (
const std::bad_alloc& e) {
1869 std::cout <<
"Error allocating map " << i <<
": " << e.what()
1880 const int initial_texture_count =
1881 std::min(4,
static_cast<int>(maps_to_texture.size()));
1884 for (
int i = 0; i < initial_texture_count; ++i) {
1893 for (
size_t i = initial_texture_count; i < maps_to_texture.size(); ++i) {
1897 if (&
maps_bmp_[j] == maps_to_texture[i]) {
1904 if (map_index >= 0) {
1905 int map_world = map_index / 0x40;
1924 return absl::OkStatus();
1929 const int depth = 0x10;
1930 for (
int i = 0; i < 3; i++)
1932 int width = sprite.width();
1933 int height = sprite.height();
1934 if (width == 0 || height == 0) {
1941 *sprite.preview_graphics());
1946 return absl::OkStatus();
1956 int refresh_count = 0;
1957 const int max_refreshes_per_frame = 2;
1967 if (is_current_map || is_current_world) {
1983 LOG_ERROR(
"OverworldEditor",
"Failed to build map %d: %s", map_index,
1991 if (!bitmap.is_active()) {
1997 bitmap.SetPalette(palette);
1998 }
catch (
const std::bad_alloc& e) {
1999 LOG_ERROR(
"OverworldEditor",
"Error allocating bitmap for map %d: %s",
2000 map_index, e.what());
2005 if (!bitmap.texture() && bitmap.is_active()) {
2023 maps_bmp_[map_index].set_modified(
true);
2049 if (!is_current_map && !is_current_world) {
2051 maps_bmp_[map_index].set_modified(
true);
2066 bool needs_graphics_rebuild =
maps_bmp_[map_index].modified();
2067 bool needs_palette_rebuild =
false;
2069 if (needs_graphics_rebuild) {
2071 map->LoadAreaGraphics();
2074 auto status = map->BuildTileset();
2076 LOG_ERROR(
"OverworldEditor",
"Failed to build tileset for map %d: %s",
2077 map_index, status.message().data());
2086 "Failed to build tiles16 graphics for map %d: %s", map_index,
2087 status.message().data());
2094 LOG_ERROR(
"OverworldEditor",
"Failed to build bitmap for map %d: %s",
2095 map_index, status.message().data());
2100 maps_bmp_[map_index].set_data(map->bitmap_data());
2101 maps_bmp_[map_index].set_modified(
false);
2104 if (!
maps_bmp_[map_index].ValidateDataSurfaceSync()) {
2106 "Warning: Surface synchronization issue detected for map %d",
2123 bool use_v3_area_sizes = (asm_version >= 3 && asm_version != 0xFF);
2125 if (use_v3_area_sizes) {
2130 if (map->is_large_map()) {
2148 static std::set<int> currently_processing;
2149 if (currently_processing.count(map_index)) {
2154 if (area_size == AreaSizeEnum::SmallArea) {
2160 "RefreshMultiAreaMapsSafely: Processing %s area map %d (parent: %d)",
2161 (area_size == AreaSizeEnum::LargeArea) ?
"large"
2162 : (area_size == AreaSizeEnum::WideArea) ?
"wide"
2164 map_index, map->
parent());
2167 std::vector<int> sibling_maps;
2168 int parent_id = map->
parent();
2171 switch (area_size) {
2172 case AreaSizeEnum::LargeArea: {
2176 sibling_maps = {parent_id, parent_id + 1, parent_id + 8, parent_id + 9};
2179 "RefreshMultiAreaMapsSafely: Large area siblings: %d, %d, %d, %d",
2180 parent_id, parent_id + 1, parent_id + 8, parent_id + 9);
2184 case AreaSizeEnum::WideArea: {
2187 sibling_maps = {parent_id, parent_id + 1};
2189 "RefreshMultiAreaMapsSafely: Wide area siblings: %d, %d",
2190 parent_id, parent_id + 1);
2194 case AreaSizeEnum::TallArea: {
2197 sibling_maps = {parent_id, parent_id + 8};
2199 "RefreshMultiAreaMapsSafely: Tall area siblings: %d, %d",
2200 parent_id, parent_id + 8);
2206 "RefreshMultiAreaMapsSafely: Unknown area size %d for map %d",
2207 static_cast<int>(area_size), map_index);
2212 for (
int sibling : sibling_maps) {
2213 currently_processing.insert(sibling);
2217 for (
int sibling : sibling_maps) {
2218 if (sibling == map_index) {
2230 bool needs_refresh =
maps_bmp_[sibling].modified();
2232 if ((is_current_map || is_current_world) && needs_refresh) {
2234 "RefreshMultiAreaMapsSafely: Refreshing %s area sibling map %d "
2236 (area_size == AreaSizeEnum::LargeArea) ?
"large"
2237 : (area_size == AreaSizeEnum::WideArea) ?
"wide"
2239 sibling, parent_id);
2243 if (sibling_map &&
maps_bmp_[sibling].modified()) {
2244 sibling_map->LoadAreaGraphics();
2246 auto status = sibling_map->BuildTileset();
2252 status = sibling_map->LoadPalette();
2254 status = sibling_map->BuildBitmap(
2257 maps_bmp_[sibling].set_data(sibling_map->bitmap_data());
2283 "RefreshMultiAreaMapsSafely: Failed to refresh sibling map %d: "
2285 sibling, status.message().data());
2288 }
else if (!is_current_map && !is_current_world) {
2295 for (
int sibling : sibling_maps) {
2296 currently_processing.erase(sibling);
2307 bool use_v3_area_sizes = (asm_version >= 3 && asm_version != 0xFF);
2309 if (use_v3_area_sizes) {
2314 if (area_size != AreaSizeEnum::SmallArea) {
2316 std::vector<int> sibling_maps;
2319 switch (area_size) {
2320 case AreaSizeEnum::LargeArea:
2322 sibling_maps = {parent_id, parent_id + 1, parent_id + 8,
2325 case AreaSizeEnum::WideArea:
2327 sibling_maps = {parent_id, parent_id + 1};
2329 case AreaSizeEnum::TallArea:
2331 sibling_maps = {parent_id, parent_id + 8};
2338 for (
int sibling_index : sibling_maps) {
2344 maps_bmp_[sibling_index].SetPalette(current_map_palette);
2354 for (
int i = 1; i < 4; i++) {
2363 if (
maps_bmp_[sibling_index].is_active() &&
2365 maps_bmp_[sibling_index].SetPalette(current_map_palette);
2377 return absl::OkStatus();
2382 if (map_index >= 0 && map_index <
static_cast<int>(
maps_bmp_.size())) {
2383 maps_bmp_[map_index].set_modified(
true);
2389 "ForceRefreshGraphics: Map %d marked for refresh", map_index);
2394 bool include_self) {
2395 if (map_index < 0 || map_index >=
static_cast<int>(
maps_bmp_.size())) {
2404 int parent_id = map->parent();
2405 std::vector<int> siblings;
2407 switch (map->area_size()) {
2409 siblings = {parent_id, parent_id + 1, parent_id + 8, parent_id + 9};
2412 siblings = {parent_id, parent_id + 1};
2415 siblings = {parent_id, parent_id + 8};
2421 for (
int sibling : siblings) {
2422 if (sibling >= 0 && sibling < 0xA0) {
2424 if (sibling == map_index && !include_self) {
2439 "RefreshSiblingMapGraphics: Refreshed sibling map %d", sibling);
2449 bool use_v3_area_sizes = (asm_version >= 3);
2451 if (use_v3_area_sizes) {
2454 auto area_size = current_ow_map.area_size();
2456 if (area_size != AreaSizeEnum::SmallArea) {
2458 std::vector<int> sibling_maps;
2459 int parent_id = current_ow_map.parent();
2461 switch (area_size) {
2462 case AreaSizeEnum::LargeArea:
2464 sibling_maps = {parent_id + 1, parent_id + 8, parent_id + 9};
2466 case AreaSizeEnum::WideArea:
2468 sibling_maps = {parent_id + 1};
2470 case AreaSizeEnum::TallArea:
2472 sibling_maps = {parent_id + 8};
2479 for (
int sibling_index : sibling_maps) {
2484 map.set_area_graphics(current_ow_map.area_graphics());
2485 map.set_area_palette(current_ow_map.area_palette());
2490 map.set_message_id(current_ow_map.message_id());
2493 map.LoadAreaGraphics();
2498 if (current_ow_map.is_large_map()) {
2500 for (
int i = 1; i < 4; i++) {
2501 int sibling_index = current_ow_map.parent() + i;
2506 map.set_area_graphics(current_ow_map.area_graphics());
2507 map.set_area_palette(current_ow_map.area_palette());
2512 map.set_message_id(current_ow_map.message_id());
2515 map.LoadAreaGraphics();
2522 LOG_DEBUG(
"OverworldEditor",
"RefreshTile16Blockset called");
2525 return absl::OkStatus();
2548 return absl::OkStatus();
2553 if (ImGui::IsMouseClicked(ImGuiMouseButton_Middle) &&
2554 ImGui::IsItemHovered()) {
2559 int hovered_map = map_x + map_y * 8;
2561 hovered_map += 0x40;
2563 hovered_map += 0x80;
2567 if (hovered_map >= 0 && hovered_map < 0xA0) {
2580 if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) &&
2581 ImGui::IsItemHovered()) {
2609 static bool init_properties =
false;
2611 if (!init_properties) {
2612 for (
int i = 0; i < 0x40; i++) {
2613 std::string area_graphics_str = absl::StrFormat(
2616 ->push_back(area_graphics_str);
2618 area_graphics_str = absl::StrFormat(
2621 ->push_back(area_graphics_str);
2623 std::string area_palette_str =
2626 ->push_back(area_palette_str);
2628 area_palette_str = absl::StrFormat(
2631 ->push_back(area_palette_str);
2632 std::string sprite_gfx_str = absl::StrFormat(
2635 ->push_back(sprite_gfx_str);
2637 sprite_gfx_str = absl::StrFormat(
2640 ->push_back(sprite_gfx_str);
2642 sprite_gfx_str = absl::StrFormat(
2645 ->push_back(sprite_gfx_str);
2647 sprite_gfx_str = absl::StrFormat(
2650 ->push_back(sprite_gfx_str);
2652 std::string sprite_palette_str = absl::StrFormat(
2655 ->push_back(sprite_palette_str);
2657 sprite_palette_str = absl::StrFormat(
2660 ->push_back(sprite_palette_str);
2662 sprite_palette_str = absl::StrFormat(
2665 ->push_back(sprite_palette_str);
2667 sprite_palette_str = absl::StrFormat(
2670 ->push_back(sprite_palette_str);
2672 init_properties =
true;
2675 ImGui::Text(
"Area Gfx LW/DW");
2683 ImGui::Text(
"Sprite Gfx LW/DW");
2697 ImGui::Text(
"Area Pal LW/DW");
2704 static bool show_gfx_group =
false;
2705 ImGui::Checkbox(
"Show Gfx Group Editor", &show_gfx_group);
2706 if (show_gfx_group) {
2714 if (ImGui::BeginTable(
2715 "UsageStatsTable", 3,
2716 ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter,
2718 ImGui::TableSetupColumn(
"Entrances");
2719 ImGui::TableSetupColumn(
"Grid", ImGuiTableColumnFlags_WidthStretch,
2720 ImGui::GetContentRegionAvail().x);
2721 ImGui::TableSetupColumn(
"Usage", ImGuiTableColumnFlags_WidthFixed, 256);
2722 ImGui::TableHeadersRow();
2723 ImGui::TableNextRow();
2725 ImGui::TableNextColumn();
2726 if (ImGui::BeginChild(
"UnusedSpritesetScroll", ImVec2(0, 0),
true,
2727 ImGuiWindowFlags_HorizontalScrollbar)) {
2728 for (
int i = 0; i < 0x81; i++) {
2732 std::string str = absl::StrFormat(
"%#x - %s", i, entrance_name);
2735 ? ImGuiSelectableFlags_Disabled
2741 if (ImGui::IsItemHovered()) {
2742 ImGui::BeginTooltip();
2743 ImGui::Text(
"Entrance ID: %d", i);
2745 ImGui::Text(
"Entrance ID: %d",
2749 ImGui::Text(
"Deleted? %s",
2751 ImGui::EndTooltip();
2757 ImGui::TableNextColumn();
2760 ImGui::TableNextColumn();
2765 return absl::OkStatus();
2770 int total_squares = 128;
2771 int squares_wide = 8;
2772 int squares_tall = (total_squares + squares_wide - 1) /
2776 for (
int row = 0; row < squares_tall; ++row) {
2779 for (
int col = 0; col < squares_wide; ++col) {
2780 if (row * squares_wide + col >= total_squares) {
2792 if (ImGui::Button(
"##square", ImVec2(20, 20))) {
2800 ImGui::PopStyleColor();
2804 if (ImGui::IsItemHovered()) {
2819 ImGui::Text(
"Current Tile16 Drawn Position (Relative): %d, %d", relative_x,
2823 ImGui::Text(
"Light World Map Tiles: %d",
2825 ImGui::Text(
"Dark World Map Tiles: %d",
2827 ImGui::Text(
"Special World Map Tiles: %d",
2830 static bool view_lw_map_tiles =
false;
2831 static MemoryEditor mem_edit;
2833 if (ImGui::Button(
"View Light World Map Tiles")) {
2834 view_lw_map_tiles = !view_lw_map_tiles;
2837 if (view_lw_map_tiles) {
2838 mem_edit.DrawContents(
2849 return absl::OkStatus();
2857 if (target_version < 2 || target_version > 3) {
2858 return absl::InvalidArgumentError(absl::StrFormat(
2859 "Invalid target version: %d. Must be 2 or 3.", target_version));
2864 if (current_version != 0xFF && current_version >= target_version) {
2865 return absl::AlreadyExistsError(absl::StrFormat(
2866 "ROM is already version %d or higher", current_version));
2869 LOG_DEBUG(
"OverworldEditor",
"Applying ZSCustomOverworld ASM v%d to ROM...",
2873 auto asar_wrapper = std::make_unique<core::AsarWrapper>();
2877 std::vector<uint8_t> original_rom_data =
rom_->
vector();
2878 std::vector<uint8_t> working_rom_data = original_rom_data;
2882 std::string asm_file_name =
2883 (target_version == 3) ?
"asm/yaze.asm"
2884 :
"asm/ZSCustomOverworld.asm";
2889 LOG_DEBUG(
"OverworldEditor",
"Using ASM file: %s", asm_file_path.c_str());
2892 if (!std::filesystem::exists(asm_file_path)) {
2893 return absl::NotFoundError(
2894 absl::StrFormat(
"ASM file not found at: %s\n\n"
2895 "Expected location: assets/%s\n"
2896 "Make sure the assets directory is accessible.",
2897 asm_file_path, asm_file_name));
2902 asar_wrapper->ApplyPatch(asm_file_path, working_rom_data);
2903 if (!patch_result.ok()) {
2904 return absl::InternalError(absl::StrFormat(
2905 "Failed to apply ASM patch: %s", patch_result.status().message()));
2908 const auto& result = patch_result.value();
2909 if (!result.success) {
2910 std::string error_details =
"ASM patch failed with errors:\n";
2911 for (
const auto& error : result.errors) {
2912 error_details +=
" - " + error +
"\n";
2914 if (!result.warnings.empty()) {
2915 error_details +=
"Warnings:\n";
2916 for (
const auto& warning : result.warnings) {
2917 error_details +=
" - " + warning +
"\n";
2920 return absl::InternalError(error_details);
2931 "ASM patch applied successfully. Found %zu symbols:",
2932 result.symbols.size());
2933 for (
const auto& symbol : result.symbols) {
2934 LOG_DEBUG(
"OverworldEditor",
" %s @ $%06X", symbol.name.c_str(),
2942 "ZSCustomOverworld v%d successfully applied to ROM",
2944 return absl::OkStatus();
2946 }
catch (
const std::exception& e) {
2949 if (!restore_result.ok()) {
2950 LOG_ERROR(
"OverworldEditor",
"Failed to restore ROM data: %s",
2951 restore_result.message().data());
2953 return absl::InternalError(
2954 absl::StrFormat(
"Exception during ASM application: %s", e.what()));
2961 static_cast<uint8_t
>(target_version);
2964 if (target_version >= 2) {
2970 "Enabled v2+ features: Custom BG colors, Main palettes");
2973 if (target_version >= 3) {
2982 "Enabled v3+ features: Subscreen overlays, Animated GFX, Tile GFX "
2986 for (
int i = 0; i < 0xA0; i++) {
2992 const std::vector<int> large_areas = {
2993 0x00, 0x02, 0x05, 0x07, 0x0A, 0x0B, 0x0F, 0x10, 0x11, 0x12,
2994 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1D,
2995 0x1E, 0x25, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2E, 0x2F, 0x30,
2996 0x32, 0x33, 0x34, 0x35, 0x37, 0x3A, 0x3B, 0x3C, 0x3F};
2998 for (
int area_id : large_areas) {
2999 if (area_id < 0xA0) {
3005 LOG_DEBUG(
"OverworldEditor",
"Initialized area size data for %zu areas",
3006 large_areas.size());
3009 LOG_DEBUG(
"OverworldEditor",
"ROM version markers updated to v%d",
3011 return absl::OkStatus();
3036 api->SetTileQueryCallback(
3046 if (x < 0 || y < 0 || x >= 512 || y >= 512) {
3059 if (!tile_data.empty()) {
3061 ImVec2(
static_cast<float>(x * 16),
static_cast<float>(y * 16)),
3075 if (x < 0 || y < 0 || x >= 512 || y >= 512) {
3088 LOG_ERROR(
"OverworldEditor",
"Cannot insert entity: overworld not loaded");
3096 "HandleEntityInsertion called: type='%s' at pos=(%.0f,%.0f) map=%d",
3097 entity_type.c_str(), mouse_pos.x, mouse_pos.y,
current_map_);
3099 if (entity_type ==
"entrance") {
3104 ImGui::OpenPopup(
"Entrance Editor");
3106 LOG_DEBUG(
"OverworldEditor",
"Entrance inserted successfully at map=%d",
3109 LOG_ERROR(
"OverworldEditor",
"Failed to insert entrance: %s",
3110 result.status().message().data());
3113 }
else if (entity_type ==
"hole") {
3118 ImGui::OpenPopup(
"Entrance Editor");
3120 LOG_DEBUG(
"OverworldEditor",
"Hole inserted successfully at map=%d",
3123 LOG_ERROR(
"OverworldEditor",
"Failed to insert hole: %s",
3124 result.status().message().data());
3127 }
else if (entity_type ==
"exit") {
3132 ImGui::OpenPopup(
"Exit editor");
3134 LOG_DEBUG(
"OverworldEditor",
"Exit inserted successfully at map=%d",
3137 LOG_ERROR(
"OverworldEditor",
"Failed to insert exit: %s",
3138 result.status().message().data());
3141 }
else if (entity_type ==
"item") {
3146 ImGui::OpenPopup(
"Item editor");
3148 LOG_DEBUG(
"OverworldEditor",
"Item inserted successfully at map=%d",
3151 LOG_ERROR(
"OverworldEditor",
"Failed to insert item: %s",
3152 result.status().message().data());
3155 }
else if (entity_type ==
"sprite") {
3161 ImGui::OpenPopup(
"Sprite editor");
3163 LOG_DEBUG(
"OverworldEditor",
"Sprite inserted successfully at map=%d",
3166 LOG_ERROR(
"OverworldEditor",
"Failed to insert sprite: %s",
3167 result.status().message().data());
3171 LOG_WARN(
"OverworldEditor",
"Unknown entity type: %s", entity_type.c_str());
project::ResourceLabelManager * resource_label()
absl::Status LoadFromData(const std::vector< uint8_t > &data, bool z3_load=true)
void set_dirty(bool dirty)
void RegisterCard(size_t session_id, const CardInfo &base_info)
Register a card for a specific session.
EditorDependencies dependencies_
std::string MakeCardTitle(const std::string &base_title) const
std::string MakeCardId(const std::string &base_id) const
absl::Status Clear() override
std::unique_ptr< MapPropertiesSystem > map_properties_system_
zelda3::OverworldItem current_item_
void HandleMapInteraction()
bool overworld_canvas_fullscreen_
bool map_blockset_loaded_
absl::Status UpdateUsageStats()
absl::Status DrawScratchSpace()
zelda3::OverworldEntranceTileTypes entrance_tiletypes_
zelda3::OverworldEntrance current_entrance_
void CenterOverworldView()
absl::Status ApplyZSCustomOverworldASM(int target_version)
Apply ZSCustomOverworld ASM patch to upgrade ROM version.
zelda3::Sprite current_sprite_
absl::Status CheckForCurrentMap()
Check for changes to the overworld map. Calls RefreshOverworldMap and RefreshTile16Blockset on the cu...
void DrawOverworldEdits()
void ForceRefreshGraphics(int map_index)
std::vector< int > selected_tile16_ids_
gfx::Bitmap current_gfx_bmp_
gfx::Tilemap tile16_blockset_
bool show_tile8_selector_
void ResetOverworldView()
bool middle_mouse_dragging_
Tile16Editor tile16_editor_
gui::Canvas ow_map_canvas_
void HandleEntityInsertion(const std::string &entity_type)
Handle entity insertion from context menu.
zelda3::GameEntity * dragged_entity_
bool show_tile16_selector_
std::array< gfx::Bitmap, zelda3::kNumOverworldMaps > maps_bmp_
bool show_map_properties_panel_
void RefreshOverworldMap()
void CheckForOverworldEdits()
Check for changes to the overworld map.
absl::Status UpdateROMVersionMarkers(int target_version)
Update ROM version markers and feature flags after ASM patching.
zelda3::OverworldExit current_exit_
void RefreshMapProperties()
void RefreshSiblingMapGraphics(int map_index, bool include_self=false)
void RenderUpdatedMapBitmap(const ImVec2 &click_position, const std::vector< uint8_t > &tile_data)
void RefreshOverworldMapOnDemand(int map_index)
On-demand map refresh that only updates what's actually needed.
gui::Canvas current_gfx_canvas_
void Initialize() override
bool show_overlay_editor_
void SetupCanvasAutomation()
void HandleOverworldPan()
void DrawFullscreenCanvas()
bool dragged_entity_free_movement_
void DrawOverworldProperties()
bool AutomationSetTile(int x, int y, int tile_id)
absl::Status RefreshMapPalette()
void RefreshMultiAreaMapsSafely(int map_index, zelda3::OverworldMap *map)
Safely refresh multi-area maps without recursion.
void DrawOverworldCanvas()
gui::Canvas blockset_canvas_
absl::Status RefreshTile16Blockset()
zelda3::Overworld & overworld()
void UpdateBlocksetSelectorState()
void CheckForSelectRectangle()
Draw and create the tile16 IDs that are currently selected.
bool show_overworld_canvas_
void RefreshChildMap(int map_index)
std::unique_ptr< OverworldEntityRenderer > entity_renderer_
absl::Status Load() override
void EnsureMapTexture(int map_index)
Ensure a specific map has its texture created.
std::vector< gfx::Bitmap > sprite_previews_
absl::Status Copy() override
absl::Status DrawAreaGraphics()
EntityEditMode entity_edit_mode_
GfxGroupEditor gfx_group_editor_
void ProcessDeferredTextures()
Create textures for deferred map bitmaps on demand.
absl::Status Update() final
std::unique_ptr< gui::TileSelectorWidget > blockset_selector_
bool show_overlay_preview_
absl::Status Paste() override
void ScrollBlocksetCanvasToCurrentTile()
Scroll the blockset canvas to show the current selected tile16.
gui::Canvas graphics_bin_canvas_
gfx::BitmapTable current_graphics_set_
bool show_custom_bg_color_editor_
zelda3::Overworld overworld_
absl::Status LoadGraphics()
Load the Bitmap objects for each OverworldMap.
void RefreshChildMapOnDemand(int map_index)
On-demand child map refresh with selective updates.
absl::Status LoadSpriteGraphics()
gfx::IRenderer * renderer_
absl::Status Save() override
void HandleOverworldZoom()
zelda3::GameEntity * current_entity_
gui::Canvas properties_canvas_
int AutomationGetTile(int x, int y)
gfx::SnesPalette palette_
gfx::Bitmap tile16_blockset_bmp_
absl::Status DrawTile16Selector()
absl::Status Initialize(const gfx::Bitmap &tile16_blockset_bmp, const gfx::Bitmap ¤t_gfx_bmp, std::array< uint8_t, 0x200 > &all_tiles_types)
absl::Status SetCurrentTile(int id)
void set_palette(const gfx::SnesPalette &palette)
void set_on_changes_committed(std::function< absl::Status()> callback)
void QueueTextureCommand(TextureCommandType type, Bitmap *bitmap)
void ProcessTextureQueue(IRenderer *renderer)
std::array< gfx::Bitmap, 223 > & gfx_sheets()
Get reference to all graphics sheets.
Represents a bitmap image optimized for SNES ROM hacking.
void WriteToPixel(int position, uint8_t value)
Write a value to a pixel at the given position.
TextureHandle texture() const
const std::vector< uint8_t > & vector() const
void set_modified(bool modified)
void SetPalette(const SnesPalette &palette)
Set the palette for the bitmap.
SDL_Surface * surface() const
RAII timer for automatic timing management.
void SetTilePaintCallback(TilePaintCallback callback)
void set_scrolling(ImVec2 scroll)
auto selected_tile_pos() const
void DrawBitmap(Bitmap &bitmap, int border_offset, float scale)
auto global_scale() const
auto select_rect_active() const
void SetUsageMode(CanvasUsage usage)
auto selected_tiles() const
void DrawBitmapGroup(std::vector< int > &group, gfx::Tilemap &tilemap, int tile_size, float scale=1.0f, int local_map_size=0x200, ImVec2 total_map_size=ImVec2(0x1000, 0x1000))
Draw group of bitmaps for multi-tile selection preview.
CanvasAutomationAPI * GetAutomationAPI()
auto hover_mouse_pos() const
void UpdateInfoGrid(ImVec2 bg_size, float grid_size=64.0f, int label_id=0)
auto drawn_tile_position() const
bool DrawTilemapPainter(gfx::Tilemap &tilemap, int current_tile)
bool DrawTileSelector(int size, int size_y=0)
auto mutable_labels(int i)
void set_selected_tile_pos(ImVec2 pos)
void set_global_scale(float scale)
void DrawSelectRect(int current_map, int tile_size=0x10, float scale=1.0f)
bool IsMouseHovering() const
void DrawOutline(int x, int y, int w, int h)
void DrawBackground(ImVec2 canvas_size=ImVec2(0, 0))
const ImVector< ImVec2 > & points() const
void DrawGrid(float grid_step=64.0f, int tile_id_offset=8)
auto selected_points() const
auto set_highlight_tile_id(int i)
Draggable, dockable card for editor sub-windows.
bool Begin(bool *p_open=nullptr)
void SetDefaultSize(float width, float height)
void SetPosition(Position pos)
enum yaze::zelda3::GameEntity::EntityType entity_type_
virtual void UpdateMapProperties(uint16_t map_id)=0
Represents a single Overworld map screen.
auto tile16_blockset_data() const
auto current_area_palette() const
void set_current_world(int world)
int GetTileFromPosition(ImVec2 position) const
absl::Status Load(Rom *rom)
absl::Status SaveMapProperties()
absl::Status SaveMap32Tiles()
absl::Status SaveMap16Tiles()
std::vector< gfx::Tile16 > tiles16() const
absl::Status CreateTile32Tilemap()
auto overworld_map(int i) const
void set_current_map(int i)
auto mutable_overworld_map(int i)
absl::Status SaveEntrances()
absl::Status EnsureMapBuilt(int map_index)
Build a map on-demand if it hasn't been built yet.
uint16_t GetTile(int x, int y) const
absl::Status SaveOverworldMaps()
auto mutable_all_tiles_types()
void SetTile(int x, int y, uint16_t tile_id)
auto mutable_sprites(int state)
const std::vector< OverworldEntrance > & entrances() const
auto current_map_bitmap_data() const
OverworldBlockset & GetMapTiles(int world_type)
A class for managing sprites in the overworld and underworld.
#define ICON_MD_GRID_VIEW
#define ICON_MD_COLLECTIONS
#define ICON_MD_OPEN_IN_FULL
#define ICON_MD_FORMAT_COLOR_FILL
#define ICON_MD_DOOR_BACK
#define ICON_MD_MUSIC_NOTE
#define ICON_MD_DOOR_FRONT
#define ICON_MD_ADD_LOCATION
#define ICON_MD_PEST_CONTROL_RODENT
#define ICON_MD_ANALYTICS
#define LOG_DEBUG(category, format,...)
#define LOG_ERROR(category, format,...)
#define LOG_WARN(category, format,...)
#define PRINT_IF_ERROR(expression)
#define RETURN_IF_ERROR(expression)
#define ASSIGN_OR_RETURN(type_variable_name, expression)
ImVec2 CalculateOverworldContentSize(float scale)
ImVec2 ClampScrollPosition(ImVec2 scroll, ImVec2 content_size, ImVec2 visible_size)
Editors are the view controllers for the application.
void MoveEntityOnGrid(zelda3::GameEntity *entity, ImVec2 canvas_p0, ImVec2 scrolling, bool free_movement)
absl::StatusOr< zelda3::OverworldItem * > InsertItem(zelda3::Overworld *overworld, ImVec2 mouse_pos, int current_map, uint8_t item_id)
Insert a new item at the specified position.
absl::StatusOr< zelda3::OverworldEntrance * > InsertEntrance(zelda3::Overworld *overworld, ImVec2 mouse_pos, int current_map, bool is_hole)
Flat helper functions for entity insertion/manipulation.
constexpr unsigned int kOverworldMapSize
bool DrawSpriteEditorPopup(zelda3::Sprite &sprite)
bool DrawOverworldEntrancePopup(zelda3::OverworldEntrance &entrance)
bool DrawItemEditorPopup(zelda3::OverworldItem &item)
absl::StatusOr< zelda3::OverworldExit * > InsertExit(zelda3::Overworld *overworld, ImVec2 mouse_pos, int current_map)
Insert a new exit at the specified position.
constexpr int kTile16Size
absl::StatusOr< zelda3::Sprite * > InsertSprite(zelda3::Overworld *overworld, ImVec2 mouse_pos, int current_map, int game_state, uint8_t sprite_id)
Insert a new sprite at the specified position.
bool DrawExitEditorPopup(zelda3::OverworldExit &exit)
void UpdateTilemap(IRenderer *renderer, Tilemap &tilemap, const std::vector< uint8_t > &data)
std::vector< uint8_t > GetTilemapData(Tilemap &tilemap, int tile_id)
Tilemap CreateTilemap(IRenderer *renderer, std::vector< uint8_t > &data, int width, int height, int tile_size, int num_tiles, SnesPalette &palette)
void BeginChildBothScrollbars(int id)
ImVec4 GetSelectedColor()
void CenterText(const char *text)
void EndWindowWithDisplaySettings()
void BeginWindowWithDisplaySettings(const char *id, bool *active, const ImVec2 &size, ImGuiWindowFlags flags)
void BeginChildWithScrollbar(const char *str_id)
std::string HexByte(uint8_t byte, HexStringParams params)
std::string GetResourcePath(const std::string &resource_path)
constexpr int OverworldCustomTileGFXGroupEnabled
constexpr int OverworldCustomAreaSpecificBGEnabled
constexpr int kOverworldScreenSize
constexpr int kNumTile16Individual
constexpr int kSpecialWorldMapIdStart
constexpr int OverworldCustomAnimatedGFXEnabled
constexpr int OverworldCustomMainPaletteEnabled
constexpr int kNumOverworldMaps
constexpr int OverworldCustomASMHasBeenApplied
constexpr int kDarkWorldMapIdStart
absl::StatusOr< OverworldEntranceTileTypes > LoadEntranceTileTypes(Rom *rom)
constexpr int OverworldCustomMosaicEnabled
constexpr const char * kEntranceNames[]
constexpr int OverworldCustomSubscreenOverlayEnabled
bool has_overworld_tile16
std::vector< int > overworld_tile16_ids
SharedClipboard * shared_clipboard
EditorCardRegistry * card_registry
Bitmap atlas
Master bitmap containing all tiles.
std::string CreateOrGetLabel(const std::string &type, const std::string &key, const std::string &defaultValue)