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(
101 std::make_unique<AreaGraphicsPanel>(
this));
102 panel_manager->RegisterEditorPanel(
103 std::make_unique<Tile16SelectorPanel>(
this));
104 panel_manager->RegisterEditorPanel(
106 panel_manager->RegisterEditorPanel(
107 std::make_unique<MapPropertiesPanel>(
this));
108 panel_manager->RegisterEditorPanel(
109 std::make_unique<ScratchSpacePanel>(
this));
110 panel_manager->RegisterEditorPanel(
111 std::make_unique<UsageStatisticsPanel>(
this));
112 panel_manager->RegisterEditorPanel(
113 std::make_unique<Tile8SelectorPanel>(
this));
114 panel_manager->RegisterEditorPanel(
115 std::make_unique<DebugWindowPanel>(
this));
116 panel_manager->RegisterEditorPanel(
117 std::make_unique<GfxGroupsPanel>(
this));
118 panel_manager->RegisterEditorPanel(
119 std::make_unique<V3SettingsPanel>(
this));
121 panel_manager->RegisterEditorPanel(
122 std::make_unique<OverworldCanvasPanel>(
this));
149 sidebar_ = std::make_unique<OverworldSidebar>(
157 toolbar_ = std::make_unique<OverworldToolbar>();
158 toolbar_->on_refresh_graphics = [
this]() {
166 toolbar_->on_save_to_scratch = [
this]() {
169 toolbar_->on_load_from_scratch = [
this]() {
179 LOG_DEBUG(
"OverworldEditor",
"Loading overworld.");
181 return absl::FailedPreconditionError(
"ROM not loaded");
207 "Overworld editor refreshed after Tile16 changes");
208 return absl::OkStatus();
214 [
this](
const std::string& entity_type) {
229 [
this](
const std::string& group_name,
int palette_index) {
231 if (group_name ==
"ow_main" || group_name ==
"ow_animated" ||
232 group_name ==
"ow_aux" || group_name ==
"grass") {
234 "Palette change detected: %s, refreshing current map",
242 LOG_DEBUG(
"OverworldEditor",
"Registered as palette listener (ID: %d)",
247 return absl::OkStatus();
256 return absl::OkStatus();
261 return absl::OkStatus();
297 ImGui::SetNextWindowSize(ImVec2(400, 500), ImGuiCond_FirstUseEver);
311 ImGui::SetNextWindowSize(ImVec2(500, 450), ImGuiCond_FirstUseEver);
312 if (ImGui::Begin(
ICON_MD_LAYERS " Visual Effects Editor###OverlayEditor",
332 if (ImGui::BeginPopupModal(
"Entity Insert Error",
nullptr,
333 ImGuiWindowFlags_AlwaysAutoResize)) {
334 ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f),
339 ImGui::TextDisabled(
"Tip: Delete an existing entity to free up a slot.");
341 if (ImGui::Button(
"OK", ImVec2(120, 0))) {
343 ImGui::CloseCurrentPopup();
350 if (ImGui::BeginPopupModal(
"UpgradeROMVersion",
nullptr,
351 ImGuiWindowFlags_AlwaysAutoResize)) {
355 "This will apply the ZSCustomOverworld ASM patch to your ROM,\n"
356 "enabling advanced features like custom tile graphics, animated GFX,\n"
357 "wide/tall areas, and more.");
361 ImGui::Text(
"Current Version: %s",
362 current_version == 0xFF
364 : absl::StrFormat(
"v%d", current_version).c_str());
366 static int target_version = 3;
367 ImGui::RadioButton(
"v2 (Basic features)", &target_version, 2);
369 ImGui::RadioButton(
"v3 (All features)", &target_version, 3);
373 if (ImGui::Button(
ICON_MD_CHECK " Apply Upgrade", ImVec2(150, 0))) {
379 ImGui::CloseCurrentPopup();
381 LOG_ERROR(
"OverworldEditor",
"Upgrade failed: %s",
382 status.message().data());
387 ImGui::CloseCurrentPopup();
407 return absl::OkStatus();
412 if (ImGui::IsAnyItemActive()) {
422 if (ImGui::IsKeyDown(ImGuiKey_1)) {
424 }
else if (ImGui::IsKeyDown(ImGuiKey_2)) {
441 if (ImGui::IsKeyDown(ImGuiKey_F11)) {
446 if (ImGui::IsKeyDown(ImGuiKey_L) && ImGui::IsKeyDown(ImGuiKey_LeftCtrl)) {
451 if (ImGui::IsKeyDown(ImGuiKey_T) && ImGui::IsKeyDown(ImGuiKey_LeftCtrl)) {
464 if (ImGui::IsKeyDown(ImGuiKey_3)) {
468 }
else if (ImGui::IsKeyDown(ImGuiKey_4)) {
472 }
else if (ImGui::IsKeyDown(ImGuiKey_5)) {
476 }
else if (ImGui::IsKeyDown(ImGuiKey_6)) {
480 }
else if (ImGui::IsKeyDown(ImGuiKey_7)) {
484 }
else if (ImGui::IsKeyDown(ImGuiKey_8)) {
494 ImGui::IsKeyDown(ImGuiKey_LeftCtrl) || ImGui::IsKeyDown(ImGuiKey_RightCtrl);
500 if (ImGui::IsKeyPressed(ImGuiKey_Z,
false)) {
501 bool shift_held = ImGui::IsKeyDown(ImGuiKey_LeftShift) ||
502 ImGui::IsKeyDown(ImGuiKey_RightShift);
511 if (ImGui::IsKeyPressed(ImGuiKey_Y,
false)) {
538 if (!ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
542 if (!hovered_entity) {
578 if (!hovered_entity || !ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
627 if (scale <= 0.0f) scale = 1.0f;
631 for (
int i = 0; i < 0x40; i++) {
635 if (world_index < 0 || world_index >=
static_cast<int>(
maps_bmp_.size())) {
652 bool can_draw =
maps_bmp_[world_index].texture() &&
660 ImDrawList* draw_list = ImGui::GetWindowDrawList();
664 ImVec2 placeholder_pos =
665 ImVec2(canvas_pos.x + scrolling.x + map_x,
666 canvas_pos.y + scrolling.y + map_y);
669 ImVec2 placeholder_size = ImVec2(scaled_size, scaled_size);
672 draw_list->AddRectFilled(
674 ImVec2(placeholder_pos.x + placeholder_size.x,
675 placeholder_pos.y + placeholder_size.y),
676 IM_COL32(32, 32, 32, 128));
679 ImVec2 spinner_pos = ImVec2(placeholder_pos.x + placeholder_size.x / 2,
680 placeholder_pos.y + placeholder_size.y / 2);
682 const float spinner_radius = 8.0f * scale;
683 const float rotation =
static_cast<float>(ImGui::GetTime()) * 3.0f;
684 const float start_angle = rotation;
685 const float end_angle = rotation +
IM_PI * 1.5f;
687 draw_list->PathArcTo(spinner_pos, spinner_radius, start_angle, end_angle,
689 draw_list->PathStroke(IM_COL32(100, 180, 100, 255), 0, 2.5f * scale);
705 if (scale <= 0.0f) scale = 1.0f;
708 ImVec2 mouse_position = ImVec2(scaled_position.x / scale,
709 scaled_position.y / scale);
721 if (current_map_ < 0 || current_map_ >=
static_cast<int>(
maps_bmp_.size())) {
730 "Error: tile16_blockset_ is not properly initialized (active: %s, "
744 int mouse_x =
static_cast<int>(mouse_position.x);
745 int mouse_y =
static_cast<int>(mouse_position.y);
751 auto& selected_world =
756 int index_x = superX * 32 + tile16_x;
757 int index_y = superY * 32 + tile16_y;
760 int old_tile_id = selected_world[index_x][index_y];
771 const ImVec2& click_position,
const std::vector<uint8_t>& tile_data) {
773 if (current_map_ < 0 || current_map_ >=
static_cast<int>(
maps_bmp_.size())) {
775 "ERROR: RenderUpdatedMapBitmap - Invalid current_map_ %d "
776 "(maps_bmp_.size()=%zu)",
788 ImVec2 start_position;
789 start_position.x =
static_cast<float>(tile_index_x *
kTile16Size);
790 start_position.y =
static_cast<float>(tile_index_y *
kTile16Size);
796 if (!current_bitmap.
is_active() || current_bitmap.
size() == 0) {
799 "ERROR: RenderUpdatedMapBitmap - Bitmap %d is not active or has no "
800 "data (active=%s, size=%zu)",
802 current_bitmap.
size());
812 if (pixel_index < 0 ||
813 pixel_index >=
static_cast<int>(current_bitmap.
size())) {
816 "ERROR: RenderUpdatedMapBitmap - pixel_index %d out of bounds "
818 pixel_index, current_bitmap.
size());
824 if (tile_data_index < 0 ||
825 tile_data_index >=
static_cast<int>(tile_data.size())) {
828 "ERROR: RenderUpdatedMapBitmap - tile_data_index %d out of bounds "
829 "(tile_data size=%zu)",
830 tile_data_index, tile_data.size());
834 current_bitmap.
WriteToPixel(pixel_index, tile_data[tile_data_index]);
846 LOG_DEBUG(
"OverworldEditor",
"CheckForOverworldEdits: Frame %d",
847 ImGui::GetFrameCount());
861 if (ImGui::IsMouseClicked(ImGuiMouseButton_Left) ||
862 ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
864 "CheckForOverworldEdits: About to apply rectangle selection");
866 auto& selected_world =
882 std::swap(start_x, end_x);
884 std::swap(start_y, end_y);
886 constexpr int local_map_size = 512;
888 constexpr int tiles_per_local_map = local_map_size /
kTile16Size;
891 "CheckForOverworldEdits: About to fill rectangle with "
892 "current_tile16_=%d",
900 for (
int y = start_y;
903 for (
int x = start_x;
907 int local_map_x = x / local_map_size;
908 int local_map_y = y / local_map_size;
915 int index_x = local_map_x * tiles_per_local_map + tile16_x;
916 int index_y = local_map_y * tiles_per_local_map + tile16_y;
922 int rect_width = ((end_x - start_x) /
kTile16Size) + 1;
923 int rect_height = ((end_y - start_y) /
kTile16Size) + 1;
928 int start_local_map_x = start_x / local_map_size;
929 int start_local_map_y = start_y / local_map_size;
930 int end_local_map_x = end_x / local_map_size;
931 int end_local_map_y = end_y / local_map_size;
933 bool in_same_local_map = (start_local_map_x == end_local_map_x) &&
934 (start_local_map_y == end_local_map_y);
936 if (in_same_local_map && index_x >= 0 &&
937 (index_x + rect_width - 1) < 0x200 && index_y >= 0 &&
938 (index_y + rect_height - 1) < 0x200) {
940 int old_tile_id = selected_world[index_x][index_y];
941 if (old_tile_id != tile16_id) {
946 selected_world[index_x][index_y] = tile16_id;
950 ImVec2 tile_position(x, y);
952 if (!tile_data.empty()) {
956 "CheckForOverworldEdits: Updated bitmap at position (%d,%d) "
961 "ERROR: Failed to get tile data for tile16_id=%d",
978 "CheckForOverworldEdits: Rectangle selection applied and cleared");
986 if (scale <= 0.0f) scale = 1.0f;
1020 return absl::FailedPreconditionError(
"Clipboard unavailable");
1025 std::vector<int> ids;
1030 static_cast<int>(std::floor(std::min(start.x, end.x) / 16.0f));
1032 static_cast<int>(std::floor(std::max(start.x, end.x) / 16.0f));
1034 static_cast<int>(std::floor(std::min(start.y, end.y) / 16.0f));
1036 static_cast<int>(std::floor(std::max(start.y, end.y) / 16.0f));
1037 const int width = end_x - start_x + 1;
1038 const int height = end_y - start_y + 1;
1039 ids.reserve(width * height);
1042 for (
int y = start_y; y <= end_y; ++y) {
1043 for (
int x = start_x; x <= end_x; ++x) {
1052 return absl::OkStatus();
1060 return absl::OkStatus();
1062 return absl::FailedPreconditionError(
"Nothing selected to copy");
1067 return absl::FailedPreconditionError(
"Clipboard unavailable");
1070 return absl::FailedPreconditionError(
"Clipboard empty");
1074 return absl::FailedPreconditionError(
"No paste target");
1081 if (scale <= 0.0f) scale = 1.0f;
1082 const ImVec2 anchor = ImVec2(scaled_anchor.x / scale, scaled_anchor.y / scale);
1085 const int tile16_x =
1087 const int tile16_y =
1090 auto& selected_world =
1097 const int tiles_per_local_map = 512 /
kTile16Size;
1104 if (width * height !=
static_cast<int>(ids.size())) {
1105 return absl::InternalError(
"Clipboard dimensions mismatch");
1108 for (
int dy = 0; dy < height; ++dy) {
1109 for (
int dx = 0; dx < width; ++dx) {
1110 const int id = ids[dy * width + dx];
1111 const int gx = tile16_x + dx;
1112 const int gy = tile16_y + dy;
1114 const int global_x = superX * 32 + gx;
1115 const int global_y = superY * 32 + gy;
1116 if (global_x < 0 || global_x >= 256 || global_y < 0 || global_y >= 256)
1118 selected_world[global_x][global_y] = id;
1123 return absl::OkStatus();
1132 if (scale <= 0.0f) scale = 1.0f;
1133 const int large_map_size = 1024;
1141 if (map_x < 0 || map_x >= 8 || map_y < 0 || map_y >= 8) {
1142 return absl::OkStatus();
1145 const bool allow_special_tail =
1149 return absl::OkStatus();
1153 int hovered_map = map_x + map_y * 8;
1155 hovered_map += 0x40;
1157 hovered_map += 0x80;
1167 bool should_build =
false;
1179 ImGui::IsMouseClicked(ImGuiMouseButton_Left) ||
1180 ImGui::IsMouseClicked(ImGuiMouseButton_Right);
1201 bool use_v3_area_sizes =
1205 if (use_v3_area_sizes) {
1208 const int highlight_parent =
1216 parent_map_x = highlight_parent % 8;
1217 parent_map_y = highlight_parent / 8;
1220 parent_map_x = (highlight_parent - 0x40) % 8;
1221 parent_map_y = (highlight_parent - 0x40) / 8;
1224 parent_map_x = (highlight_parent - 0x80) % 8;
1225 parent_map_y = (highlight_parent - 0x80) / 8;
1229 switch (area_size) {
1230 case AreaSizeEnum::LargeArea:
1234 large_map_size, large_map_size);
1236 case AreaSizeEnum::WideArea:
1242 case AreaSizeEnum::TallArea:
1248 case AreaSizeEnum::SmallArea:
1260 const int highlight_parent =
1272 parent_map_x = highlight_parent % 8;
1273 parent_map_y = highlight_parent / 8;
1276 parent_map_x = (highlight_parent - 0x40) % 8;
1277 parent_map_y = (highlight_parent - 0x40) / 8;
1280 parent_map_x = (highlight_parent - 0x80) % 8;
1281 parent_map_y = (highlight_parent - 0x80) / 8;
1286 large_map_size, large_map_size);
1293 current_map_x = current_highlighted_map % 8;
1294 current_map_y = current_highlighted_map / 8;
1297 current_map_x = (current_highlighted_map - 0x40) % 8;
1298 current_map_y = (current_highlighted_map - 0x40) / 8;
1303 current_map_x = (current_highlighted_map - 0x80) % 8;
1304 current_map_y = (current_highlighted_map - 0x80) / 8;
1331 if (ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
1336 if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Right)) {
1340 return absl::OkStatus();
1350 constexpr float kWorldSize = 512.0f * 8.0f;
1351 return ImVec2(kWorldSize * scale, kWorldSize * scale);
1356 ImVec2 visible_size) {
1358 float max_scroll_x = std::max(0.0f, content_size.x - visible_size.x);
1359 float max_scroll_y = std::max(0.0f, content_size.y - visible_size.y);
1363 float clamped_x = std::clamp(scroll.x, -max_scroll_x, 0.0f);
1364 float clamped_y = std::clamp(scroll.y, -max_scroll_y, 0.0f);
1366 return ImVec2(clamped_x, clamped_y);
1466 if (canvas_rt.hovered) {
1476 ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
1487 ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
1488 ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
1489 ImVec2 mouse_delta = ImGui::GetIO().MouseDelta;
1490 float scale = canvas_rt.scale;
1499 ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
1501 float end_scale = canvas_rt.scale;
1503 canvas_rt.scrolling,
1525 ImGui::BeginGroup();
1539 "OwBlocksetSelector", selector_config);
1549 if (result.selection_changed) {
1554 util::logf(
"Failed to set tile16: %s", status.message().data());
1562 if (result.tile_double_clicked) {
1570 return absl::OkStatus();
1589 int offset = 0x40 * (key + 1);
1590 int top_left_y = canvas_rt.canvas_p0.y + 2;
1592 top_left_y = canvas_rt.canvas_p0.y + 0x40 * key;
1594 auto texture = value.texture();
1595 canvas_rt.draw_list->AddImage(
1596 (ImTextureID)(intptr_t)texture,
1597 ImVec2(canvas_rt.canvas_p0.x + 2, top_left_y),
1598 ImVec2(canvas_rt.canvas_p0.x + 0x100,
1599 canvas_rt.canvas_p0.y + offset));
1625 auto bmp = std::make_unique<gfx::Bitmap>();
1646 ImGui::BeginGroup();
1662 return absl::OkStatus();
1671 return absl::OkStatus();
1677 ImGui::TextWrapped(
"ZSCustomOverworld v3 settings panel");
1686 "This panel will contain ZSCustomOverworld configuration options "
1687 "such as custom map sizes, extended tile sets, and other v3 features.");
1699 static bool show_custom_bg_color_editor =
false;
1700 static bool show_overlay_editor =
false;
1701 static int game_state = 0;
1705 show_custom_bg_color_editor, show_overlay_editor);
1709 if (show_custom_bg_color_editor) {
1710 ImGui::OpenPopup(
"CustomBGColorEditor");
1711 show_custom_bg_color_editor =
false;
1713 if (show_overlay_editor) {
1714 ImGui::OpenPopup(
"OverlayEditor");
1715 show_overlay_editor =
false;
1718 if (ImGui::BeginPopup(
"CustomBGColorEditor")) {
1726 if (ImGui::BeginPopup(
"OverlayEditor")) {
1729 show_overlay_editor);
1755 return absl::OkStatus();
1775 auto now = std::chrono::steady_clock::now();
1785 std::make_pair(x, y), old_tile_id);
1794 .tile_changes = {{{x, y}, old_tile_id}},
1823 for (
const auto& [coords, tile_id] : point.
tile_changes) {
1824 auto [x, y] = coords;
1825 world_tiles[x][y] = tile_id;
1837 return absl::FailedPreconditionError(
"Nothing to undo");
1845 .world = point.
world,
1847 .timestamp = std::chrono::steady_clock::now()};
1852 for (
const auto& [coords, old_tile_id] : point.
tile_changes) {
1853 auto [x, y] = coords;
1854 int current_tile_id = world_tiles[x][y];
1857 redo_point.tile_changes.emplace_back(coords, current_tile_id);
1860 world_tiles[x][y] = old_tile_id;
1868 return absl::OkStatus();
1873 return absl::FailedPreconditionError(
"Nothing to redo");
1881 .world = point.
world,
1883 .timestamp = std::chrono::steady_clock::now()};
1888 for (
const auto& [coords, tile_id] : point.
tile_changes) {
1889 auto [x, y] = coords;
1890 int current_tile_id = world_tiles[x][y];
1893 undo_point.tile_changes.emplace_back(coords, current_tile_id);
1896 world_tiles[x][y] = tile_id;
1904 return absl::OkStatus();
1912 LOG_DEBUG(
"OverworldEditor",
"Loading overworld.");
1928 LOG_DEBUG(
"OverworldEditor",
"Loading overworld graphics (optimized).");
1942 "Loading overworld tileset (deferred textures).");
1955 LOG_DEBUG(
"OverworldEditor",
"Loading overworld tile16 graphics.");
1974#ifdef __EMSCRIPTEN__
1975 constexpr int kEssentialMapsPerWorld = 4;
1977 constexpr int kEssentialMapsPerWorld = 16;
1979 constexpr int kLightWorldEssential = kEssentialMapsPerWorld;
1980 constexpr int kDarkWorldEssential =
1982 constexpr int kSpecialWorldEssential =
1987 "Creating bitmaps for essential maps only (first %d maps per world)",
1988 kEssentialMapsPerWorld);
1990 std::vector<gfx::Bitmap*> maps_to_texture;
1991 maps_to_texture.reserve(kEssentialMapsPerWorld *
1997 bool is_essential =
false;
2000 if (i < kLightWorldEssential) {
2001 is_essential =
true;
2003 is_essential =
true;
2005 i < kSpecialWorldEssential) {
2006 is_essential =
true;
2017 maps_to_texture.push_back(&
maps_bmp_[i]);
2018 }
catch (
const std::bad_alloc& e) {
2019 std::cout <<
"Error allocating map " << i <<
": " << e.what()
2030 const int initial_texture_count =
2031 std::min(4,
static_cast<int>(maps_to_texture.size()));
2034 for (
int i = 0; i < initial_texture_count; ++i) {
2044 for (
size_t i = initial_texture_count; i < maps_to_texture.size(); ++i) {
2048 if (&
maps_bmp_[j] == maps_to_texture[i]) {
2055 if (map_index >= 0) {
2056 int map_world = map_index / 0x40;
2075 return absl::OkStatus();
2080 const int depth = 0x10;
2081 for (
int i = 0; i < 3; i++)
2083 int width = sprite.width();
2084 int height = sprite.height();
2085 if (width == 0 || height == 0) {
2092 *sprite.preview_graphics());
2098 return absl::OkStatus();
2108 int refresh_count = 0;
2109 const int max_refreshes_per_frame = 2;
2119 if (is_current_map || is_current_world) {
2135 LOG_ERROR(
"OverworldEditor",
"Failed to build map %d: %s", map_index,
2143 if (!bitmap.is_active()) {
2149 bitmap.SetPalette(palette);
2150 }
catch (
const std::bad_alloc& e) {
2151 LOG_ERROR(
"OverworldEditor",
"Error allocating bitmap for map %d: %s",
2152 map_index, e.what());
2157 if (!bitmap.texture() && bitmap.is_active()) {
2165#ifdef __EMSCRIPTEN__
2178 int world_offset = (center_map / 64) * 64;
2179 int local_index = center_map % 64;
2180 int map_x = local_index % 8;
2181 int map_y = local_index / 8;
2185 static const int dx[] = {-1, 1, 0, 0};
2186 static const int dy[] = {0, 0, -1, 1};
2188 for (
int i = 0; i < 4; ++i) {
2189 int nx = map_x + dx[i];
2190 int ny = map_y + dy[i];
2193 if (nx >= 0 && nx < 8 && ny >= 0 && ny < max_rows) {
2194 int neighbor_index = world_offset + ny * 8 + nx;
2196 if (neighbor_index >= 0 &&
2206#ifdef __EMSCRIPTEN__
2224 LOG_DEBUG(
"OverworldEditor",
"Background preload of map %d failed: %s",
2225 map_to_preload, status.message().data());
2240 maps_bmp_[map_index].set_modified(
true);
2266 if (!is_current_map && !is_current_world) {
2269 maps_bmp_[map_index].set_modified(
true);
2284 bool needs_graphics_rebuild =
maps_bmp_[map_index].modified();
2285 bool needs_palette_rebuild =
false;
2287 if (needs_graphics_rebuild) {
2289 map->LoadAreaGraphics();
2292 auto status = map->BuildTileset();
2294 LOG_ERROR(
"OverworldEditor",
"Failed to build tileset for map %d: %s",
2295 map_index, status.message().data());
2304 "Failed to build tiles16 graphics for map %d: %s", map_index,
2305 status.message().data());
2312 LOG_ERROR(
"OverworldEditor",
"Failed to build bitmap for map %d: %s",
2313 map_index, status.message().data());
2318 maps_bmp_[map_index].set_data(map->bitmap_data());
2319 maps_bmp_[map_index].set_modified(
false);
2322 if (!
maps_bmp_[map_index].ValidateDataSurfaceSync()) {
2324 "Warning: Surface synchronization issue detected for map %d",
2341 bool use_v3_area_sizes =
2344 if (use_v3_area_sizes) {
2349 if (map->is_large_map()) {
2374 if (area_size == AreaSizeEnum::SmallArea) {
2379 int parent_id = map->
parent();
2385 "RefreshMultiAreaMapsSafely: Could not get parent map %d for map %d",
2386 parent_id, map_index);
2392 "RefreshMultiAreaMapsSafely: Processing %s area from parent %d (trigger: %d)",
2393 (area_size == AreaSizeEnum::LargeArea) ?
"large"
2394 : (area_size == AreaSizeEnum::WideArea) ?
"wide"
2396 parent_id, map_index);
2400 std::vector<int> sibling_maps;
2402 switch (area_size) {
2403 case AreaSizeEnum::LargeArea:
2405 sibling_maps = {parent_id, parent_id + 1, parent_id + 8, parent_id + 9};
2408 case AreaSizeEnum::WideArea:
2410 sibling_maps = {parent_id, parent_id + 1};
2413 case AreaSizeEnum::TallArea:
2415 sibling_maps = {parent_id, parent_id + 8};
2420 "RefreshMultiAreaMapsSafely: Unknown area size %d for map %d",
2421 static_cast<int>(area_size), map_index);
2428 for (
int sibling : sibling_maps) {
2430 if (sibling == map_index) {
2446 if (is_current_map || is_current_world) {
2448 "RefreshMultiAreaMapsSafely: Refreshing sibling map %d", sibling);
2452 if (!sibling_map)
continue;
2454 sibling_map->LoadAreaGraphics();
2456 auto status = sibling_map->BuildTileset();
2458 LOG_ERROR(
"OverworldEditor",
"Failed to build tileset for sibling %d: %s",
2459 sibling, status.message().data());
2466 LOG_ERROR(
"OverworldEditor",
"Failed to build tiles16 for sibling %d: %s",
2467 sibling, status.message().data());
2471 status = sibling_map->LoadPalette();
2473 LOG_ERROR(
"OverworldEditor",
"Failed to load palette for sibling %d: %s",
2474 sibling, status.message().data());
2480 LOG_ERROR(
"OverworldEditor",
"Failed to build bitmap for sibling %d: %s",
2481 sibling, status.message().data());
2486 maps_bmp_[sibling].set_data(sibling_map->bitmap_data());
2490 maps_bmp_[sibling].SetPalette(sibling_map->current_palette());
2523 bool use_v3_area_sizes =
2526 if (use_v3_area_sizes) {
2531 if (area_size != AreaSizeEnum::SmallArea) {
2533 std::vector<int> sibling_maps;
2536 switch (area_size) {
2537 case AreaSizeEnum::LargeArea:
2539 sibling_maps = {parent_id, parent_id + 1, parent_id + 8,
2542 case AreaSizeEnum::WideArea:
2544 sibling_maps = {parent_id, parent_id + 1};
2546 case AreaSizeEnum::TallArea:
2548 sibling_maps = {parent_id, parent_id + 8};
2555 for (
int sibling_index : sibling_maps) {
2561 maps_bmp_[sibling_index].SetPalette(sibling_map->current_palette());
2571 for (
int i = 1; i < 4; i++) {
2581 if (
maps_bmp_[sibling_index].is_active() &&
2583 maps_bmp_[sibling_index].SetPalette(sibling_map->current_palette());
2595 return absl::OkStatus();
2600 if (map_index >= 0 && map_index <
static_cast<int>(
maps_bmp_.size())) {
2601 maps_bmp_[map_index].set_modified(
true);
2611 "ForceRefreshGraphics: Map %d marked for refresh", map_index);
2616 bool include_self) {
2617 if (map_index < 0 || map_index >=
static_cast<int>(
maps_bmp_.size())) {
2626 int parent_id = map->parent();
2627 std::vector<int> siblings;
2629 switch (map->area_size()) {
2631 siblings = {parent_id, parent_id + 1, parent_id + 8, parent_id + 9};
2634 siblings = {parent_id, parent_id + 1};
2637 siblings = {parent_id, parent_id + 8};
2643 for (
int sibling : siblings) {
2644 if (sibling >= 0 && sibling < 0xA0) {
2646 if (sibling == map_index && !include_self) {
2662 "RefreshSiblingMapGraphics: Refreshed sibling map %d", sibling);
2672 bool use_v3_area_sizes =
2675 if (use_v3_area_sizes) {
2678 auto area_size = current_ow_map.area_size();
2680 if (area_size != AreaSizeEnum::SmallArea) {
2682 std::vector<int> sibling_maps;
2683 int parent_id = current_ow_map.parent();
2685 switch (area_size) {
2686 case AreaSizeEnum::LargeArea:
2688 sibling_maps = {parent_id + 1, parent_id + 8, parent_id + 9};
2690 case AreaSizeEnum::WideArea:
2692 sibling_maps = {parent_id + 1};
2694 case AreaSizeEnum::TallArea:
2696 sibling_maps = {parent_id + 8};
2703 for (
int sibling_index : sibling_maps) {
2708 map.set_area_graphics(current_ow_map.area_graphics());
2709 map.set_area_palette(current_ow_map.area_palette());
2714 map.set_message_id(current_ow_map.message_id());
2717 map.LoadAreaGraphics();
2722 if (current_ow_map.is_large_map()) {
2724 for (
int i = 1; i < 4; i++) {
2725 int sibling_index = current_ow_map.parent() + i;
2730 map.set_area_graphics(current_ow_map.area_graphics());
2731 map.set_area_palette(current_ow_map.area_palette());
2736 map.set_message_id(current_ow_map.message_id());
2739 map.LoadAreaGraphics();
2746 LOG_DEBUG(
"OverworldEditor",
"RefreshTile16Blockset called");
2749 return absl::OkStatus();
2779 return absl::OkStatus();
2801 constexpr int kTilesPerRow = 8;
2802 constexpr int kTileSize = 16;
2806 bool atlas_modified =
false;
2818 if (!pending_bmp || !pending_bmp->
is_active() ||
2819 pending_bmp->
vector().empty()) {
2824 int tile_x = (tile_id % kTilesPerRow) * kTileSize;
2825 int tile_y = (tile_id / kTilesPerRow) * kTileSize;
2828 if (tile_x + kTileSize > atlas_width || tile_y + kTileSize > atlas_height) {
2834 const auto& pending_data = pending_bmp->
vector();
2836 for (
int y = 0; y < kTileSize && y < pending_bmp->
height(); ++y) {
2837 for (
int x = 0; x < kTileSize && x < pending_bmp->
width(); ++x) {
2838 int atlas_idx = (tile_y + y) * atlas_width + (tile_x + x);
2839 int pending_idx = y * pending_bmp->
width() + x;
2841 if (atlas_idx >= 0 &&
2842 atlas_idx <
static_cast<int>(atlas_data.size()) &&
2844 pending_idx <
static_cast<int>(pending_data.size())) {
2845 atlas_data[atlas_idx] = pending_data[pending_idx];
2846 atlas_modified =
true;
2862 if (ImGui::IsMouseClicked(ImGuiMouseButton_Middle) &&
2863 ImGui::IsItemHovered()) {
2867 if (scale <= 0.0f) scale = 1.0f;
2870 int hovered_map = map_x + map_y * 8;
2872 hovered_map += 0x40;
2874 hovered_map += 0x80;
2878 if (hovered_map >= 0 && hovered_map < 0xA0) {
2891 if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) &&
2892 ImGui::IsItemHovered()) {
2922 static bool init_properties =
false;
2924 if (!init_properties) {
2925 for (
int i = 0; i < 0x40; i++) {
2926 std::string area_graphics_str = absl::StrFormat(
2929 ->push_back(area_graphics_str);
2931 area_graphics_str = absl::StrFormat(
2934 ->push_back(area_graphics_str);
2936 std::string area_palette_str =
2939 ->push_back(area_palette_str);
2941 area_palette_str = absl::StrFormat(
2944 ->push_back(area_palette_str);
2945 std::string sprite_gfx_str = absl::StrFormat(
2948 ->push_back(sprite_gfx_str);
2950 sprite_gfx_str = absl::StrFormat(
2953 ->push_back(sprite_gfx_str);
2955 sprite_gfx_str = absl::StrFormat(
2958 ->push_back(sprite_gfx_str);
2960 sprite_gfx_str = absl::StrFormat(
2963 ->push_back(sprite_gfx_str);
2965 std::string sprite_palette_str = absl::StrFormat(
2968 ->push_back(sprite_palette_str);
2970 sprite_palette_str = absl::StrFormat(
2973 ->push_back(sprite_palette_str);
2975 sprite_palette_str = absl::StrFormat(
2978 ->push_back(sprite_palette_str);
2980 sprite_palette_str = absl::StrFormat(
2983 ->push_back(sprite_palette_str);
2985 init_properties =
true;
2988 ImGui::Text(
"Area Gfx LW/DW");
2996 ImGui::Text(
"Sprite Gfx LW/DW");
3010 ImGui::Text(
"Area Pal LW/DW");
3017 static bool show_gfx_group =
false;
3018 ImGui::Checkbox(
"Show Gfx Group Editor", &show_gfx_group);
3019 if (show_gfx_group) {
3037 return absl::OkStatus();
3045 if (target_version < 2 || target_version > 3) {
3046 return absl::InvalidArgumentError(absl::StrFormat(
3047 "Invalid target version: %d. Must be 2 or 3.", target_version));
3052 if (current_version != 0xFF && current_version >= target_version) {
3053 return absl::AlreadyExistsError(absl::StrFormat(
3054 "ROM is already version %d or higher", current_version));
3057 LOG_DEBUG(
"OverworldEditor",
"Applying ZSCustomOverworld ASM v%d to ROM...",
3061 auto asar_wrapper = std::make_unique<core::AsarWrapper>();
3065 std::vector<uint8_t> original_rom_data =
rom_->
vector();
3066 std::vector<uint8_t> working_rom_data = original_rom_data;
3071 std::string asm_file_name =
3072 (target_version == 3) ?
"asm/yaze.asm"
3073 :
"asm/ZSCustomOverworld.asm";
3079 LOG_DEBUG(
"OverworldEditor",
"Using ASM file: %s", asm_file_path.c_str());
3082 if (!std::filesystem::exists(asm_file_path)) {
3083 return absl::NotFoundError(
3084 absl::StrFormat(
"ASM file not found at: %s\n\n"
3085 "Expected location: assets/%s\n"
3086 "Make sure the assets directory is accessible.",
3087 asm_file_path, asm_file_name));
3092 asar_wrapper->ApplyPatch(asm_file_path, working_rom_data);
3093 if (!patch_result.ok()) {
3094 return absl::InternalError(absl::StrFormat(
3095 "Failed to apply ASM patch: %s", patch_result.status().message()));
3098 const auto& result = patch_result.value();
3099 if (!result.success) {
3100 std::string error_details =
"ASM patch failed with errors:\n";
3101 for (
const auto& error : result.errors) {
3102 error_details +=
" - " + error +
"\n";
3104 if (!result.warnings.empty()) {
3105 error_details +=
"Warnings:\n";
3106 for (
const auto& warning : result.warnings) {
3107 error_details +=
" - " + warning +
"\n";
3110 return absl::InternalError(error_details);
3121 "ASM patch applied successfully. Found %zu symbols:",
3122 result.symbols.size());
3123 for (
const auto& symbol : result.symbols) {
3124 LOG_DEBUG(
"OverworldEditor",
" %s @ $%06X", symbol.name.c_str(),
3132 "ZSCustomOverworld v%d successfully applied to ROM",
3134 return absl::OkStatus();
3136 }
catch (
const std::exception& e) {
3139 if (!restore_result.ok()) {
3140 LOG_ERROR(
"OverworldEditor",
"Failed to restore ROM data: %s",
3141 restore_result.message().data());
3143 return absl::InternalError(
3144 absl::StrFormat(
"Exception during ASM application: %s", e.what()));
3151 static_cast<uint8_t
>(target_version);
3154 if (target_version >= 2) {
3160 "Enabled v2+ features: Custom BG colors, Main palettes");
3163 if (target_version >= 3) {
3172 "Enabled v3+ features: Subscreen overlays, Animated GFX, Tile GFX "
3176 for (
int i = 0; i < 0xA0; i++) {
3182 const std::vector<int> large_areas = {
3183 0x00, 0x02, 0x05, 0x07, 0x0A, 0x0B, 0x0F, 0x10, 0x11, 0x12,
3184 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1D,
3185 0x1E, 0x25, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2E, 0x2F, 0x30,
3186 0x32, 0x33, 0x34, 0x35, 0x37, 0x3A, 0x3B, 0x3C, 0x3F};
3188 for (
int area_id : large_areas) {
3189 if (area_id < 0xA0) {
3195 LOG_DEBUG(
"OverworldEditor",
"Initialized area size data for %zu areas",
3196 large_areas.size());
3199 LOG_DEBUG(
"OverworldEditor",
"ROM version markers updated to v%d",
3201 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