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");
66 ImGui::TextDisabled(tr(
"Current map selection is invalid."));
68 tr(
"Hover or jump to a valid overworld map to continue."));
75 const int parent = selected->parent();
76 const int property_map =
86 DrawHeader(current_world, current_map, property_map, current_map_lock,
89 if (ImGui::CollapsingHeader(tr(
"Area"), ImGuiTreeNodeFlags_DefaultOpen)) {
92 if (ImGui::CollapsingHeader(tr(
"Graphics"),
93 ImGuiTreeNodeFlags_DefaultOpen)) {
96 if (ImGui::CollapsingHeader(tr(
"Palettes"),
97 ImGuiTreeNodeFlags_DefaultOpen)) {
100 if (ImGui::CollapsingHeader(tr(
"Music"))) {
103 if (ImGui::CollapsingHeader(tr(
"Effects"))) {
118 bool current_map_lock,
130 const int parent = selected->parent();
141 ImGui::PushID(
"OverworldAppearance");
144 ImGui::PushID(property_map);
146 ImGui::AlignTextToFramePadding();
147 ImGui::Text(
"Area %02X%s", property_map,
148 current_map_lock ?
" (pinned)" :
" (follow)");
149 if (ImGui::IsItemHovered()) {
151 "Appearance edits apply to area %02X and its shared screens.\n"
152 "Unpinned controls follow the cursor over the canvas.\n"
153 "Pin the map to keep editing this area while navigating.",
157 const float row_start = ImGui::GetCursorPosX();
158 const float right = row_start + ImGui::GetContentRegionAvail().x;
159 const float input_width =
160 ImGui::CalcTextSize(
"FF").x + ImGui::GetStyle().FramePadding.x * 2.0f;
161 bool first_field =
true;
162 const auto field = [&](
const char* label,
const char* tip,
164 const float width = ImGui::CalcTextSize(label).x + input_width +
165 ImGui::GetStyle().ItemInnerSpacing.x;
166 if (compact ? !first_field
167 : ImGui::GetItemRectMax().x - ImGui::GetWindowPos().x +
168 ImGui::GetScrollX() +
169 ImGui::GetStyle().ItemSpacing.x + width <=
175 ImGui::AlignTextToFramePadding();
176 ImGui::TextUnformatted(
180 ImGui::SameLine(0, ImGui::GetStyle().ItemInnerSpacing.x);
181 ImGui::PushID(label);
183 Apply(kind, 0, property_map, value);
186 std::string(
"OverworldToolbar/field:") + label,
"input",
190 if (ImGui::IsItemHovered()) {
192 "%s\nHexadecimal ID. Changes refresh the canvas and "
193 "Tile16 preview; undo restores the previous value.%s%s",
198 field(
"Graphics",
"Tile graphics group assigned to this area",
200 field(
"Palette",
"Area palette set assigned to this area",
205 field(
"Main palette",
"Extended main palette assigned to this area",
216 int property_map,
bool& current_map_lock,
219 current_map, game_state);
221 ImGui::TextWrapped(
ICON_MD_MAP " %s", metadata.map_title.c_str());
225 current_map_lock ?
"Pinned: properties stay on this map.\n"
226 "Click to follow the cursor again."
227 :
"Following the cursor.\n"
228 "Click to pin properties to this map.",
230 current_map_lock = !current_map_lock;
232 ImGui::SameLine(0, 2);
233 ImGui::AlignTextToFramePadding();
234 ImGui::TextDisabled(
"%s", current_map_lock ?
"Pinned" :
"Follow");
235 ImGui::SameLine(0, 6);
236 ImGui::BeginDisabled(
project_ ==
nullptr);
238 project_ ?
"Rename this map in the project"
239 :
"Open a project to name maps")) {
240 label_target_ = {
"Map name",
"overworld_map", current_map, 2};
243 ImGui::EndDisabled();
245 ImGui::AlignTextToFramePadding();
246 ImGui::TextDisabled(
"%s", metadata.area_size_label.c_str());
249 if (property_map != current_map) {
251 ImGui::TextDisabled(
"%s", metadata.parent_label.c_str());
254 ImGui::TextDisabled(
ICON_MD_INFO " %s", metadata.version_label.c_str());
255 if (ImGui::IsItemHovered()) {
257 "%s", tr(
"ROM version determines available overworld features.\n"
258 "v2+: custom BG colors, main palettes\n"
259 "v3+: wide/tall areas, custom tile GFX, animated GFX"));
265 if (!ImGui::BeginTable(
id, 2, ImGuiTableFlags_SizingFixedFit)) {
268 ImGui::TableSetupColumn(
"Label", ImGuiTableColumnFlags_WidthFixed,
270 ImGui::TableSetupColumn(
"Value", ImGuiTableColumnFlags_WidthStretch);
275 ImGui::TableNextRow();
276 ImGui::TableNextColumn();
277 ImGui::AlignTextToFramePadding();
278 ImGui::TextUnformatted(label);
279 if (tooltip && ImGui::IsItemHovered()) {
280 ImGui::SetTooltip(
"%s", tooltip);
282 ImGui::TableNextColumn();
286 int property_map,
int value) {
288 {property_map, field, index, value});
289 edit_error_ = status.ok() ?
"" : std::string(status.message());
295 int property_map, uint8_t value,
303 Apply(field, index, property_map, value);
306 if (ImGui::IsItemHovered()) {
310 "%s\n%s\n%s", tooltip ? tooltip : label,
311 project_label.empty()
312 ? tr(
"No project label")
313 : absl::StrFormat(
"Label: %s", project_label).c_str(),
314 tr(
"Right-click to rename the label"));
323 if (ImGui::BeginPopupContextItem(
"##LabelMenu")) {
324 ImGui::BeginDisabled(
project_ ==
nullptr);
329 ImGui::EndDisabled();
331 ImGui::TextDisabled(
"%s", tr(
"Open a project to store labels"));
341 const std::string current =
346 ImGui::OpenPopup(kLabelPopupId);
348 if (!ImGui::BeginPopup(kLabelPopupId)) {
353 ImGui::SetNextItemWidth(260.0f);
354 if (ImGui::IsWindowAppearing()) {
355 ImGui::SetKeyboardFocusHere();
357 const bool submitted = ImGui::InputText(
"##LabelInput",
label_buffer_.data(),
359 ImGuiInputTextFlags_EnterReturnsTrue);
360 auto apply = [&](
const std::string& label) {
368 ImGui::CloseCurrentPopup();
393 tr(
"Story state to preview. Sprite graphics and sprite "
394 "palettes below are stored per game state."));
395 ImGui::SetNextItemWidth(-FLT_MIN);
396 int selected_state = game_state;
397 if (ImGui::Combo(
"##GameState", &selected_state,
kGameStateNames, 3) &&
402 FieldLabel(tr(
"Area size"), tr(
"Screens this area spans. Wide and Tall need "
403 "ZSCustomOverworld v3."));
405 int area_size =
static_cast<int>(map->area_size());
406 ImGui::SetNextItemWidth(-FLT_MIN);
412 const char* limited_names[] = {
"Small (1x1)",
"Large (2x2)"};
413 int limited = (area_size == 0 || area_size == 1) ? area_size : 0;
414 if (ImGui::Combo(
"##AreaSize", &limited, limited_names, 2)) {
418 static_cast<int>(size));
422 const LabelTarget message_label{
"Message",
"message", map->message_id(), 4};
423 FieldLabel(tr(
"Message"), tr(
"Area name message shown on entry (hex)"));
424 ImGui::PushID(
"Message");
425 uint16_t message_id = map->message_id();
427 ImGui::GetContentRegionAvail().x,
true)) {
441 const int state = std::clamp(game_state, 0, 2);
443 const LabelTarget area_gfx{
"Area graphics",
"graphics", map->area_graphics()};
445 tr(
"Tile graphics group for this area (hex)"),
447 map->area_graphics(), &area_gfx);
449 const std::string sprite_tip = absl::StrFormat(
450 "Sprite graphics group while in \"%s\" (hex)",
kGameStateNames[state]);
451 const LabelTarget sprite_gfx{
"Sprite graphics",
"graphics",
452 map->sprite_graphics(state)};
453 HexByteField(
"SpriteGfx", tr(
"Sprite graphics"), sprite_tip.c_str(),
455 map->sprite_graphics(state), &sprite_gfx);
458 const LabelTarget animated{
"Animated graphics",
"graphics",
459 map->animated_gfx()};
461 tr(
"Animated tile sheet (water, flowers) (hex)"),
463 map->animated_gfx(), &animated);
468 ImGui::TreeNode(tr(
"Custom tile sheets"))) {
470 for (
int i = 0; i < 8; ++i) {
471 const std::string
id = absl::StrFormat(
"Sheet%d", i);
472 const std::string label = absl::StrFormat(
"Sheet %d", i);
473 const std::string tip =
474 absl::StrFormat(
"Custom graphics sheet slot %d (hex)", i);
477 map->custom_tileset(i));
486 bool& show_custom_bg_color_editor) {
492 const int state = std::clamp(game_state, 0, 2);
494 const LabelTarget area_palette{
"Area palette",
"overworld_area_palette",
495 map->area_palette()};
496 HexByteField(
"AreaPalette", tr(
"Area palette"), tr(
"Area palette set (hex)"),
498 map->area_palette(), &area_palette);
501 const LabelTarget main_palette{
"Main palette",
"overworld_main_palette",
502 map->main_palette()};
504 tr(
"Extended main palette (ZSCustomOverworld v2+) (hex)"),
506 map->main_palette(), &main_palette);
509 const std::string sprite_tip = absl::StrFormat(
511 const LabelTarget sprite_palette{
"Sprite palette",
"overworld_sprite_palette",
512 map->sprite_palette(state)};
513 HexByteField(
"SpritePalette", tr(
"Sprite palette"), sprite_tip.c_str(),
515 map->sprite_palette(state), &sprite_palette);
520 ImVec2(-FLT_MIN, 0))) {
521 show_custom_bg_color_editor =
true;
531 for (
int i = 0; i < 4; ++i) {
532 const std::string
id = absl::StrFormat(
"Music%d", i);
533 const LabelTarget music{kMusicLabels[i],
"overworld_music",
535 HexByteField(
id.c_str(), tr(kMusicLabels[i]), tr(kMusicTooltips[i]),
537 map->area_music(i), &music);
543 bool& show_overlay_editor) {
551 ImVec2(-FLT_MIN, 0))) {
552 show_overlay_editor =
true;
556 ImGui::TextUnformatted(tr(
"Mosaic transition"));
558 std::array<bool, 4> mosaic = map->mosaic_expanded();
559 const char* directions[] = {
"North",
"South",
"East",
"West"};
560 if (ImGui::BeginTable(
"MosaicTable", 2)) {
561 for (
int i = 0; i < 4; ++i) {
562 ImGui::TableNextColumn();
563 if (ImGui::Checkbox(tr(directions[i]), &mosaic[i])) {
571 bool mosaic = *map->mutable_mosaic();
572 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.
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.