13#define IM_PI 3.14159265358979323846f
30#include <unordered_map>
35#include "absl/status/status.h"
36#include "absl/strings/str_format.h"
37#include "imgui/imgui.h"
116 for (
size_t i = 0; i < lhs.
items.size(); ++i) {
157 const int clamped_world = std::clamp(current_world, 0, 2);
158 int normalized_map = current_map;
164 const int normalized_world = std::clamp(normalized_map / 0x40, 0, 2);
166 normalized_world != current_world || normalized_map != current_map;
167 current_world = normalized_world;
168 current_map = normalized_map;
177 "Gfx Groups: selection syncs with the Graphics editor for this ROM "
179 "(this surface uses its own preview canvases).");
190 std::make_unique<EntityMutationService>(
overworld_);
211 [
this](
int map_id) -> absl::Status {
217 [
this](
int map_index,
bool respect_pin) {
225 [
this](
const std::vector<OverworldPropertyEdit>& edits,
226 const std::string& description) {
230 [
this](
const std::string&
type,
int id,
const std::string& label) {
243 [
this](
const std::string&
type,
int id,
const std::string& label) {
249 toolbar_ = std::make_unique<OverworldToolbar>();
250 toolbar_->on_story_changed = [
this](
int state) {
254 toolbar_->on_world_changed = [
this](
int world) {
263 toolbar_->on_insert_item = [
this]() {
266 toolbar_->on_open_map_properties = [
this]() {
269 toolbar_->on_toggle_overlay_preview = [
this]() {
272 toolbar_->is_overlay_preview_enabled = [
this]() {
275 toolbar_->on_toggle_grid = [
this]() {
278 toolbar_->is_grid_visible = [
this]() {
281 toolbar_->on_toggle_entities = [
this]() {
284 toolbar_->are_entities_visible = [
this]() {
296 toolbar_->on_center_map = [
this]() {
302 toolbar_->on_toggle_overlay_preview = [
this]() {
305 toolbar_->is_overlay_preview_enabled = [
this]() {
308 toolbar_->on_upgrade_rom_version = [
this](int) {
309 ImGui::OpenPopup(
"UpgradeROMVersion");
329 [
this](
int map_id) {
JumpToMap(map_id); });
331 [
this](
const char* name) -> std::string {
357 const int step = shift_held ? 16 : 1;
364 "overworld.entity_workbench"));
374 switch (entity->entity_type_) {
405 std::make_unique<OverworldInteractionCoordinator>(std::move(sink));
448 tile_painting_ = std::make_unique<TilePaintingManager>(deps, callbacks);
491 canvas_nav_ = std::make_unique<CanvasNavigationManager>();
515 const Rom& replacement)
const {
522 return absl::OkStatus();
524 return absl::FailedPreconditionError(
525 "Lyra canvas session replacement requires a fresh editor session");
531 LOG_DEBUG(
"OverworldEditor",
"Loading overworld.");
533 return absl::FailedPreconditionError(
"ROM not loaded");
537 return absl::FailedPreconditionError(
538 "Invalid Lyra descriptor; overworld loading refused");
546 return absl::OkStatus();
551 return absl::FailedPreconditionError(
552 "Lyra canvas session replacement requires a fresh editor session");
570 return absl::OkStatus();
573 [
this]() { FinalizePaintOperation(); });
584 [
this](
int tile16_id,
int quadrant) {
589 [
this](
const std::vector<Tile16Commit>&) {
600 [
this](
const std::string& entity_type,
625 [
this](
const std::string& group_name,
int palette_index) {
631 map_refresh_->RefreshPaletteUsers(group_name, palette_index);
633 LOG_DEBUG(
"OverworldEditor",
"Registered as palette listener (ID: %d)",
640 if (!scratch_status.ok()) {
641 LOG_WARN(
"OverworldEditor",
"Failed to load scratch pad: %s",
642 std::string(scratch_status.message()).c_str());
651 return absl::OkStatus();
655 state = std::clamp(state, 0, 2);
666 map->set_game_state(state);
678 bool refresh_selection =
false;
681 if (overworld_map !=
nullptr) {
682 overworld_map->set_game_state(std::clamp(
game_state_, 0, 2));
687 if (overworld_map !=
nullptr && !
maps_bmp_[map].modified() &&
688 overworld_map->SourceSheetsChanged()) {
693 if (refresh_selection) {
707 return absl::OkStatus();
712 return absl::OkStatus();
754 ImGui::SetNextWindowSize(ImVec2(400, 500), ImGuiCond_FirstUseEver);
768 ImGui::SetNextWindowSize(ImVec2(500, 450), ImGuiCond_FirstUseEver);
769 if (ImGui::Begin(
ICON_MD_LAYERS " Visual Effects Editor###OverlayEditor",
785 workbench->DrawPopups();
788 if (ImGui::BeginPopupModal(
"UpgradeROMVersion",
nullptr,
789 ImGuiWindowFlags_AlwaysAutoResize)) {
793 tr(
"This will apply the ZSCustomOverworld ASM patch to your ROM,\n"
794 "enabling advanced overworld features."));
797 const uint8_t current_version =
800 ImGui::Text(tr(
"Current Version: %s"),
801 current_version == 0xFF
803 : absl::StrFormat(
"v%d", current_version).c_str());
805 static int target_version = 3;
806 ImGui::RadioButton(tr(
"v2 (Basic features)"), &target_version, 2);
808 ImGui::RadioButton(tr(
"v3 (All features)"), &target_version, 3);
812 if (ImGui::Button(
ICON_MD_CHECK " Apply Upgrade", ImVec2(150.0f, 0.0f))) {
813 auto upgrade_status =
816 : absl::FailedPreconditionError(
"Upgrade system not initialized");
817 if (upgrade_status.ok()) {
823 ImGui::CloseCurrentPopup();
826 LOG_ERROR(
"OverworldEditor",
"Upgrade failed: %s",
827 upgrade_status.message().data());
831 if (ImGui::Button(
ICON_MD_CANCEL " Cancel", ImVec2(150.0f, 0.0f))) {
832 ImGui::CloseCurrentPopup();
847 return absl::OkStatus();
859 bool changed =
false;
861 const int normalized =
863 const int world = normalized / 0x40;
881 "Normalized stale overworld selection to world=%d map=%d",
913 const bool tracking_paint = respect_pin &&
917 if (!tracking_paint) {
944 map_texture_->PrimeWorldMaps(world, process_texture_queue);
950 const int clamped_world = std::clamp(world, 0, 2);
952 const int world_start = clamped_world * 0x40;
954 if (maps_remaining <= 0) {
992 "overworld.entity_workbench"));
1006 "overworld.entity_workbench"));
1021 "overworld.entity_workbench"));
1037 workbench->SetActiveEntity(item);
1060 *items = snapshot.
items;
1063 auto* selected_item =
1065 if (selected_item) {
1068 workbench->SetActiveEntity(selected_item);
1084 std::string description) {
1086 if (ItemSnapshotsEqual(before, after)) {
1091 std::move(before), std::move(after),
1095 std::move(description)));
1100 if (!selected_item) {
1103 "Select an overworld item first (click one, or Items mode: 5)",
1113 *selected_item, offset_x, offset_y);
1116 offset_x, offset_y);
1117 if (duplicate_or.ok()) {
1118 duplicate_result.
entity = *duplicate_or;
1119 duplicate_result.
status = absl::OkStatus();
1121 duplicate_result.
status = duplicate_or.status();
1123 "Failed to duplicate overworld item: " +
1124 std::string(duplicate_or.status().message());
1127 if (!duplicate_result.
ok()) {
1131 ?
"Failed to duplicate overworld item"
1138 auto* duplicated_item =
1143 workbench->SetActiveEntity(duplicated_item);
1146 absl::StrFormat(
"Duplicate overworld item 0x%02X",
1147 static_cast<int>(duplicated_item->id_)));
1151 absl::StrFormat(
"Duplicated item 0x%02X",
1152 static_cast<int>(duplicated_item->id_)),
1160 if (!selected_item) {
1165 auto status =
NudgeItem(selected_item, delta_x, delta_y);
1177 workbench->SetActiveEntity(selected_item);
1180 std::move(before_snapshot),
1181 absl::StrFormat(
"Move overworld item 0x%02X (%+d,%+d)",
1182 static_cast<int>(selected_item->id_), delta_x, delta_y));
1189 if (!selected_item) {
1195 const uint8_t deleted_item_id = selected_identity.
id_;
1196 absl::Status remove_status =
1197 absl::FailedPreconditionError(
"Entity mutation service not initialized");
1204 if (!remove_status.ok()) {
1212 auto* nearest_item =
1220 workbench->SetActiveEntity(nearest_item);
1227 absl::StrFormat(
"Delete overworld item 0x%02X",
1228 static_cast<int>(deleted_item_id)));
1233 absl::StrFormat(
"Deleted item 0x%02X (selected nearest 0x%02X)",
1234 static_cast<int>(deleted_item_id),
1235 static_cast<int>(nearest_item->id_)),
1239 absl::StrFormat(
"Deleted overworld item 0x%02X",
1240 static_cast<int>(deleted_item_id)),
1253 return absl::FailedPreconditionError(
"Clipboard unavailable");
1261 return absl::OkStatus();
1270 return absl::OkStatus();
1272 return absl::FailedPreconditionError(
"Nothing selected to copy");
1277 return absl::FailedPreconditionError(
"Lyra preview is read-only");
1280 return absl::FailedPreconditionError(
"Clipboard unavailable");
1283 return absl::FailedPreconditionError(
"Clipboard empty");
1286 return absl::FailedPreconditionError(
"Tile painting is unavailable");
1290 clipboard.overworld_height,
1291 clipboard.overworld_tile16_ids});
1297 return absl::OkStatus();
1302 ImGui::TextDisabled(
"Lyra preview: graphics editing unavailable");
1303 return absl::OkStatus();
1310 return absl::OkStatus();
1320 return absl::FailedPreconditionError(
1321 "Overworld save transaction is already active");
1325 return absl::OkStatus();
1345 if (
rom_ ==
nullptr) {
1346 return absl::FailedPreconditionError(
"No ROM for overworld sprites");
1353 std::vector<std::pair<uint32_t, uint32_t>> ranges;
1354 for (
const auto& write : sprite_plan) {
1355 ranges.emplace_back(write.address, write.address + write.bytes.size());
1365 const bool saving_maps =
1377 manifest, write_policy, ranges,
"overworld maps",
"OverworldEditor",
1384 if (profile.has_expanded_tile32) {
1389 if (profile.has_expanded_tile16) {
1411 save_transaction.
Commit();
1412 return absl::OkStatus();
1432 auto now = std::chrono::steady_clock::now();
1449 .tile_changes = {{{x, y}, old_tile_id}},
1461 std::vector<OverworldTileChange> changes;
1463 for (
const auto& [coords, old_tile_id] :
1465 auto [x, y] = coords;
1466 int new_tile_id = world_tiles[x][y];
1467 changes.push_back({x, y, old_tile_id, new_tile_id});
1469 auto action = std::make_unique<OverworldTilePaintAction>(
1471 std::move(changes), &
overworld_, std::function<
void()>{},
1472 [
this](
int map_id) {
1473 maps_bmp_[map_id].set_modified(true);
1474 RefreshOverworldMapOnDemand(map_id);
1485 return absl::FailedPreconditionError(
"Lyra preview is read-only");
1488 return absl::FailedPreconditionError(
"MapPropertiesSystem not initialized");
1500 before.
value = before_value;
1505 if (after_value == before_value) {
1506 return absl::OkStatus();
1510 after.
value = after_value;
1517 return absl::OkStatus();
1521 const std::vector<OverworldPropertyEdit>& edits,
1522 const std::string& description,
bool record_undo) {
1524 return absl::FailedPreconditionError(
"Lyra preview is read-only");
1527 return absl::FailedPreconditionError(
"MapPropertiesSystem not initialized");
1529 if (edits.empty()) {
1530 return absl::OkStatus();
1534 for (
const auto& edit : edits) {
1537 return absl::OkStatus();
1542 std::vector<OverworldPropertyEdit> before_edits;
1543 std::vector<OverworldPropertyEdit> after_edits;
1544 before_edits.reserve(edits.size());
1545 after_edits.reserve(edits.size());
1547 for (
const auto& edit : edits) {
1553 if (!before_value.ok()) {
1554 for (
auto it = before_edits.rbegin(); it != before_edits.rend(); ++it) {
1557 return before_value.status();
1561 before.
value = *before_value;
1562 const absl::Status apply_status =
1564 if (!apply_status.ok()) {
1565 for (
auto it = before_edits.rbegin(); it != before_edits.rend(); ++it) {
1568 return apply_status;
1572 if (!after_value.ok()) {
1574 for (
auto it = before_edits.rbegin(); it != before_edits.rend(); ++it) {
1577 return after_value.status();
1579 if (*after_value == *before_value) {
1584 after.
value = *after_value;
1585 before_edits.push_back(std::move(before));
1586 after_edits.push_back(std::move(after));
1589 if (after_edits.empty()) {
1590 return absl::OkStatus();
1593 const std::string action_description =
1595 ? absl::StrFormat(
"Edit %d overworld map properties",
1596 static_cast<int>(after_edits.size()))
1599 before_edits, after_edits,
1603 action_description));
1604 return absl::OkStatus();
1608 const std::string& type,
int id,
const std::string& label) {
1610 return absl::FailedPreconditionError(
"Lyra preview is read-only");
1613 return absl::FailedPreconditionError(
"No project is open");
1617 const std::string before =
1621 const std::string after =
1623 if (after == before) {
1624 return absl::OkStatus();
1629 [
this,
type,
id](
const std::string& value) {
1633 absl::StrFormat(
"Rename %s 0x%02X",
type,
id)));
1634 return absl::OkStatus();
1639 return absl::FailedPreconditionError(
"Lyra preview is read-only");
1648 return absl::FailedPreconditionError(
"Lyra preview is read-only");
1658 const std::string path =
1660 ? owner->GetAbsolutePath(owner->overworld_graphics_preview)
1663 return absl::OkStatus();
1676 return preview.status();
1682 LOG_DEBUG(
"OverworldEditor",
"Loading overworld.");
1699 return absl::OkStatus();
1718 LOG_DEBUG(
"OverworldEditor",
"Loading overworld graphics (optimized).");
1732 "Loading overworld tileset (deferred textures).");
1745 LOG_DEBUG(
"OverworldEditor",
"Loading overworld tile16 graphics.");
1774 return absl::OkStatus();
1779 const int depth = 0x10;
1780 for (
int i = 0; i < 3; i++)
1782 int width = sprite.width();
1783 int height = sprite.height();
1784 if (width == 0 || height == 0) {
1791 *sprite.preview_graphics());
1797 return absl::OkStatus();
1829 map_texture_ = std::make_unique<OverworldMapTextureCoordinator>(ctx);
1852 map_refresh_ = std::make_unique<MapRefreshCoordinator>(ctx);
1869 return absl::FailedPreconditionError(
"Overworld ROM session is not loaded");
1871 if (tile16_id < 0 ||
1873 return absl::InvalidArgumentError(
"Selected Tile16 is unavailable");
1876 if (manager ==
nullptr) {
1877 return absl::FailedPreconditionError(
"Graphics editor is unavailable");
1884 if (map ==
nullptr || !map->is_built()) {
1885 return absl::FailedPreconditionError(
"Selected overworld map is not built");
1887 if (map->HasGraphicsPreview()) {
1888 return absl::UnimplementedError(
1889 "Project graphics overrides need an explicit editable source map");
1899 auto preview_map = std::make_shared<zelda3::OverworldMap>(*map);
1901 context->resolve_live_source = [
this, owner, source_map_id, tile16_id,
1905 return absl::FailedPreconditionError(
"Overworld source session changed");
1910 if (!live || live->HasGraphicsPreview()) {
1911 return absl::UnimplementedError(
1912 "Project graphics overrides have no writable source");
1914 live->LoadAreaGraphics();
1919 return resolved.status();
1920 fresh = std::move(*resolved);
1921 return absl::OkStatus();
1923 context->limitation +=
" Palette colors follow live edits.";
1924 context->refresh_palettes = [
this, preview_map, owner, tile16_id, quadrant](
1928 return absl::FailedPreconditionError(
"Overworld source session changed");
1934 if (!refreshed.ok())
1935 return refreshed.status();
1939 [
this, preview_map, owner,
1940 source_map_id]() -> absl::StatusOr<usage_preview::PreviewImage> {
1942 return absl::FailedPreconditionError(
1943 "Overworld graphics source session has changed");
1945 std::set<uint16_t> ids;
1946 for (
int slot = 0; slot < 8; ++slot) {
1947 ids.insert(preview_map->static_graphics(slot));
1949 ids.insert(preview_map->animated_sheet());
1950 preview_map->SetGraphicsSheetOverrides(
1956 const int world = source_map_id / 0x40;
1961 image.
pixels = preview_map->bitmap_data();
1963 const auto& palette = preview_map->current_palette();
1964 for (
size_t i = 0; i < palette.size() && i < image.
colors.size(); ++i) {
1965 const auto rgb = palette[i].rgb();
1967 SDL_Color{
static_cast<uint8_t
>(rgb.x),
static_cast<uint8_t
>(rgb.y),
1968 static_cast<uint8_t
>(rgb.z), 255};
1972 context->return_to_source = [
this, manager, source_map_id, tile16_id]() {
1981 auto* editor_set = manager->GetCurrentEditorSet();
1982 if (editor_set ==
nullptr || editor_set->GetGraphicsEditor() ==
nullptr) {
1983 return absl::FailedPreconditionError(
"Graphics editor is unavailable");
1987 editor_set->GetGraphicsEditor()->OpenInContext(std::move(*
context)));
1989 return absl::OkStatus();
2010 LOG_WARN(
"OverworldEditor",
"Failed to refresh selected Tile16 %d: %s",
2011 tile_id, status.message().data());
2037 return absl::OkStatus();
2081 const int x = local % 8 + dx;
2082 const int y = local / 8 + dy;
2083 if (x < 0 || x >= 8 || y < 0 || y >= rows) {
2086 const int target = world * 0x40 + y * 8 + x;
2107 const int world = std::clamp(map_id / 0x40, 0, 2);
2162 if (!window_manager) {
2169 const std::string window_name = window_manager->GetWorkspaceWindowName(
2171 if (!window_name.empty()) {
2172 ImGui::SetWindowFocus(window_name.c_str());
2187 const char* mode_label;
2190 mode_label =
"Entrances";
2193 mode_label =
"Exits";
2196 mode_label =
"Items";
2199 mode_label =
"Sprites";
2202 mode_label =
"Transports";
2205 mode_label =
"Music";
2210 mode_label =
"Mouse";
2213 mode_label =
"Draw";
2216 mode_label =
"Fill";
2219 mode_label =
"Draw";
2224 mode_label =
"Draw";
2236 snapshot.
title =
"Overworld";
2237 snapshot.
subtitle =
"Load a ROM to inspect map context";
2247 {.id =
"map", .label =
"Map", .value = map.
map_id_label},
2248 {.id =
"parent", .label =
"Parent", .value = map.
parent_label},
2252 {.id =
"graphics", .label =
"Graphics", .value = map.
area_gfx_label},
2257 const auto belongs_to_area = [owner_map](
const auto& entity) {
2258 return static_cast<int>(entity.map_id_) == owner_map;
2266 .value = std::to_string(std::count_if(
2267 sprites.begin(), sprites.end(),
2269 return static_cast<int>(sprite.map_id()) == owner_map;
2273 .value = std::to_string(
2274 std::count_if(items.begin(), items.end(), belongs_to_area))},
2276 .label =
"Entrances",
2282 .value = exits ? std::to_string(std::count_if(
2283 exits->begin(), exits->end(), belongs_to_area))
2294 .message =
"Map context is pinned; cursor hover will not replace it.",
2299 .id =
"tile_selection",
2301 .message = absl::StrFormat(
"%zu Tile16 cells selected",
2307 "overworld.properties",
2308 "overworld.entities",
2312 {.id =
"open_properties",
2313 .label =
"Map Properties",
2314 .target =
"overworld.properties"},
2315 {.id =
"open_entities",
2316 .label =
"Entity Workbench",
2317 .target =
"overworld.entity_workbench"},
2318 {.id =
"open_tile16",
2319 .label =
"Tile16 Selector",
2320 .target =
"overworld.tile16_selector"},
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
absl::Status CheckLayoutWritable() const
rom::LyraLayoutInspection InspectLyraLayout() const
void set_dirty(bool dirty)
bool loaded() const
Check if the manifest has been loaded.
The EditorManager controls the main editor window and manages the various editor classes.
EditorContext context() const
UndoManager undo_manager_
EditorDependencies dependencies_
void SetHostSurfaceHint(std::string hint)
Main UI class for editing overworld maps in A Link to the Past.
void ToggleItemListWindow()
bool grid_visible() const
void LocateOverworldTile(int map_id, int world_x, int world_y)
std::unique_ptr< UsageStatisticsCard > usage_stats_card_
absl::Status Clear() override
std::unique_ptr< MapPropertiesSystem > map_properties_system_
std::unique_ptr< OverworldCanvasRenderer > canvas_renderer_
OverworldEntityWorkbench * GetWorkbench()
Resolve the entity workbench window content (may be null).
void HandleMapInteraction()
bool overworld_canvas_fullscreen_
bool map_blockset_loaded_
void InitMapTextureCoordinator()
zelda3::OverworldEntranceTileTypes entrance_tiletypes_
void CenterOverworldView()
std::array< gfx::Bitmap, zelda3::kExpandedMapCount > maps_bmp_
EditorContextSnapshot BuildContextSnapshot() const override
std::optional< OverworldUndoPoint > current_paint_operation_
absl::Status BeginSaveTransaction() override
void PushItemUndoAction(OverworldItemsSnapshot before, std::string description)
absl::Status CheckForCurrentMap()
Check for map changes and refresh if needed.
void CreateUndoPoint(int map_id, int world, int x, int y, int old_tile_id)
void CommitSaveTransaction() override
void ForceRefreshGraphics(int map_index)
void RequestTile16Selection(int tile_id)
void RestoreItemUndoSnapshot(const OverworldItemsSnapshot &snapshot)
std::vector< int > selected_tile16_ids_
absl::Status ApplyOverworldPropertyEdit(const OverworldPropertyEdit &edit, bool record_undo=true)
zelda3::GameData * game_data_
gfx::Bitmap current_gfx_bmp_
gfx::Tilemap tile16_blockset_
void InitCanvasNavigationManager()
Initialize the canvas navigation manager (called during Initialize)
void JumpToMap(int map_id)
void InitMapRefreshCoordinator()
Initialize the map refresh coordinator (called during Initialize)
void RequestItemInsertPopup(const std::optional< OverworldContextTarget > &target)
std::optional< zelda3::OverworldItem > selected_item_identity_
void CycleTileSelection(int delta)
absl::Status RefreshMapPalette(int map_id=-1)
void ResetOverworldView()
void OpenMapPropertiesWindow()
Tile16Editor tile16_editor_
std::unique_ptr< zelda3::OverworldUpgradeSystem > upgrade_system_
gui::Canvas ow_map_canvas_
absl::Status SyncProjectGraphicsPreview(bool force=false)
void HandleEntityInsertion(const std::string &entity_type, const OverworldContextTarget &target)
Handle entity insertion from context menu.
void NotifyEntityModified(zelda3::GameEntity *entity)
Notify that an entity has been modified, marking ROM as dirty.
void SwitchToWorld(int world)
absl::Status CheckRomReplacement(const Rom &replacement) const
absl::Status Undo() override
void ToggleTile16EditorWindow()
void SetPreviewStoryState(int state)
std::unique_ptr< MapRefreshCoordinator > map_refresh_
void SelectAdjacentMap(int dx, int dy)
Alt+Arrow: select the neighbouring screen in the world grid and center it.
std::unique_ptr< OverworldMapTextureCoordinator > map_texture_
bool is_lyra_preview() const
static bool NormalizeMapSelection(int ¤t_world, int ¤t_map)
Clamp a possibly stale world/map selection back into a valid overworld range.
bool PickTile16FromHoveredCanvas()
void RefreshOverworldMap()
void CheckForOverworldEdits()
Check for tile edits - delegates to TilePaintingManager.
void ContributeStatus(StatusBar *status_bar) override
~OverworldEditor() override
gui::Canvas scratch_canvas_
void PrimeWorldMaps(int world, bool process_texture_queue=false)
bool SampleContextTile16(const OverworldContextTarget &target)
absl::Status ApplyOverworldPropertyEdits(const std::vector< OverworldPropertyEdit > &edits, const std::string &description, bool record_undo=true)
void set_current_map(int map_id)
Set the current map for editing (also updates world)
void RefreshOverworldMapOnDemand(int map_index)
void RefreshMapsForSheetEdits()
Mark maps for a graphics rebuild when a sheet they use changed in the session's sheet store (an unsav...
gui::Canvas current_gfx_canvas_
void Initialize() override
bool show_overlay_editor_
std::unique_ptr< OverworldSidebar > sidebar_
absl::Status Redo() override
void SetupCanvasAutomation()
std::optional< int > pending_tile16_selection_after_gfx_
void HandleTile16Edit(const OverworldContextTarget &target)
Handle tile16 editing from context menu (MOUSE mode) Opens the Tile16 captured at menu-open through t...
void SetEditingMode(EditingMode mode)
Tool change from the toolbar or keys 1/2: also leaves entity modes.
absl::Status RenameProjectResourceLabelWithUndo(const std::string &type, int id, const std::string &label)
zelda3::OverworldItem * GetSelectedItem()
Resolve selected item identity to the current live item pointer.
bool NormalizeCurrentSelectionState()
Handle overworld keyboard shortcuts and edit-mode hotkeys.
bool DuplicateSelectedItem(int offset_x=16, int offset_y=0)
Duplicate selected item with optional pixel offset.
void DrawOverworldCanvas()
Draw the main overworld canvas.
gui::Canvas blockset_canvas_
absl::Status RefreshTile16Blockset()
void ToggleCanvasFullscreen()
zelda3::Overworld & overworld()
Access the underlying Overworld data.
void SetCurrentEntity(zelda3::GameEntity *entity)
Set the currently active entity for editing.
absl::Status LoadScratchPad()
void UpdateBlocksetSelectorState()
void RollbackSaveTransaction() override
void FinalizePaintOperation()
std::unique_ptr< OverworldEntityRenderer > entity_renderer_
auto & GetWorldTiles(int world)
std::optional< OverworldEntityInsertionRequest > pending_entity_insertion_
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 OpenGraphicsForTile16Quadrant(int tile16_id, int quadrant)
std::unique_ptr< CanvasNavigationManager > canvas_nav_
absl::Status Copy() override
bool NudgeSelectedItem(int delta_x, int delta_y)
Move selected item by signed pixel deltas.
EntityEditMode entity_edit_mode_
const gfx::Bitmap * AreaScreenBitmap(int map_id)
std::unique_ptr< OverworldToolbar > toolbar_
GfxGroupEditor gfx_group_editor_
void ProcessDeferredTextures()
Create textures for deferred map bitmaps on demand.
absl::Status OpenGraphicsForContextTile16(const OverworldContextTarget &target)
absl::Status Update() final
std::unique_ptr< gui::TileSelectorWidget > blockset_selector_
bool show_overlay_preview_
bool entities_visible() const
void RefreshMapProperties(int map_id=-1)
void ToggleEntityVisibility()
absl::Status Paste() override
void ScrollBlocksetCanvasToCurrentTile()
Scroll the blockset canvas to show the current selected tile16.
bool DeleteSelectedItem()
Delete selected item and preserve nearest-item selection continuity.
std::unique_ptr< TilePaintingManager > tile_painting_
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.
absl::Status LoadSpriteGraphics()
gfx::IRenderer * renderer_
OverworldItemsSnapshot CaptureItemUndoSnapshot() const
absl::Status Save() override
void InitInteractionCoordinator()
std::unique_ptr< DebugWindowCard > debug_window_card_
void SelectMapForEditing(int map_id, bool respect_pin=true)
std::unique_ptr< OverworldInteractionCoordinator > interaction_coordinator_
zelda3::GameEntity * current_entity_
void SetEntityEditMode(EntityEditMode mode)
Entity focus (entrances/exits/items/sprites); NONE = all entities.
gui::Canvas properties_canvas_
std::unique_ptr< EntityMutationService > entity_mutation_service_
gfx::SnesPalette palette_
gfx::Bitmap tile16_blockset_bmp_
std::string graphics_preview_config_path_
bool map_tile_save_transaction_open_
void ClearSelectedItem()
Clear active item selection.
std::string lyra_loaded_rom_sha1_
void InitTilePaintingManager()
Initialize the tile painting manager (called after graphics load)
bool SelectItemByIdentity(const zelda3::OverworldItem &item_identity)
Select an overworld item using value identity matching.
Authoritative component for entity editing state and UI.
void OpenContextMenuFor(zelda3::GameEntity *entity)
void SetActiveEntity(zelda3::GameEntity *entity)
std::string GetDisplayString(const std::string &name) const
Live, platform-formatted binding for a shortcut name.
A session-aware status bar displayed at the bottom of the application.
void SetCustomSegment(const std::string &key, const std::string &value)
Set a custom segment with key-value pair.
void SetEditorMode(const std::string &mode)
Set the current editor mode or tool.
int current_tile16() const
absl::Status SetCurrentTile(int id)
void BindDocument(std::vector< gfx::Tile16 > *definitions, UndoManager *history, std::function< void()> before_edit={})
void SetOpenGraphicsForQuadrant(std::function< absl::Status(int, int)> callback)
void set_on_document_changed(std::function< void(const std::vector< Tile16Commit > &)> callback)
void RequestTileSwitch(int target_tile_id)
absl::Status Initialize(gfx::Bitmap &tile16_blockset_bmp, gfx::Bitmap ¤t_gfx_bmp, std::array< uint8_t, 0x200 > &all_tiles_types)
void set_palette(const gfx::SnesPalette &palette)
void set_on_current_tile_changed(std::function< void(int)> callback)
void Show(const std::string &message, ToastType type=ToastType::kInfo, float ttl_seconds=3.0f, std::string action_label={}, std::function< void()> on_action=nullptr)
void Push(std::unique_ptr< UndoAction > action)
absl::Status Redo()
Redo the top action. Returns error if stack is empty.
absl::Status Undo()
Undo the top action. Returns error if stack is empty.
size_t GetActiveSessionId() const
bool OpenWindow(size_t session_id, const std::string &base_window_id)
WindowContent * GetWindowContent(const std::string &window_id)
Get a WindowContent instance by ID.
bool ToggleWindow(size_t session_id, const std::string &base_window_id)
void RegisterPanelAlias(const std::string &legacy_base_id, const std::string &canonical_base_id)
Register a legacy panel ID alias that resolves to a canonical ID.
void QueueTextureCommand(TextureCommandType type, Bitmap *bitmap)
int RegisterPaletteListener(PaletteChangeCallback callback)
Register a callback for palette change notifications.
void UnregisterPaletteListener(int listener_id)
Unregister a palette change listener.
Represents a bitmap image optimized for SNES ROM hacking.
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
void SetPalette(const SnesPalette &palette)
Set the palette for the bitmap using SNES palette format.
SDL_Surface * surface() const
static PaletteManager & Get()
Get the singleton instance.
RAII timer for automatic timing management.
auto global_scale() const
void SetUsageMode(CanvasUsage usage)
void ApplyConfigSnapshot(const CanvasConfig &snapshot)
void ClearContextMenuItems()
CanvasConfig & GetConfig()
bool IsMouseHovering() const
Base class for all overworld and dungeon entities.
enum yaze::zelda3::GameEntity::EntityType entity_type_
Represents an overworld exit that transitions from dungeon to overworld.
absl::Status SaveMap32Expanded()
Save expanded tile32 definitions (v1+ ROMs)
void set_current_world(int world)
absl::Status Load(Rom *rom)
Load all overworld data from ROM.
void SyncAreaProperties(int map_index)
Copy the area parent's render settings (main palette, animated GFX, tile GFX groups,...
const std::vector< uint8_t > & current_graphics() const
absl::Status SaveMapProperties()
Save per-area graphics, palettes, and messages.
std::vector< std::pair< uint32_t, uint32_t > > GetProjectedWriteRanges() const
Get the projected write ranges (PC offsets) for overworld map saves.
absl::Status SaveMap32Tiles()
Save tile32 definitions to ROM.
const gfx::SnesPalette & current_area_palette() const
absl::Status SaveMap16Tiles()
Save tile16 definitions to ROM.
auto sprites(int state) const
const std::vector< gfx::Tile16 > & tiles16() const
bool is_lyra_preview() const
int inspected_map_count() const
absl::Status CreateTile32Tilemap()
Build tile32 tilemap from current tile16 data.
auto overworld_map(int i) const
const std::vector< uint8_t > & tile16_blockset_data() const
void SetGraphicsPreview(OverworldGraphicsPreview preview)
absl::Status SaveCustomOverworldData()
Save all v2/v3 property tables using the ROM's current feature flags.
void set_current_map(int i)
absl::StatusOr< OverworldSpriteSavePlan > PrepareSpriteSave() const
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.
void RollbackSavedMapTiles()
absl::Status SaveItems()
Save hidden overworld items to ROM.
void CommitSavedMapTiles()
absl::Status LoadLyraTerrainPreview(Rom *rom)
absl::Status SaveOverworldMaps()
Save compressed map tile data to ROM.
auto mutable_all_tiles_types()
auto mutable_sprites(int state)
void set_game_state(int state)
const std::vector< OverworldEntrance > & entrances() const
absl::Status SaveMap16Expanded()
Save expanded tile16 definitions (v1+ ROMs)
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)
absl::StatusOr< zelda3::OverworldGraphicsPreview > LoadOverworldGraphicsPreview(const std::string &filename)
bool ItemIdentityMatchesForUndo(const zelda3::OverworldItem &lhs, const zelda3::OverworldItem &rhs)
bool ItemSnapshotsEqual(const OverworldItemsSnapshot &lhs, const OverworldItemsSnapshot &rhs)
SheetPixels SnapshotSessionSheets(const std::set< uint16_t > &ids, const zelda3::GameData *game_data)
Editors are the view controllers for the application.
absl::Status RenameProjectResourceLabel(project::YazeProject *project, const std::string &type, int id, const std::string &label)
absl::Status ValidateHackManifestSaveConflicts(const core::HackManifest &manifest, project::RomWritePolicy write_policy, const std::vector< std::pair< uint32_t, uint32_t > > &ranges, absl::string_view save_scope, const char *log_tag, ToastManager *toast_manager)
absl::Status RemoveItemByIdentity(zelda3::Overworld *overworld, const zelda3::OverworldItem &item_identity)
Remove an item by value identity instead of pointer identity.
constexpr unsigned int kOverworldMapSize
absl::Status NudgeItem(zelda3::OverworldItem *item, int delta_x, int delta_y)
Move an item by pixel deltas with overworld bounds clamping.
absl::Status UpdateGraphicsContextPalettes(GraphicsEditContext &context, GraphicsEditContext refreshed)
zelda3::OverworldItem * FindNearestItemForSelection(zelda3::Overworld *overworld, const zelda3::OverworldItem &anchor_identity)
Find the best next item to keep selection continuity after deletion.
zelda3::OverworldItem * FindItemByIdentity(zelda3::Overworld *overworld, const zelda3::OverworldItem &item_identity)
Find a live item by value identity.
std::string DescribeOverworldPropertyEdit(const OverworldPropertyEdit &edit)
void AppendResolvedGraphicsContext(EditorContextSnapshot &snapshot, const zelda3::ResolvedGraphicsContext &context)
constexpr int kTile16Size
absl::StatusOr< GraphicsEditContext > MakeOverworldGraphicsEditContext(const zelda3::OverworldMap &map, const gfx::Tile16 &tile16, int tile16_id, int quadrant, const zelda3::GameData *owner)
OverworldMapMetadata BuildOverworldMapMetadata(const zelda3::Overworld &overworld, const Rom *rom, const project::YazeProject *project, int map_id, int game_state)
int EffectiveOverworldHoverMap(int hovered_map, bool canvas_hovered)
std::string FormatOverworldMapStatusSegment(int current_map, int hovered_map)
std::string GetProjectResourceLabel(const project::YazeProject *project, const std::string &type, int id)
absl::StatusOr< zelda3::OverworldItem * > DuplicateItemByIdentity(zelda3::Overworld *overworld, const zelda3::OverworldItem &item_identity, int offset_x, int offset_y)
Duplicate an existing item by identity with a positional offset.
Tilemap CreateTilemap(IRenderer *renderer, const std::vector< uint8_t > &data, int width, int height, int tile_size, int num_tiles, const SnesPalette &palette)
EmptyStateOptions EmptyLoading(const char *what, bool compact)
bool DrawEmptyState(const EmptyStateOptions &options)
Draw a centered empty-state block.
EmptyStateOptions EmptyNoRom(bool compact)
std::string ComputeSha1Hex(const uint8_t *data, size_t size)
Compute SHA-1 hash of data, return lowercase hex string (40 chars).
constexpr int kNumTile16Individual
constexpr int kNumOverworldMaps
absl::Status ApplyOverworldSpriteSave(Rom &rom, const OverworldSpriteSavePlan &plan)
constexpr int OverworldCustomASMHasBeenApplied
OverworldRomProfile DetectOverworldRomProfile(const Rom &rom)
absl::StatusOr< OverworldEntranceTileTypes > LoadEntranceTileTypes(Rom *rom)
ResolvedGraphicsContext DescribeOverworldGraphicsContext(const OverworldMap &map, const GraphicsContextIdentity &identity)
#define RETURN_IF_ERROR(expr)
struct yaze::core::FeatureFlags::Flags::Overworld overworld
Callbacks for operations that remain in the OverworldEditor.
std::function< void()> refresh_overworld_map
std::function< absl::Status()> refresh_tile16_blockset
std::function< void(int, bool)> select_map_for_editing
std::function< bool()> is_entity_hovered
Returns true if an entity is currently hovered (for pan suppression).
std::function< bool()> pick_tile16_from_hovered_canvas
std::function< void(int)> ensure_map_texture
std::function< void()> open_map_properties
Opens (and focuses) the Map Properties window. Double-click target.
Shared state pointers that the navigation manager reads/writes.
std::array< gfx::Bitmap, zelda3::kExpandedMapCount > * maps_bmp
std::unique_ptr< gui::TileSelectorWidget > * blockset_selector
gui::Canvas * ow_map_canvas
zelda3::Overworld * overworld
bool * is_dragging_entity
gfx::Tilemap * tile16_blockset
EditingMode * current_mode
std::vector< EditorContextValue > counts
std::string pending_label
std::vector< EditorContextAction > actions
std::vector< EditorContextDiagnostic > diagnostics
std::string semantic_owner
std::vector< EditorContextValue > metadata
std::vector< std::string > capabilities
project::YazeProject * project
ToastManager * toast_manager
SharedClipboard * shared_clipboard
gfx::IRenderer * renderer
ShortcutManager * shortcut_manager
WorkspaceWindowManager * window_manager
std::string error_message
zelda3::GameEntity * entity
Context struct holding all data dependencies for map refresh operations. All pointers/references must...
gfx::Tilemap * tile16_blockset
gfx::SnesPalette * palette
zelda3::Overworld * overworld
std::array< gfx::Bitmap, zelda3::kExpandedMapCount > * maps_bmp
std::function< void(int map_index)> ensure_map_texture
Callback to ensure a map texture is created (stays in OverworldEditor)
gfx::IRenderer * renderer
Tile16Editor * tile16_editor
gfx::Bitmap * current_gfx_bmp
bool * map_blockset_loaded
gfx::BitmapTable * current_graphics_set
std::function< void(int map_index)> refresh_map_on_demand
std::array< gfx::Bitmap, zelda3::kExpandedMapCount > * maps_bmp
gfx::IRenderer * renderer
zelda3::Overworld * overworld
Sink for high-level commands emitted by the interaction coordinator.
std::function< void(zelda3::GameEntity *) on_entity_double_click)
std::function< void()> on_pick_tile_from_hover
std::function< void(zelda3::GameEntity *) on_entity_context_menu)
std::function< bool()> can_edit_items
std::function< void()> on_duplicate_selected
std::function< void(int, int, bool)> on_nudge_selected
std::function< void()> on_undo
std::function< void(EditingMode)> on_set_editor_mode
std::function< void()> on_redo
std::function< void(EntityEditMode)> on_set_entity_mode
Snapshot of overworld item list + current item selection.
std::vector< zelda3::OverworldItem > items
std::optional< zelda3::OverworldItem > selected_item_identity
static constexpr const char * kMapProperties
static constexpr const char * kItemList
static constexpr const char * kTile16Editor
std::vector< int > overworld_tile16_ids
bool has_overworld_tile16
Callbacks for undo integration and map refresh.
std::function< void(int tile_id)> request_tile16_selection
When set, eyedropper routes here (guarded RequestTileSwitch path).
std::function< void(int map_index)> refresh_overworld_map_on_demand
std::function< void()> scroll_blockset_to_current_tile
std::function< void()> finalize_paint_operation
std::function< void(int map_id)> select_map_for_editing
std::function< void(int map_id, int world, int x, int y, int old_tile_id)> create_undo_point
Shared state for the tile painting system.
Tile16Editor * tile16_editor
std::array< gfx::Bitmap, zelda3::kExpandedMapCount > * maps_bmp
gui::Canvas * ow_map_canvas
zelda3::Overworld * overworld
std::vector< int > * selected_tile16_ids
gfx::Tilemap * tile16_blockset
EditingMode * current_mode
std::array< SDL_Color, 256 > colors
std::vector< uint8_t > pixels
Bitmap atlas
Master bitmap containing all tiles.
core::HackManifest hack_manifest
std::string overworld_graphics_preview
LyraLayoutPresence presence