5#define IM_PI 3.14159265358979323846f
22#include <unordered_map>
27#include "absl/status/status.h"
28#include "absl/strings/str_format.h"
29#include "imgui/imgui.h"
100 panel_manager->RegisterEditorPanel(std::make_unique<AreaGraphicsPanel>(
this));
101 panel_manager->RegisterEditorPanel(
102 std::make_unique<Tile16SelectorPanel>(
this));
103 panel_manager->RegisterEditorPanel(
105 panel_manager->RegisterEditorPanel(
106 std::make_unique<MapPropertiesPanel>(
this));
107 panel_manager->RegisterEditorPanel(std::make_unique<ScratchSpacePanel>(
this));
108 panel_manager->RegisterEditorPanel(
109 std::make_unique<UsageStatisticsPanel>(
this));
110 panel_manager->RegisterEditorPanel(
111 std::make_unique<Tile8SelectorPanel>(
this));
112 panel_manager->RegisterEditorPanel(std::make_unique<DebugWindowPanel>(
this));
113 panel_manager->RegisterEditorPanel(std::make_unique<GfxGroupsPanel>(
this));
114 panel_manager->RegisterEditorPanel(std::make_unique<V3SettingsPanel>(
this));
116 panel_manager->RegisterEditorPanel(
117 std::make_unique<OverworldCanvasPanel>(
this));
150 toolbar_ = std::make_unique<OverworldToolbar>();
151 toolbar_->on_refresh_graphics = [
this]() {
157 toolbar_->on_refresh_map = [
this]() {
161 toolbar_->on_save_to_scratch = [
this]() {
164 toolbar_->on_load_from_scratch = [
this]() {
174 LOG_DEBUG(
"OverworldEditor",
"Loading overworld.");
176 return absl::FailedPreconditionError(
"ROM not loaded");
202 "Overworld editor refreshed after Tile16 changes");
203 return absl::OkStatus();
209 [
this](
const std::string& entity_type) {
223 [
this](
const std::string& group_name,
int palette_index) {
225 if (group_name ==
"ow_main" || group_name ==
"ow_animated" ||
226 group_name ==
"ow_aux" || group_name ==
"grass") {
228 "Palette change detected: %s, refreshing current map",
236 LOG_DEBUG(
"OverworldEditor",
"Registered as palette listener (ID: %d)",
241 return absl::OkStatus();
250 return absl::OkStatus();
255 return absl::OkStatus();
291 ImGui::SetNextWindowSize(ImVec2(400, 500), ImGuiCond_FirstUseEver);
305 ImGui::SetNextWindowSize(ImVec2(500, 450), ImGuiCond_FirstUseEver);
306 if (ImGui::Begin(
ICON_MD_LAYERS " Visual Effects Editor###OverlayEditor",
326 if (ImGui::BeginPopupModal(
"Entity Insert Error",
nullptr,
327 ImGuiWindowFlags_AlwaysAutoResize)) {
328 ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f),
333 ImGui::TextDisabled(
"Tip: Delete an existing entity to free up a slot.");
335 if (ImGui::Button(
"OK", ImVec2(120, 0))) {
337 ImGui::CloseCurrentPopup();
344 if (ImGui::BeginPopupModal(
"UpgradeROMVersion",
nullptr,
345 ImGuiWindowFlags_AlwaysAutoResize)) {
349 "This will apply the ZSCustomOverworld ASM patch to your ROM,\n"
350 "enabling advanced features like custom tile graphics, animated GFX,\n"
351 "wide/tall areas, and more.");
355 ImGui::Text(
"Current Version: %s",
356 current_version == 0xFF
358 : absl::StrFormat(
"v%d", current_version).c_str());
360 static int target_version = 3;
361 ImGui::RadioButton(
"v2 (Basic features)", &target_version, 2);
363 ImGui::RadioButton(
"v3 (All features)", &target_version, 3);
367 if (ImGui::Button(
ICON_MD_CHECK " Apply Upgrade", ImVec2(150, 0))) {
373 ImGui::CloseCurrentPopup();
375 LOG_ERROR(
"OverworldEditor",
"Upgrade failed: %s",
376 status.message().data());
381 ImGui::CloseCurrentPopup();
401 return absl::OkStatus();
406 if (ImGui::IsAnyItemActive()) {
416 if (ImGui::IsKeyDown(ImGuiKey_1)) {
418 }
else if (ImGui::IsKeyDown(ImGuiKey_2)) {
435 if (ImGui::IsKeyDown(ImGuiKey_F11)) {
440 if (ImGui::IsKeyDown(ImGuiKey_L) && ImGui::IsKeyDown(ImGuiKey_LeftCtrl)) {
445 if (ImGui::IsKeyDown(ImGuiKey_T) && ImGui::IsKeyDown(ImGuiKey_LeftCtrl)) {
458 if (ImGui::IsKeyDown(ImGuiKey_3)) {
462 }
else if (ImGui::IsKeyDown(ImGuiKey_4)) {
466 }
else if (ImGui::IsKeyDown(ImGuiKey_5)) {
470 }
else if (ImGui::IsKeyDown(ImGuiKey_6)) {
474 }
else if (ImGui::IsKeyDown(ImGuiKey_7)) {
478 }
else if (ImGui::IsKeyDown(ImGuiKey_8)) {
487 bool ctrl_held = ImGui::IsKeyDown(ImGuiKey_LeftCtrl) ||
488 ImGui::IsKeyDown(ImGuiKey_RightCtrl);
494 if (ImGui::IsKeyPressed(ImGuiKey_Z,
false)) {
495 bool shift_held = ImGui::IsKeyDown(ImGuiKey_LeftShift) ||
496 ImGui::IsKeyDown(ImGuiKey_RightShift);
505 if (ImGui::IsKeyPressed(ImGuiKey_Y,
false)) {
532 if (!ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
536 if (!hovered_entity) {
574 if (!hovered_entity || !ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
628 for (
int i = 0; i < 0x40; i++) {
632 if (world_index < 0 || world_index >=
static_cast<int>(
maps_bmp_.size())) {
657 ImDrawList* draw_list = ImGui::GetWindowDrawList();
661 ImVec2 placeholder_pos = ImVec2(canvas_pos.x + scrolling.x + map_x,
662 canvas_pos.y + scrolling.y + map_y);
665 ImVec2 placeholder_size = ImVec2(scaled_size, scaled_size);
668 draw_list->AddRectFilled(
670 ImVec2(placeholder_pos.x + placeholder_size.x,
671 placeholder_pos.y + placeholder_size.y),
672 IM_COL32(32, 32, 32, 128));
675 ImVec2 spinner_pos = ImVec2(placeholder_pos.x + placeholder_size.x / 2,
676 placeholder_pos.y + placeholder_size.y / 2);
678 const float spinner_radius = 8.0f * scale;
679 const float rotation =
static_cast<float>(ImGui::GetTime()) * 3.0f;
680 const float start_angle = rotation;
681 const float end_angle = rotation +
IM_PI * 1.5f;
683 draw_list->PathArcTo(spinner_pos, spinner_radius, start_angle, end_angle,
685 draw_list->PathStroke(IM_COL32(100, 180, 100, 255), 0, 2.5f * scale);
705 ImVec2 mouse_position =
706 ImVec2(scaled_position.x / scale, scaled_position.y / scale);
718 if (current_map_ < 0 || current_map_ >=
static_cast<int>(
maps_bmp_.size())) {
727 "Error: tile16_blockset_ is not properly initialized (active: %s, "
741 int mouse_x =
static_cast<int>(mouse_position.x);
742 int mouse_y =
static_cast<int>(mouse_position.y);
748 auto& selected_world =
753 int index_x = superX * 32 + tile16_x;
754 int index_y = superY * 32 + tile16_y;
757 int old_tile_id = selected_world[index_x][index_y];
769 const ImVec2& click_position,
const std::vector<uint8_t>& tile_data) {
771 if (current_map_ < 0 || current_map_ >=
static_cast<int>(
maps_bmp_.size())) {
773 "ERROR: RenderUpdatedMapBitmap - Invalid current_map_ %d "
774 "(maps_bmp_.size()=%zu)",
786 ImVec2 start_position;
787 start_position.x =
static_cast<float>(tile_index_x *
kTile16Size);
788 start_position.y =
static_cast<float>(tile_index_y *
kTile16Size);
794 if (!current_bitmap.
is_active() || current_bitmap.
size() == 0) {
797 "ERROR: RenderUpdatedMapBitmap - Bitmap %d is not active or has no "
798 "data (active=%s, size=%zu)",
800 current_bitmap.
size());
810 if (pixel_index < 0 ||
811 pixel_index >=
static_cast<int>(current_bitmap.
size())) {
814 "ERROR: RenderUpdatedMapBitmap - pixel_index %d out of bounds "
816 pixel_index, current_bitmap.
size());
822 if (tile_data_index < 0 ||
823 tile_data_index >=
static_cast<int>(tile_data.size())) {
826 "ERROR: RenderUpdatedMapBitmap - tile_data_index %d out of bounds "
827 "(tile_data size=%zu)",
828 tile_data_index, tile_data.size());
832 current_bitmap.
WriteToPixel(pixel_index, tile_data[tile_data_index]);
844 LOG_DEBUG(
"OverworldEditor",
"CheckForOverworldEdits: Frame %d",
845 ImGui::GetFrameCount());
859 if (ImGui::IsMouseClicked(ImGuiMouseButton_Left) ||
860 ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
862 "CheckForOverworldEdits: About to apply rectangle selection");
864 auto& selected_world =
880 std::swap(start_x, end_x);
882 std::swap(start_y, end_y);
884 constexpr int local_map_size = 512;
886 constexpr int tiles_per_local_map = local_map_size /
kTile16Size;
889 "CheckForOverworldEdits: About to fill rectangle with "
890 "current_tile16_=%d",
898 for (
int y = start_y;
901 for (
int x = start_x;
905 int local_map_x = x / local_map_size;
906 int local_map_y = y / local_map_size;
913 int index_x = local_map_x * tiles_per_local_map + tile16_x;
914 int index_y = local_map_y * tiles_per_local_map + tile16_y;
920 int rect_width = ((end_x - start_x) /
kTile16Size) + 1;
921 int rect_height = ((end_y - start_y) /
kTile16Size) + 1;
926 int start_local_map_x = start_x / local_map_size;
927 int start_local_map_y = start_y / local_map_size;
928 int end_local_map_x = end_x / local_map_size;
929 int end_local_map_y = end_y / local_map_size;
931 bool in_same_local_map = (start_local_map_x == end_local_map_x) &&
932 (start_local_map_y == end_local_map_y);
934 if (in_same_local_map && index_x >= 0 &&
935 (index_x + rect_width - 1) < 0x200 && index_y >= 0 &&
936 (index_y + rect_height - 1) < 0x200) {
938 int old_tile_id = selected_world[index_x][index_y];
939 if (old_tile_id != tile16_id) {
944 selected_world[index_x][index_y] = tile16_id;
948 ImVec2 tile_position(x, y);
950 if (!tile_data.empty()) {
954 "CheckForOverworldEdits: Updated bitmap at position (%d,%d) "
959 "ERROR: Failed to get tile data for tile16_id=%d",
976 "CheckForOverworldEdits: Rectangle selection applied and cleared");
1019 return absl::FailedPreconditionError(
"Clipboard unavailable");
1024 std::vector<int> ids;
1029 static_cast<int>(std::floor(std::min(start.x, end.x) / 16.0f));
1031 static_cast<int>(std::floor(std::max(start.x, end.x) / 16.0f));
1033 static_cast<int>(std::floor(std::min(start.y, end.y) / 16.0f));
1035 static_cast<int>(std::floor(std::max(start.y, end.y) / 16.0f));
1036 const int width = end_x - start_x + 1;
1037 const int height = end_y - start_y + 1;
1038 ids.reserve(width * height);
1041 for (
int y = start_y; y <= end_y; ++y) {
1042 for (
int x = start_x; x <= end_x; ++x) {
1051 return absl::OkStatus();
1059 return absl::OkStatus();
1061 return absl::FailedPreconditionError(
"Nothing selected to copy");
1066 return absl::FailedPreconditionError(
"Clipboard unavailable");
1069 return absl::FailedPreconditionError(
"Clipboard empty");
1073 return absl::FailedPreconditionError(
"No paste target");
1082 const ImVec2 anchor =
1083 ImVec2(scaled_anchor.x / scale, scaled_anchor.y / scale);
1086 const int tile16_x =
1088 const int tile16_y =
1091 auto& selected_world =
1098 const int tiles_per_local_map = 512 /
kTile16Size;
1105 if (width * height !=
static_cast<int>(ids.size())) {
1106 return absl::InternalError(
"Clipboard dimensions mismatch");
1109 for (
int dy = 0; dy < height; ++dy) {
1110 for (
int dx = 0; dx < width; ++dx) {
1111 const int id = ids[dy * width + dx];
1112 const int gx = tile16_x + dx;
1113 const int gy = tile16_y + dy;
1115 const int global_x = superX * 32 + gx;
1116 const int global_y = superY * 32 + gy;
1117 if (global_x < 0 || global_x >= 256 || global_y < 0 || global_y >= 256)
1119 selected_world[global_x][global_y] = id;
1124 return absl::OkStatus();
1135 const int large_map_size = 1024;
1143 if (map_x < 0 || map_x >= 8 || map_y < 0 || map_y >= 8) {
1144 return absl::OkStatus();
1147 const bool allow_special_tail =
1151 return absl::OkStatus();
1155 int hovered_map = map_x + map_y * 8;
1157 hovered_map += 0x40;
1159 hovered_map += 0x80;
1169 bool should_build =
false;
1181 ImGui::IsMouseClicked(ImGuiMouseButton_Left) ||
1182 ImGui::IsMouseClicked(ImGuiMouseButton_Right);
1203 bool use_v3_area_sizes =
1207 if (use_v3_area_sizes) {
1210 const int highlight_parent =
1218 parent_map_x = highlight_parent % 8;
1219 parent_map_y = highlight_parent / 8;
1222 parent_map_x = (highlight_parent - 0x40) % 8;
1223 parent_map_y = (highlight_parent - 0x40) / 8;
1226 parent_map_x = (highlight_parent - 0x80) % 8;
1227 parent_map_y = (highlight_parent - 0x80) / 8;
1231 switch (area_size) {
1232 case AreaSizeEnum::LargeArea:
1236 large_map_size, large_map_size);
1238 case AreaSizeEnum::WideArea:
1244 case AreaSizeEnum::TallArea:
1250 case AreaSizeEnum::SmallArea:
1262 const int highlight_parent =
1274 parent_map_x = highlight_parent % 8;
1275 parent_map_y = highlight_parent / 8;
1278 parent_map_x = (highlight_parent - 0x40) % 8;
1279 parent_map_y = (highlight_parent - 0x40) / 8;
1282 parent_map_x = (highlight_parent - 0x80) % 8;
1283 parent_map_y = (highlight_parent - 0x80) / 8;
1288 large_map_size, large_map_size);
1295 current_map_x = current_highlighted_map % 8;
1296 current_map_y = current_highlighted_map / 8;
1299 current_map_x = (current_highlighted_map - 0x40) % 8;
1300 current_map_y = (current_highlighted_map - 0x40) / 8;
1305 current_map_x = (current_highlighted_map - 0x80) % 8;
1306 current_map_y = (current_highlighted_map - 0x80) / 8;
1333 if (ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
1338 if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Right)) {
1342 return absl::OkStatus();
1352 constexpr float kWorldSize = 512.0f * 8.0f;
1353 return ImVec2(kWorldSize * scale, kWorldSize * scale);
1358 ImVec2 visible_size) {
1360 float max_scroll_x = std::max(0.0f, content_size.x - visible_size.x);
1361 float max_scroll_y = std::max(0.0f, content_size.y - visible_size.y);
1365 float clamped_x = std::clamp(scroll.x, -max_scroll_x, 0.0f);
1366 float clamped_y = std::clamp(scroll.y, -max_scroll_y, 0.0f);
1368 return ImVec2(clamped_x, clamped_y);
1398 bool show_context_menu =
1464 if (canvas_rt.hovered) {
1474 ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
1485 ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
1486 ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
1487 ImVec2 mouse_delta = ImGui::GetIO().MouseDelta;
1488 float scale = canvas_rt.scale;
1497 ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
1499 float end_scale = canvas_rt.scale;
1523 ImGui::BeginGroup();
1537 "OwBlocksetSelector", selector_config);
1547 if (result.selection_changed) {
1552 util::logf(
"Failed to set tile16: %s", status.message().data());
1560 if (result.tile_double_clicked) {
1568 return absl::OkStatus();
1587 int offset = 0x40 * (key + 1);
1588 int top_left_y = canvas_rt.canvas_p0.y + 2;
1590 top_left_y = canvas_rt.canvas_p0.y + 0x40 * key;
1592 auto texture = value.texture();
1593 canvas_rt.draw_list->AddImage(
1594 (ImTextureID)(intptr_t)texture,
1595 ImVec2(canvas_rt.canvas_p0.x + 2, top_left_y),
1596 ImVec2(canvas_rt.canvas_p0.x + 0x100,
1597 canvas_rt.canvas_p0.y + offset));
1623 auto bmp = std::make_unique<gfx::Bitmap>();
1644 ImGui::BeginGroup();
1660 return absl::OkStatus();
1669 return absl::OkStatus();
1675 ImGui::TextWrapped(
"ZSCustomOverworld v3 settings panel");
1684 "This panel will contain ZSCustomOverworld configuration options "
1685 "such as custom map sizes, extended tile sets, and other v3 features.");
1697 static bool show_custom_bg_color_editor =
false;
1698 static bool show_overlay_editor =
false;
1699 static int game_state = 0;
1703 show_custom_bg_color_editor, show_overlay_editor);
1707 if (show_custom_bg_color_editor) {
1708 ImGui::OpenPopup(
"CustomBGColorEditor");
1709 show_custom_bg_color_editor =
false;
1711 if (show_overlay_editor) {
1712 ImGui::OpenPopup(
"OverlayEditor");
1713 show_overlay_editor =
false;
1716 if (ImGui::BeginPopup(
"CustomBGColorEditor")) {
1724 if (ImGui::BeginPopup(
"OverlayEditor")) {
1727 show_overlay_editor);
1753 return absl::OkStatus();
1773 auto now = std::chrono::steady_clock::now();
1792 .tile_changes = {{{x, y}, old_tile_id}},
1821 for (
const auto& [coords, tile_id] : point.
tile_changes) {
1822 auto [x, y] = coords;
1823 world_tiles[x][y] = tile_id;
1835 return absl::FailedPreconditionError(
"Nothing to undo");
1843 .world = point.
world,
1845 .timestamp = std::chrono::steady_clock::now()};
1850 for (
const auto& [coords, old_tile_id] : point.
tile_changes) {
1851 auto [x, y] = coords;
1852 int current_tile_id = world_tiles[x][y];
1855 redo_point.tile_changes.emplace_back(coords, current_tile_id);
1858 world_tiles[x][y] = old_tile_id;
1866 return absl::OkStatus();
1871 return absl::FailedPreconditionError(
"Nothing to redo");
1879 .world = point.
world,
1881 .timestamp = std::chrono::steady_clock::now()};
1886 for (
const auto& [coords, tile_id] : point.
tile_changes) {
1887 auto [x, y] = coords;
1888 int current_tile_id = world_tiles[x][y];
1891 undo_point.tile_changes.emplace_back(coords, current_tile_id);
1894 world_tiles[x][y] = tile_id;
1902 return absl::OkStatus();
1910 LOG_DEBUG(
"OverworldEditor",
"Loading overworld.");
1926 LOG_DEBUG(
"OverworldEditor",
"Loading overworld graphics (optimized).");
1940 "Loading overworld tileset (deferred textures).");
1953 LOG_DEBUG(
"OverworldEditor",
"Loading overworld tile16 graphics.");
1972#ifdef __EMSCRIPTEN__
1973 constexpr int kEssentialMapsPerWorld = 4;
1975 constexpr int kEssentialMapsPerWorld =
1978 constexpr int kLightWorldEssential = kEssentialMapsPerWorld;
1979 constexpr int kDarkWorldEssential =
1981 constexpr int kSpecialWorldEssential =
1986 "Creating bitmaps for essential maps only (first %d maps per world)",
1987 kEssentialMapsPerWorld);
1989 std::vector<gfx::Bitmap*> maps_to_texture;
1990 maps_to_texture.reserve(kEssentialMapsPerWorld *
1996 bool is_essential =
false;
1999 if (i < kLightWorldEssential) {
2000 is_essential =
true;
2002 is_essential =
true;
2004 i < kSpecialWorldEssential) {
2005 is_essential =
true;
2016 maps_to_texture.push_back(&
maps_bmp_[i]);
2017 }
catch (
const std::bad_alloc& e) {
2018 std::cout <<
"Error allocating map " << i <<
": " << e.what()
2029 const int initial_texture_count =
2030 std::min(4,
static_cast<int>(maps_to_texture.size()));
2033 for (
int i = 0; i < initial_texture_count; ++i) {
2043 for (
size_t i = initial_texture_count; i < maps_to_texture.size(); ++i) {
2047 if (&
maps_bmp_[j] == maps_to_texture[i]) {
2054 if (map_index >= 0) {
2055 int map_world = map_index / 0x40;
2074 return absl::OkStatus();
2079 const int depth = 0x10;
2080 for (
int i = 0; i < 3; i++)
2082 int width = sprite.width();
2083 int height = sprite.height();
2084 if (width == 0 || height == 0) {
2091 *sprite.preview_graphics());
2097 return absl::OkStatus();
2107 int refresh_count = 0;
2108 const int max_refreshes_per_frame = 2;
2118 if (is_current_map || is_current_world) {
2134 LOG_ERROR(
"OverworldEditor",
"Failed to build map %d: %s", map_index,
2142 if (!bitmap.is_active()) {
2148 bitmap.SetPalette(palette);
2149 }
catch (
const std::bad_alloc& e) {
2150 LOG_ERROR(
"OverworldEditor",
"Error allocating bitmap for map %d: %s",
2151 map_index, e.what());
2156 if (!bitmap.texture() && bitmap.is_active()) {
2164#ifdef __EMSCRIPTEN__
2177 int world_offset = (center_map / 64) * 64;
2178 int local_index = center_map % 64;
2179 int map_x = local_index % 8;
2180 int map_y = local_index / 8;
2184 static const int dx[] = {-1, 1, 0, 0};
2185 static const int dy[] = {0, 0, -1, 1};
2187 for (
int i = 0; i < 4; ++i) {
2188 int nx = map_x + dx[i];
2189 int ny = map_y + dy[i];
2192 if (nx >= 0 && nx < 8 && ny >= 0 && ny < max_rows) {
2193 int neighbor_index = world_offset + ny * 8 + nx;
2204#ifdef __EMSCRIPTEN__
2222 LOG_DEBUG(
"OverworldEditor",
"Background preload of map %d failed: %s",
2223 map_to_preload, status.message().data());
2238 maps_bmp_[map_index].set_modified(
true);
2264 if (!is_current_map && !is_current_world) {
2267 maps_bmp_[map_index].set_modified(
true);
2282 bool needs_graphics_rebuild =
maps_bmp_[map_index].modified();
2283 bool needs_palette_rebuild =
false;
2285 if (needs_graphics_rebuild) {
2287 map->LoadAreaGraphics();
2290 auto status = map->BuildTileset();
2292 LOG_ERROR(
"OverworldEditor",
"Failed to build tileset for map %d: %s",
2293 map_index, status.message().data());
2302 "Failed to build tiles16 graphics for map %d: %s", map_index,
2303 status.message().data());
2310 LOG_ERROR(
"OverworldEditor",
"Failed to build bitmap for map %d: %s",
2311 map_index, status.message().data());
2316 maps_bmp_[map_index].set_data(map->bitmap_data());
2317 maps_bmp_[map_index].set_modified(
false);
2320 if (!
maps_bmp_[map_index].ValidateDataSurfaceSync()) {
2322 "Warning: Surface synchronization issue detected for map %d",
2339 bool use_v3_area_sizes =
2342 if (use_v3_area_sizes) {
2347 if (map->is_large_map()) {
2372 if (area_size == AreaSizeEnum::SmallArea) {
2377 int parent_id = map->
parent();
2384 "RefreshMultiAreaMapsSafely: Could not get parent map %d for map %d",
2385 parent_id, map_index);
2390 "RefreshMultiAreaMapsSafely: Processing %s area from parent %d "
2392 (area_size == AreaSizeEnum::LargeArea) ?
"large"
2393 : (area_size == AreaSizeEnum::WideArea) ?
"wide"
2395 parent_id, map_index);
2399 std::vector<int> sibling_maps;
2401 switch (area_size) {
2402 case AreaSizeEnum::LargeArea:
2404 sibling_maps = {parent_id, parent_id + 1, parent_id + 8, parent_id + 9};
2407 case AreaSizeEnum::WideArea:
2409 sibling_maps = {parent_id, parent_id + 1};
2412 case AreaSizeEnum::TallArea:
2414 sibling_maps = {parent_id, parent_id + 8};
2419 "RefreshMultiAreaMapsSafely: Unknown area size %d for map %d",
2420 static_cast<int>(area_size), map_index);
2427 for (
int sibling : sibling_maps) {
2429 if (sibling == map_index) {
2445 if (is_current_map || is_current_world) {
2447 "RefreshMultiAreaMapsSafely: Refreshing sibling map %d",
2455 sibling_map->LoadAreaGraphics();
2457 auto status = sibling_map->BuildTileset();
2460 "Failed to build tileset for sibling %d: %s", sibling,
2461 status.message().data());
2469 "Failed to build tiles16 for sibling %d: %s", sibling,
2470 status.message().data());
2474 status = sibling_map->LoadPalette();
2477 "Failed to load palette for sibling %d: %s", sibling,
2478 status.message().data());
2485 "Failed to build bitmap for sibling %d: %s", sibling,
2486 status.message().data());
2491 maps_bmp_[sibling].set_data(sibling_map->bitmap_data());
2495 maps_bmp_[sibling].SetPalette(sibling_map->current_palette());
2528 bool use_v3_area_sizes =
2531 if (use_v3_area_sizes) {
2536 if (area_size != AreaSizeEnum::SmallArea) {
2538 std::vector<int> sibling_maps;
2541 switch (area_size) {
2542 case AreaSizeEnum::LargeArea:
2544 sibling_maps = {parent_id, parent_id + 1, parent_id + 8,
2547 case AreaSizeEnum::WideArea:
2549 sibling_maps = {parent_id, parent_id + 1};
2551 case AreaSizeEnum::TallArea:
2553 sibling_maps = {parent_id, parent_id + 8};
2560 for (
int sibling_index : sibling_maps) {
2566 maps_bmp_[sibling_index].SetPalette(sibling_map->current_palette());
2576 for (
int i = 1; i < 4; i++) {
2586 if (
maps_bmp_[sibling_index].is_active() &&
2588 maps_bmp_[sibling_index].SetPalette(sibling_map->current_palette());
2600 return absl::OkStatus();
2605 if (map_index >= 0 && map_index <
static_cast<int>(
maps_bmp_.size())) {
2606 maps_bmp_[map_index].set_modified(
true);
2616 "ForceRefreshGraphics: Map %d marked for refresh", map_index);
2621 bool include_self) {
2622 if (map_index < 0 || map_index >=
static_cast<int>(
maps_bmp_.size())) {
2631 int parent_id = map->parent();
2632 std::vector<int> siblings;
2634 switch (map->area_size()) {
2636 siblings = {parent_id, parent_id + 1, parent_id + 8, parent_id + 9};
2639 siblings = {parent_id, parent_id + 1};
2642 siblings = {parent_id, parent_id + 8};
2648 for (
int sibling : siblings) {
2649 if (sibling >= 0 && sibling < 0xA0) {
2651 if (sibling == map_index && !include_self) {
2667 "RefreshSiblingMapGraphics: Refreshed sibling map %d", sibling);
2677 bool use_v3_area_sizes =
2680 if (use_v3_area_sizes) {
2683 auto area_size = current_ow_map.area_size();
2685 if (area_size != AreaSizeEnum::SmallArea) {
2687 std::vector<int> sibling_maps;
2688 int parent_id = current_ow_map.parent();
2690 switch (area_size) {
2691 case AreaSizeEnum::LargeArea:
2693 sibling_maps = {parent_id + 1, parent_id + 8, parent_id + 9};
2695 case AreaSizeEnum::WideArea:
2697 sibling_maps = {parent_id + 1};
2699 case AreaSizeEnum::TallArea:
2701 sibling_maps = {parent_id + 8};
2708 for (
int sibling_index : sibling_maps) {
2713 map.set_area_graphics(current_ow_map.area_graphics());
2714 map.set_area_palette(current_ow_map.area_palette());
2719 map.set_message_id(current_ow_map.message_id());
2722 map.LoadAreaGraphics();
2727 if (current_ow_map.is_large_map()) {
2729 for (
int i = 1; i < 4; i++) {
2730 int sibling_index = current_ow_map.parent() + i;
2735 map.set_area_graphics(current_ow_map.area_graphics());
2736 map.set_area_palette(current_ow_map.area_palette());
2741 map.set_message_id(current_ow_map.message_id());
2744 map.LoadAreaGraphics();
2751 LOG_DEBUG(
"OverworldEditor",
"RefreshTile16Blockset called");
2754 return absl::OkStatus();
2784 return absl::OkStatus();
2806 constexpr int kTilesPerRow = 8;
2807 constexpr int kTileSize = 16;
2811 bool atlas_modified =
false;
2824 if (!pending_bmp || !pending_bmp->
is_active() ||
2825 pending_bmp->
vector().empty()) {
2830 int tile_x = (tile_id % kTilesPerRow) * kTileSize;
2831 int tile_y = (tile_id / kTilesPerRow) * kTileSize;
2834 if (tile_x + kTileSize > atlas_width || tile_y + kTileSize > atlas_height) {
2840 const auto& pending_data = pending_bmp->
vector();
2842 for (
int y = 0; y < kTileSize && y < pending_bmp->
height(); ++y) {
2843 for (
int x = 0; x < kTileSize && x < pending_bmp->
width(); ++x) {
2844 int atlas_idx = (tile_y + y) * atlas_width + (tile_x + x);
2845 int pending_idx = y * pending_bmp->
width() + x;
2847 if (atlas_idx >= 0 && atlas_idx <
static_cast<int>(atlas_data.size()) &&
2849 pending_idx <
static_cast<int>(pending_data.size())) {
2850 atlas_data[atlas_idx] = pending_data[pending_idx];
2851 atlas_modified =
true;
2867 if (ImGui::IsMouseClicked(ImGuiMouseButton_Middle) &&
2868 ImGui::IsItemHovered()) {
2876 int hovered_map = map_x + map_y * 8;
2878 hovered_map += 0x40;
2880 hovered_map += 0x80;
2884 if (hovered_map >= 0 && hovered_map < 0xA0) {
2897 if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) &&
2898 ImGui::IsItemHovered()) {
2928 static bool init_properties =
false;
2930 if (!init_properties) {
2931 for (
int i = 0; i < 0x40; i++) {
2932 std::string area_graphics_str = absl::StrFormat(
2935 ->push_back(area_graphics_str);
2937 area_graphics_str = absl::StrFormat(
2940 ->push_back(area_graphics_str);
2942 std::string area_palette_str =
2945 ->push_back(area_palette_str);
2947 area_palette_str = absl::StrFormat(
2950 ->push_back(area_palette_str);
2951 std::string sprite_gfx_str = absl::StrFormat(
2954 ->push_back(sprite_gfx_str);
2956 sprite_gfx_str = absl::StrFormat(
2959 ->push_back(sprite_gfx_str);
2961 sprite_gfx_str = absl::StrFormat(
2964 ->push_back(sprite_gfx_str);
2966 sprite_gfx_str = absl::StrFormat(
2969 ->push_back(sprite_gfx_str);
2971 std::string sprite_palette_str = absl::StrFormat(
2974 ->push_back(sprite_palette_str);
2976 sprite_palette_str = absl::StrFormat(
2979 ->push_back(sprite_palette_str);
2981 sprite_palette_str = absl::StrFormat(
2984 ->push_back(sprite_palette_str);
2986 sprite_palette_str = absl::StrFormat(
2989 ->push_back(sprite_palette_str);
2991 init_properties =
true;
2994 ImGui::Text(
"Area Gfx LW/DW");
3002 ImGui::Text(
"Sprite Gfx LW/DW");
3016 ImGui::Text(
"Area Pal LW/DW");
3023 static bool show_gfx_group =
false;
3024 ImGui::Checkbox(
"Show Gfx Group Editor", &show_gfx_group);
3025 if (show_gfx_group) {
3043 return absl::OkStatus();
3051 if (target_version < 2 || target_version > 3) {
3052 return absl::InvalidArgumentError(absl::StrFormat(
3053 "Invalid target version: %d. Must be 2 or 3.", target_version));
3058 if (current_version != 0xFF && current_version >= target_version) {
3059 return absl::AlreadyExistsError(absl::StrFormat(
3060 "ROM is already version %d or higher", current_version));
3063 LOG_DEBUG(
"OverworldEditor",
"Applying ZSCustomOverworld ASM v%d to ROM...",
3067 auto asar_wrapper = std::make_unique<core::AsarWrapper>();
3071 std::vector<uint8_t> original_rom_data =
rom_->
vector();
3072 std::vector<uint8_t> working_rom_data = original_rom_data;
3077 std::string asm_file_name =
3078 (target_version == 3) ?
"asm/yaze.asm"
3079 :
"asm/ZSCustomOverworld.asm";
3085 LOG_DEBUG(
"OverworldEditor",
"Using ASM file: %s", asm_file_path.c_str());
3088 if (!std::filesystem::exists(asm_file_path)) {
3089 return absl::NotFoundError(
3090 absl::StrFormat(
"ASM file not found at: %s\n\n"
3091 "Expected location: assets/%s\n"
3092 "Make sure the assets directory is accessible.",
3093 asm_file_path, asm_file_name));
3098 asar_wrapper->ApplyPatch(asm_file_path, working_rom_data);
3099 if (!patch_result.ok()) {
3100 return absl::InternalError(absl::StrFormat(
3101 "Failed to apply ASM patch: %s", patch_result.status().message()));
3104 const auto& result = patch_result.value();
3105 if (!result.success) {
3106 std::string error_details =
"ASM patch failed with errors:\n";
3107 for (
const auto& error : result.errors) {
3108 error_details +=
" - " + error +
"\n";
3110 if (!result.warnings.empty()) {
3111 error_details +=
"Warnings:\n";
3112 for (
const auto& warning : result.warnings) {
3113 error_details +=
" - " + warning +
"\n";
3116 return absl::InternalError(error_details);
3127 "ASM patch applied successfully. Found %zu symbols:",
3128 result.symbols.size());
3129 for (
const auto& symbol : result.symbols) {
3130 LOG_DEBUG(
"OverworldEditor",
" %s @ $%06X", symbol.name.c_str(),
3138 "ZSCustomOverworld v%d successfully applied to ROM",
3140 return absl::OkStatus();
3142 }
catch (
const std::exception& e) {
3145 if (!restore_result.ok()) {
3146 LOG_ERROR(
"OverworldEditor",
"Failed to restore ROM data: %s",
3147 restore_result.message().data());
3149 return absl::InternalError(
3150 absl::StrFormat(
"Exception during ASM application: %s", e.what()));
3157 static_cast<uint8_t
>(target_version);
3160 if (target_version >= 2) {
3166 "Enabled v2+ features: Custom BG colors, Main palettes");
3169 if (target_version >= 3) {
3178 "Enabled v3+ features: Subscreen overlays, Animated GFX, Tile GFX "
3182 for (
int i = 0; i < 0xA0; i++) {
3188 const std::vector<int> large_areas = {
3189 0x00, 0x02, 0x05, 0x07, 0x0A, 0x0B, 0x0F, 0x10, 0x11, 0x12,
3190 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1D,
3191 0x1E, 0x25, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2E, 0x2F, 0x30,
3192 0x32, 0x33, 0x34, 0x35, 0x37, 0x3A, 0x3B, 0x3C, 0x3F};
3194 for (
int area_id : large_areas) {
3195 if (area_id < 0xA0) {
3201 LOG_DEBUG(
"OverworldEditor",
"Initialized area size data for %zu areas",
3202 large_areas.size());
3205 LOG_DEBUG(
"OverworldEditor",
"ROM version markers updated to v%d",
3207 return absl::OkStatus();
void set_dirty(bool dirty)
const auto & vector() const
absl::Status LoadFromData(const std::vector< uint8_t > &data, const LoadOptions &options=LoadOptions::Defaults())
EditorDependencies dependencies_
std::unique_ptr< UsageStatisticsCard > usage_stats_card_
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_
void HandleEntityInteraction()
Handle entity interaction in MOUSE mode Includes: right-click context menus, double-click navigation,...
static constexpr float kHoverBuildDelay
zelda3::OverworldEntranceTileTypes entrance_tiletypes_
zelda3::OverworldEntrance current_entrance_
void HandleTile16Edit()
Handle tile16 editing from context menu (MOUSE mode) Gets the tile16 under the cursor and opens the T...
absl::Status ApplyZSCustomOverworldASM(int target_version)
Apply ZSCustomOverworld ASM patch to upgrade ROM version.
zelda3::Sprite current_sprite_
std::optional< OverworldUndoPoint > current_paint_operation_
std::string entity_insert_error_message_
absl::Status CheckForCurrentMap()
Check for map changes and refresh if needed.
void DrawOverworldEdits()
void CreateUndoPoint(int map_id, int world, int x, int y, int old_tile_id)
absl::Status LoadScratchToSelection()
void ForceRefreshGraphics(int map_index)
std::vector< int > selected_tile16_ids_
gfx::Bitmap current_gfx_bmp_
gfx::Tilemap tile16_blockset_
void ProcessPendingEntityInsertion()
Process any pending entity insertion request Called from Update() - needed because ImGui::OpenPopup()...
std::vector< OverworldUndoPoint > undo_stack_
void CycleTileSelection(int delta)
Tile16Editor tile16_editor_
gui::Canvas ow_map_canvas_
ScratchSpace scratch_space_
absl::Status Undo() override
void HandleEntityInsertion(const std::string &entity_type)
Handle entity insertion from context menu.
zelda3::GameEntity * dragged_entity_
std::array< gfx::Bitmap, zelda3::kNumOverworldMaps > maps_bmp_
bool show_map_properties_panel_
void RefreshOverworldMap()
void CheckForOverworldEdits()
Check for tile edits - handles painting and selection.
absl::Status UpdateROMVersionMarkers(int target_version)
Update ROM version markers and feature flags after ASM patching.
void ProcessPreloadQueue()
Process one map from the preload queue (called each frame)
zelda3::OverworldExit current_exit_
void UpdateBlocksetWithPendingTileChanges()
void RefreshMapProperties()
void RefreshSiblingMapGraphics(int map_index, bool include_self=false)
std::vector< OverworldUndoPoint > redo_stack_
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_
std::unique_ptr< OverworldSidebar > sidebar_
absl::Status Redo() override
static constexpr float kPreloadStartDelay
void SetupCanvasAutomation()
std::chrono::steady_clock::time_point last_paint_time_
void InvalidateGraphicsCache(int map_id=-1)
Invalidate cached graphics for a specific map or all maps.
void HandleOverworldPan()
void HandleEntityDoubleClick(zelda3::GameEntity *hovered_entity)
Handle double-click actions on entities (e.g., jump to room)
bool dragged_entity_free_movement_
void DrawOverworldProperties()
void HandleEntityContextMenus(zelda3::GameEntity *hovered_entity)
Handle right-click context menus for entities.
absl::Status SaveCurrentSelectionToScratch()
absl::Status RefreshMapPalette()
void RefreshMultiAreaMapsSafely(int map_index, zelda3::OverworldMap *map)
Safely refresh multi-area maps without recursion.
void DrawOverworldCanvas()
Draw the main overworld canvas.
gui::Canvas blockset_canvas_
std::vector< int > preload_queue_
absl::Status RefreshTile16Blockset()
zelda3::Overworld & overworld()
Access the underlying Overworld data.
void UpdateBlocksetSelectorState()
void CheckForSelectRectangle()
Draw and create the tile16 IDs that are currently selected.
void RefreshChildMap(int map_index)
void FinalizePaintOperation()
std::unique_ptr< OverworldEntityRenderer > entity_renderer_
auto & GetWorldTiles(int world)
absl::Status Load() override
absl::Status UpdateGfxGroupEditor()
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_
std::unique_ptr< OverworldToolbar > toolbar_
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_
void DrawEntityEditorPopups()
Draw entity editor popups and update entity data.
absl::Status Paste() override
void HandleEntityEditingShortcuts()
void ScrollBlocksetCanvasToCurrentTile()
Scroll the blockset canvas to show the current selected tile16.
static constexpr auto kPaintBatchTimeout
static constexpr size_t kMaxUndoHistory
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
std::unique_ptr< DebugWindowCard > debug_window_card_
void ApplyUndoPoint(const OverworldUndoPoint &point)
void HandleOverworldZoom()
zelda3::GameEntity * current_entity_
void HandleUndoRedoShortcuts()
void HandleKeyboardShortcuts()
Handle keyboard shortcuts for the Overworld Editor Shortcuts: 1-2 (modes), 3-8 (entities),...
gui::Canvas properties_canvas_
gfx::SnesPalette palette_
gfx::Bitmap tile16_blockset_bmp_
void QueueAdjacentMapsForPreload(int center_map)
Queue adjacent maps for background pre-loading.
absl::Status DrawTile16Selector()
bool TogglePanel(size_t session_id, const std::string &base_card_id)
bool ShowPanel(size_t session_id, const std::string &base_card_id)
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)
const gfx::Bitmap * GetPendingTileBitmap(int tile_id) const
Get preview bitmap for a pending tile (nullptr if not modified)
bool has_pending_changes() const
Check if any tiles have uncommitted changes.
bool is_tile_modified(int tile_id) const
Check if a specific tile has pending changes.
void set_palette(const gfx::SnesPalette &palette)
void set_on_changes_committed(std::function< absl::Status()> callback)
void QueueTextureCommand(TextureCommandType type, Bitmap *bitmap)
int RegisterPaletteListener(PaletteChangeCallback callback)
Register a callback for palette change notifications.
void ProcessTextureQueue(IRenderer *renderer)
std::array< gfx::Bitmap, 223 > & gfx_sheets()
Get reference to all graphics sheets.
void UnregisterPaletteListener(int listener_id)
Unregister a palette change listener.
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.
void Create(int width, int height, int depth, std::span< uint8_t > data)
Create a bitmap with the given dimensions and data.
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 using SNES palette format.
std::vector< uint8_t > & mutable_data()
SDL_Surface * surface() const
RAII timer for automatic timing management.
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.
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)
void ClearContextMenuItems()
auto mutable_labels(int i)
void set_selected_tile_pos(ImVec2 pos)
void DrawSelectRect(int current_map, int tile_size=0x10, float scale=1.0f)
void DrawOutline(int x, int y, int w, int h)
const ImVector< ImVec2 > & points() const
auto selected_points() const
Base class for all overworld and dungeon entities.
virtual void UpdateMapProperties(uint16_t map_id, const void *context=nullptr)=0
Update entity properties based on map position.
enum yaze::zelda3::GameEntity::EntityType entity_type_
Represents an overworld exit that transitions from dungeon to overworld.
Represents a single Overworld map screen.
static OverworldVersion GetVersion(const Rom &rom)
Detect ROM version from ASM marker byte.
static bool SupportsAreaEnum(OverworldVersion version)
Check if ROM supports area enum system (v3+ only)
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)
Load all overworld data from ROM.
absl::Status SaveMapProperties()
Save per-area graphics, palettes, and messages.
void ClearGraphicsConfigCache()
Clear entire graphics config cache Call when palette or graphics settings change globally.
absl::Status SaveMap32Tiles()
Save tile32 definitions to ROM.
absl::Status SaveMap16Tiles()
Save tile16 definitions to ROM.
std::vector< gfx::Tile16 > tiles16() const
void InvalidateSiblingMapCaches(int map_index)
Invalidate cached tilesets for a map and all its siblings.
auto current_graphics() const
absl::Status CreateTile32Tilemap()
Build tile32 tilemap from current tile16 data.
auto overworld_map(int i) const
void set_current_map(int i)
auto mutable_overworld_map(int i)
absl::Status SaveEntrances()
Save entrance warp points to ROM.
absl::Status SaveExits()
Save exit return points to ROM.
absl::Status EnsureMapBuilt(int map_index)
Build a map on-demand if it hasn't been built yet.
absl::Status SaveItems()
Save hidden overworld items to ROM.
uint16_t GetTile(int x, int y) const
absl::Status SaveOverworldMaps()
Save compressed map tile data to ROM.
auto mutable_all_tiles_types()
auto mutable_sprites(int state)
auto current_map_bitmap_data() const
OverworldBlockset & GetMapTiles(int world_type)
absl::Status SaveMusic()
Save per-area music IDs.
A class for managing sprites in the overworld and underworld.
#define ICON_MD_FORMAT_COLOR_FILL
#define LOG_DEBUG(category, format,...)
#define LOG_ERROR(category, format,...)
#define LOG_WARN(category, format,...)
#define PRINT_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.
constexpr ImVec2 kOverworldCanvasSize(kOverworldMapSize *8, kOverworldMapSize *8)
constexpr unsigned int kOverworldMapSize
bool DrawSpriteEditorPopup(zelda3::Sprite &sprite)
constexpr ImVec2 kCurrentGfxCanvasSize(0x100+1, 0x10 *0x40+1)
bool DrawOverworldEntrancePopup(zelda3::OverworldEntrance &entrance)
bool DrawItemEditorPopup(zelda3::OverworldItem &item)
constexpr int kTile16Size
void MoveEntityOnGrid(zelda3::GameEntity *entity, ImVec2 canvas_p0, ImVec2 scrolling, bool free_movement, float scale)
Move entity to grid-aligned position based on mouse.
constexpr ImVec2 kGraphicsBinCanvasSize(0x100+1, kNumSheetsToLoad *0x40+1)
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)
constexpr const char * kOverworld
void EndCanvas(Canvas &canvas)
void BeginChildBothScrollbars(int id)
void BeginCanvas(Canvas &canvas, ImVec2 child_size)
void CenterText(const char *text)
void EndWindowWithDisplaySettings()
std::string MakePopupId(size_t session_id, const std::string &editor_name, const std::string &popup_name)
Generate session-aware popup IDs to prevent conflicts in multi-editor layouts.
void BeginWindowWithDisplaySettings(const char *id, bool *active, const ImVec2 &size, ImGuiWindowFlags flags)
void BeginChildWithScrollbar(const char *str_id)
std::string GetResourcePath(const std::string &resource_path)
void logf(const absl::FormatSpec< Args... > &format, Args &&... args)
constexpr int OverworldCustomTileGFXGroupEnabled
constexpr int OverworldCustomAreaSpecificBGEnabled
constexpr int kOverworldScreenSize
constexpr int kNumTile16Individual
constexpr int kSpecialWorldMapIdStart
constexpr int OverworldCustomAnimatedGFXEnabled
constexpr int OverworldCustomMainPaletteEnabled
constexpr int kNumOverworldMaps
AreaSizeEnum
Area size enumeration for v3+ ROMs.
constexpr int OverworldCustomASMHasBeenApplied
constexpr int kDarkWorldMapIdStart
absl::StatusOr< OverworldEntranceTileTypes > LoadEntranceTileTypes(Rom *rom)
constexpr int OverworldCustomMosaicEnabled
constexpr int OverworldCustomSubscreenOverlayEnabled
#define RETURN_IF_ERROR(expr)
bool kEnableSpecialWorldExpansion
struct yaze::core::FeatureFlags::Flags::Overworld overworld
bool has_overworld_tile16
std::vector< int > overworld_tile16_ids
SharedClipboard * shared_clipboard
gfx::IRenderer * renderer
PanelManager * panel_manager
std::vector< std::pair< std::pair< int, int >, int > > tile_changes
static constexpr const char * kTile16Editor
Bitmap atlas
Master bitmap containing all tiles.
std::optional< float > grid_step