9#include "absl/strings/str_format.h"
16#include "imgui/imgui.h"
30constexpr const char*
kMusicLabels[] = {
"Music: start",
"Music: Zelda saved",
31 "Music: Master Sword",
34 "Track before rescuing Zelda",
35 "Track after rescuing Zelda from Hyrule Castle",
36 "Track after drawing the Master Sword",
"Track after defeating Agahnim"};
39 return width <= 2 ? absl::StrFormat(
"0x%02X",
id)
40 : absl::StrFormat(
"0x%04X",
id);
47 : overworld_(overworld),
49 map_properties_system_(map_properties_system) {}
52 bool& current_map_lock,
int& game_state,
53 bool& show_custom_bg_color_editor,
54 bool& show_overlay_editor,
61 if (ImGui::BeginChild(
"OverworldSidebar", ImVec2(0, 0),
false,
62 ImGuiWindowFlags_None)) {
63 ImGui::PushID(
"OverworldSidebar");
69 ImGui::TextUnformatted(metadata.map_title.c_str());
71 ImGui::TextUnformatted(
"32 x 32 Tile16 cells");
72 ImGui::TextUnformatted(
"Loom Beach graphics and palette");
74 "Use the Tile16 selector to paint terrain across neighboring maps. "
75 "Save Project keeps these terrain edits.");
77 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.85f, 0.3f, 1.0f));
78 ImGui::Text(
ICON_MD_INFO " Tail Map Limitations ($A0-$BF)");
79 ImGui::PopStyleColor();
81 "• Items & Bomb Doors: Disabled (pending SRAM event allocation in "
83 "• Exits: 1 free slot (full links require ASM exit table v2)\n"
84 "• Entrances: Trigger table v2 in development (Phase P2)\n"
85 "• Sprites: Draft state (actor compilation needed for ROM)");
92 ImGui::TextDisabled(tr(
"Current map selection is invalid."));
94 tr(
"Hover or jump to a valid overworld map to continue."));
101 const int parent = selected->parent();
102 const int property_map =
112 DrawHeader(current_world, current_map, property_map, current_map_lock,
115 if (ImGui::CollapsingHeader(tr(
"Area"), ImGuiTreeNodeFlags_DefaultOpen)) {
118 if (ImGui::CollapsingHeader(tr(
"Graphics"),
119 ImGuiTreeNodeFlags_DefaultOpen)) {
122 if (ImGui::CollapsingHeader(tr(
"Palettes"),
123 ImGuiTreeNodeFlags_DefaultOpen)) {
126 if (ImGui::CollapsingHeader(tr(
"Music"))) {
129 if (ImGui::CollapsingHeader(tr(
"Effects"))) {
146 bool current_map_lock,
158 const int parent = selected->parent();
169 ImGui::PushID(
"OverworldAppearance");
172 ImGui::PushID(property_map);
174 ImGui::AlignTextToFramePadding();
175 ImGui::Text(
"Area %02X%s", property_map,
176 current_map_lock ?
" (pinned)" :
" (follow)");
177 if (ImGui::IsItemHovered()) {
179 "Appearance edits apply to area %02X and its shared screens.\n"
180 "Unpinned controls follow the cursor over the canvas.\n"
181 "Pin the map to keep editing this area while navigating.",
185 const float row_start = ImGui::GetCursorPosX();
186 const float right = row_start + ImGui::GetContentRegionAvail().x;
187 const float input_width =
188 ImGui::CalcTextSize(
"FF").x + ImGui::GetStyle().FramePadding.x * 2.0f;
189 bool first_field =
true;
190 const auto field = [&](
const char* label,
const char* tip,
192 const float width = ImGui::CalcTextSize(label).x + input_width +
193 ImGui::GetStyle().ItemInnerSpacing.x;
194 if (compact ? !first_field
195 : ImGui::GetItemRectMax().x - ImGui::GetWindowPos().x +
196 ImGui::GetScrollX() +
197 ImGui::GetStyle().ItemSpacing.x + width <=
203 ImGui::AlignTextToFramePadding();
204 ImGui::TextUnformatted(
208 ImGui::SameLine(0, ImGui::GetStyle().ItemInnerSpacing.x);
209 ImGui::PushID(label);
211 Apply(kind, 0, property_map, value);
214 std::string(
"OverworldToolbar/field:") + label,
"input",
218 if (ImGui::IsItemHovered()) {
220 "%s\nHexadecimal ID. Changes refresh the canvas and "
221 "Tile16 preview; undo restores the previous value.%s%s",
226 field(
"Graphics",
"Tile graphics group assigned to this area",
228 field(
"Palette",
"Area palette set assigned to this area",
233 field(
"Main palette",
"Extended main palette assigned to this area",
244 int property_map,
bool& current_map_lock,
247 current_map, game_state);
249 ImGui::TextWrapped(
ICON_MD_MAP " %s", metadata.map_title.c_str());
253 current_map_lock ?
"Pinned: properties stay on this map.\n"
254 "Click to follow the cursor again."
255 :
"Following the cursor.\n"
256 "Click to pin properties to this map.",
258 current_map_lock = !current_map_lock;
260 ImGui::SameLine(0, 2);
261 ImGui::AlignTextToFramePadding();
262 ImGui::TextDisabled(
"%s", current_map_lock ?
"Pinned" :
"Follow");
263 ImGui::SameLine(0, 6);
264 ImGui::BeginDisabled(
project_ ==
nullptr);
266 project_ ?
"Rename this map in the project"
267 :
"Open a project to name maps")) {
268 label_target_ = {
"Map name",
"overworld_map", current_map, 2};
271 ImGui::EndDisabled();
273 ImGui::AlignTextToFramePadding();
274 ImGui::TextDisabled(
"%s", metadata.area_size_label.c_str());
277 if (property_map != current_map) {
279 ImGui::TextDisabled(
"%s", metadata.parent_label.c_str());
282 ImGui::TextDisabled(
ICON_MD_INFO " %s", metadata.version_label.c_str());
283 if (ImGui::IsItemHovered()) {
285 "%s", tr(
"ROM version determines available overworld features.\n"
286 "v2+: custom BG colors, main palettes\n"
287 "v3+: wide/tall areas, custom tile GFX, animated GFX"));
293 if (!ImGui::BeginTable(
id, 2, ImGuiTableFlags_SizingFixedFit)) {
296 ImGui::TableSetupColumn(
"Label", ImGuiTableColumnFlags_WidthFixed,
298 ImGui::TableSetupColumn(
"Value", ImGuiTableColumnFlags_WidthStretch);
303 ImGui::TableNextRow();
304 ImGui::TableNextColumn();
305 ImGui::AlignTextToFramePadding();
306 ImGui::TextUnformatted(label);
307 if (tooltip && ImGui::IsItemHovered()) {
308 ImGui::SetTooltip(
"%s", tooltip);
310 ImGui::TableNextColumn();
314 int property_map,
int value) {
316 {property_map, field, index, value});
317 edit_error_ = status.ok() ?
"" : std::string(status.message());
323 int property_map, uint8_t value,
331 Apply(field, index, property_map, value);
334 if (ImGui::IsItemHovered()) {
338 "%s\n%s\n%s", tooltip ? tooltip : label,
339 project_label.empty()
340 ? tr(
"No project label")
341 : absl::StrFormat(
"Label: %s", project_label).c_str(),
342 tr(
"Right-click to rename the label"));
351 if (ImGui::BeginPopupContextItem(
"##LabelMenu")) {
352 ImGui::BeginDisabled(
project_ ==
nullptr);
357 ImGui::EndDisabled();
359 ImGui::TextDisabled(
"%s", tr(
"Open a project to store labels"));
369 const std::string current =
374 ImGui::OpenPopup(kLabelPopupId);
376 if (!ImGui::BeginPopup(kLabelPopupId)) {
381 ImGui::SetNextItemWidth(260.0f);
382 if (ImGui::IsWindowAppearing()) {
383 ImGui::SetKeyboardFocusHere();
385 const bool submitted = ImGui::InputText(
"##LabelInput",
label_buffer_.data(),
387 ImGuiInputTextFlags_EnterReturnsTrue);
388 auto apply = [&](
const std::string& label) {
396 ImGui::CloseCurrentPopup();
421 tr(
"Story state to preview. Sprite graphics and sprite "
422 "palettes below are stored per game state."));
423 ImGui::SetNextItemWidth(-FLT_MIN);
424 int selected_state = game_state;
425 if (ImGui::Combo(
"##GameState", &selected_state,
kGameStateNames, 3) &&
430 FieldLabel(tr(
"Area size"), tr(
"Screens this area spans. Wide and Tall need "
431 "ZSCustomOverworld v3."));
433 int area_size =
static_cast<int>(map->area_size());
434 ImGui::SetNextItemWidth(-FLT_MIN);
440 const char* limited_names[] = {
"Small (1x1)",
"Large (2x2)"};
441 int limited = (area_size == 0 || area_size == 1) ? area_size : 0;
442 if (ImGui::Combo(
"##AreaSize", &limited, limited_names, 2)) {
446 static_cast<int>(size));
450 const LabelTarget message_label{
"Message",
"message", map->message_id(), 4};
451 FieldLabel(tr(
"Message"), tr(
"Area name message shown on entry (hex)"));
452 ImGui::PushID(
"Message");
453 uint16_t message_id = map->message_id();
455 ImGui::GetContentRegionAvail().x,
true)) {
469 const int state = std::clamp(game_state, 0, 2);
471 const LabelTarget area_gfx{
"Area graphics",
"graphics", map->area_graphics()};
473 tr(
"Tile graphics group for this area (hex)"),
475 map->area_graphics(), &area_gfx);
477 const std::string sprite_tip = absl::StrFormat(
478 "Sprite graphics group while in \"%s\" (hex)",
kGameStateNames[state]);
479 const LabelTarget sprite_gfx{
"Sprite graphics",
"graphics",
480 map->sprite_graphics(state)};
481 HexByteField(
"SpriteGfx", tr(
"Sprite graphics"), sprite_tip.c_str(),
483 map->sprite_graphics(state), &sprite_gfx);
486 const LabelTarget animated{
"Animated graphics",
"graphics",
487 map->animated_gfx()};
489 tr(
"Animated tile sheet (water, flowers) (hex)"),
491 map->animated_gfx(), &animated);
496 ImGui::TreeNode(tr(
"Custom tile sheets"))) {
498 for (
int i = 0; i < 8; ++i) {
499 const std::string
id = absl::StrFormat(
"Sheet%d", i);
500 const std::string label = absl::StrFormat(
"Sheet %d", i);
501 const std::string tip =
502 absl::StrFormat(
"Custom graphics sheet slot %d (hex)", i);
505 map->custom_tileset(i));
514 bool& show_custom_bg_color_editor) {
520 const int state = std::clamp(game_state, 0, 2);
522 const LabelTarget area_palette{
"Area palette",
"overworld_area_palette",
523 map->area_palette()};
524 HexByteField(
"AreaPalette", tr(
"Area palette"), tr(
"Area palette set (hex)"),
526 map->area_palette(), &area_palette);
529 const LabelTarget main_palette{
"Main palette",
"overworld_main_palette",
530 map->main_palette()};
532 tr(
"Extended main palette (ZSCustomOverworld v2+) (hex)"),
534 map->main_palette(), &main_palette);
537 const std::string sprite_tip = absl::StrFormat(
539 const LabelTarget sprite_palette{
"Sprite palette",
"overworld_sprite_palette",
540 map->sprite_palette(state)};
541 HexByteField(
"SpritePalette", tr(
"Sprite palette"), sprite_tip.c_str(),
543 map->sprite_palette(state), &sprite_palette);
548 ImVec2(-FLT_MIN, 0))) {
549 show_custom_bg_color_editor =
true;
559 for (
int i = 0; i < 4; ++i) {
560 const std::string
id = absl::StrFormat(
"Music%d", i);
561 const LabelTarget music{kMusicLabels[i],
"overworld_music",
563 HexByteField(
id.c_str(), tr(kMusicLabels[i]), tr(kMusicTooltips[i]),
565 map->area_music(i), &music);
571 bool& show_overlay_editor) {
579 ImVec2(-FLT_MIN, 0))) {
580 show_overlay_editor =
true;
584 ImGui::TextUnformatted(tr(
"Mosaic transition"));
586 std::array<bool, 4> mosaic = map->mosaic_expanded();
587 const char* directions[] = {
"North",
"South",
"East",
"West"};
588 if (ImGui::BeginTable(
"MosaicTable", 2)) {
589 for (
int i = 0; i < 4; ++i) {
590 ImGui::TableNextColumn();
591 if (ImGui::Checkbox(tr(directions[i]), &mosaic[i])) {
599 bool mosaic = *map->mutable_mosaic();
600 if (ImGui::Checkbox(tr(
"Mosaic effect"), &mosaic)) {
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
absl::Status ApplyPropertyEdit(const OverworldPropertyEdit &edit)
static bool SupportsCustomBGColors(OverworldVersion version)
Check if ROM supports custom background colors per area (v2+)
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)
static bool SupportsAnimatedGFX(OverworldVersion version)
Check if ROM supports animated GFX selection (v3+)
static bool SupportsExpandedSpace(OverworldVersion version)
Check if ROM uses expanded ROM space for overworld data.
bool is_lyra_preview() const
auto overworld_map(int i) const
auto mutable_overworld_map(int i)
#define ICON_MD_GPS_FIXED
#define ICON_MD_FORMAT_COLOR_FILL
absl::Status RenameProjectResourceLabel(project::YazeProject *project, const std::string &type, int id, const std::string &label)
constexpr const char * kAreaSizeNames[]
OverworldMapMetadata BuildOverworldMapMetadata(const zelda3::Overworld &overworld, const Rom *rom, const project::YazeProject *project, int map_id, int game_state)
std::string GetProjectResourceLabel(const project::YazeProject *project, const std::string &type, int id)
constexpr const char * kGameStateNames[]
bool InputHexWord(const char *label, uint16_t *data, float input_width, bool no_step)
bool ToolbarIconButton(const char *icon, const char *tooltip, bool is_active)
Convenience wrapper for toolbar-sized icon buttons.
bool InputHexByte(const char *label, uint8_t *data, float input_width, bool no_step)
constexpr int kNumOverworldMaps
@ kVanilla
0xFF in ROM, no ZScream ASM applied
Modern project structure with comprehensive settings consolidation.
std::string lyra_terrain_file