yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
map_properties.cc
Go to the documentation of this file.
2
13#include "imgui/imgui.h"
16
17namespace yaze {
18namespace editor {
19
20using ImGui::BeginTable;
21// HOVER_HINT is defined in util/macro.h
22using ImGui::Separator;
23using ImGui::TableNextColumn;
24using ImGui::Text;
25
26// Using centralized UI constants
27
29 int& current_world, int& current_map, bool& current_map_lock,
30 bool& show_map_properties_panel, bool& show_custom_bg_color_editor,
31 bool& show_overlay_editor, bool& show_overlay_preview, int& game_state,
32 EditingMode& current_mode, EntityEditMode& entity_edit_mode) {
33 (void)show_overlay_editor; // Reserved for future use
34 (void)show_custom_bg_color_editor; // Now handled by sidebar
35 (void)game_state; // Now handled by sidebar
36 (void)show_overlay_preview; // Reserved
37
38 // Simplified canvas toolbar - Navigation and Mode controls
39 if (BeginTable("CanvasToolbar", 7,
40 ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit,
41 ImVec2(0, 0), -1)) {
42 ImGui::TableSetupColumn("World", ImGuiTableColumnFlags_WidthFixed,
44 ImGui::TableSetupColumn("Map", ImGuiTableColumnFlags_WidthFixed,
46 ImGui::TableSetupColumn("Area Size", ImGuiTableColumnFlags_WidthFixed,
48 ImGui::TableSetupColumn("Lock", ImGuiTableColumnFlags_WidthFixed,
50 ImGui::TableSetupColumn("Mode", ImGuiTableColumnFlags_WidthFixed,
51 80.0f); // Mouse/Paint
52 ImGui::TableSetupColumn("Entity", ImGuiTableColumnFlags_WidthStretch); // Entity status
53 ImGui::TableSetupColumn("Sidebar", ImGuiTableColumnFlags_WidthFixed, 40.0f);
54
55 TableNextColumn();
56 ImGui::SetNextItemWidth(kComboWorldWidth);
57 ImGui::Combo("##world", &current_world, kWorldNames, 3);
58
59 TableNextColumn();
60 ImGui::Text("%d (0x%02X)", current_map, current_map);
61
62 TableNextColumn();
63 // Use centralized version detection
65
66 // ALL ROMs support Small/Large. Only v3+ supports Wide/Tall.
67 int current_area_size =
68 static_cast<int>(overworld_->overworld_map(current_map)->area_size());
69 ImGui::SetNextItemWidth(kComboAreaSizeWidth);
70
72 // v3+ ROM: Show all 4 area size options
73 if (ImGui::Combo("##AreaSize", &current_area_size, kAreaSizeNames, 4)) {
74 auto status = overworld_->ConfigureMultiAreaMap(
75 current_map, static_cast<zelda3::AreaSizeEnum>(current_area_size));
76 if (status.ok()) {
77 RefreshSiblingMapGraphics(current_map, true);
79 }
80 }
81 } else {
82 // Vanilla/v1/v2 ROM: Show only Small/Large (first 2 options)
83 const char* limited_names[] = {"Small (1x1)", "Large (2x2)"};
84 int limited_size = (current_area_size == 0 || current_area_size == 1)
85 ? current_area_size
86 : 0;
87
88 if (ImGui::Combo("##AreaSize", &limited_size, limited_names, 2)) {
89 // limited_size is 0 (Small) or 1 (Large)
90 auto size = (limited_size == 1) ? zelda3::AreaSizeEnum::LargeArea
92 auto status = overworld_->ConfigureMultiAreaMap(current_map, size);
93 if (status.ok()) {
94 RefreshSiblingMapGraphics(current_map, true);
96 }
97 }
98
99 if (rom_version == zelda3::OverworldVersion::kVanilla ||
101 HOVER_HINT("Small (1x1) and Large (2x2) maps. Wide/Tall require v3+");
102 }
103 }
104
105 TableNextColumn();
106 if (ImGui::Button(current_map_lock ? ICON_MD_LOCK : ICON_MD_LOCK_OPEN,
107 ImVec2(40, 0))) {
108 current_map_lock = !current_map_lock;
109 }
110 HOVER_HINT(current_map_lock ? "Unlock Map" : "Lock Map");
111
112 TableNextColumn();
113 // Mode Controls
114 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
115 if (gui::ToggleButton(ICON_MD_MOUSE, current_mode == EditingMode::MOUSE, ImVec2(30, 0))) {
116 current_mode = EditingMode::MOUSE;
118 }
119 HOVER_HINT("Mouse Mode (1)\nNavigate, pan, and manage entities");
120
121 ImGui::SameLine();
122 if (gui::ToggleButton(ICON_MD_DRAW, current_mode == EditingMode::DRAW_TILE, ImVec2(30, 0))) {
123 current_mode = EditingMode::DRAW_TILE;
125 }
126 HOVER_HINT("Tile Paint Mode (2)\nDraw tiles on the map");
127 ImGui::PopStyleVar();
128
129 TableNextColumn();
130 // Entity Status
131 if (entity_edit_mode != EntityEditMode::NONE) {
132 const char* entity_icon = "";
133 const char* entity_label = "";
134 switch (entity_edit_mode) {
135 case EntityEditMode::ENTRANCES: entity_icon = ICON_MD_DOOR_FRONT; entity_label = "Entrances"; break;
136 case EntityEditMode::EXITS: entity_icon = ICON_MD_DOOR_BACK; entity_label = "Exits"; break;
137 case EntityEditMode::ITEMS: entity_icon = ICON_MD_GRASS; entity_label = "Items"; break;
138 case EntityEditMode::SPRITES: entity_icon = ICON_MD_PEST_CONTROL_RODENT; entity_label = "Sprites"; break;
139 case EntityEditMode::TRANSPORTS: entity_icon = ICON_MD_ADD_LOCATION; entity_label = "Transports"; break;
140 case EntityEditMode::MUSIC: entity_icon = ICON_MD_MUSIC_NOTE; entity_label = "Music"; break;
141 default: break;
142 }
143 ImGui::TextColored(ImVec4(0.4f, 0.8f, 1.0f, 1.0f), "%s %s", entity_icon, entity_label);
144 }
145
146 TableNextColumn();
147 // Sidebar Toggle
148 if (ImGui::Button(ICON_MD_TUNE, ImVec2(40, 0))) {
149 show_map_properties_panel = !show_map_properties_panel;
150 }
151 HOVER_HINT("Toggle Map Properties Sidebar");
152
153 ImGui::EndTable();
154 }
155}
156
158 int current_map, bool& show_map_properties_panel) {
159 (void)show_map_properties_panel; // Used by caller for window state
160 if (!overworld_->is_loaded()) {
161 Text("No overworld loaded");
162 return;
163 }
164
165 // Header with map info and lock status
166 ImGui::BeginGroup();
167 Text("Current Map: %d (0x%02X)", current_map, current_map);
168 ImGui::EndGroup();
169
170 Separator();
171
172 // Create tabs for different property categories
173 if (ImGui::BeginTabBar("MapPropertiesTabs",
174 ImGuiTabBarFlags_FittingPolicyScroll)) {
175 // Basic Properties Tab
176 if (ImGui::BeginTabItem("Basic Properties")) {
177 DrawBasicPropertiesTab(current_map);
178 ImGui::EndTabItem();
179 }
180
181 // Sprite Properties Tab
182 if (ImGui::BeginTabItem("Sprite Properties")) {
183 DrawSpritePropertiesTab(current_map);
184 ImGui::EndTabItem();
185 }
186
187 // Custom Overworld Features Tab
189 if (rom_version != zelda3::OverworldVersion::kVanilla &&
190 ImGui::BeginTabItem("Custom Features")) {
191 DrawCustomFeaturesTab(current_map);
192 ImGui::EndTabItem();
193 }
194
195 // Tile Graphics Tab
196 if (ImGui::BeginTabItem("Tile Graphics")) {
197 DrawTileGraphicsTab(current_map);
198 ImGui::EndTabItem();
199 }
200
201 // Music Tab
202 if (ImGui::BeginTabItem("Music")) {
203 DrawMusicTab(current_map);
204 ImGui::EndTabItem();
205 }
206
207 ImGui::EndTabBar();
208 }
209}
210
212 int current_map, bool& show_custom_bg_color_editor) {
213 (void)show_custom_bg_color_editor; // Used by caller for window state
214 if (!overworld_->is_loaded()) {
215 Text("No overworld loaded");
216 return;
217 }
218
221 Text("Custom background colors require ZSCustomOverworld v2+");
222 return;
223 }
224
225 Text("Custom Background Color Editor");
226 Separator();
227
228 // Read enable flag from ROM (not static - must reflect current ROM state)
229 bool use_area_specific_bg_color =
231 if (ImGui::Checkbox("Use Area-Specific Background Color",
232 &use_area_specific_bg_color)) {
233 // Update ROM data when checkbox is toggled
235 use_area_specific_bg_color ? 0x01 : 0x00;
236 }
237
238 if (use_area_specific_bg_color) {
239 // Get current color
240 uint16_t current_color =
241 overworld_->overworld_map(current_map)->area_specific_bg_color();
242 gfx::SnesColor snes_color(current_color);
243
244 // Convert to ImVec4 for color picker
245 ImVec4 color_vec = gui::ConvertSnesColorToImVec4(snes_color);
246
247 if (ImGui::ColorPicker4(
248 "Background Color", (float*)&color_vec,
249 ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHex)) {
250 // Convert back to SNES color and update
251 gfx::SnesColor new_snes_color = gui::ConvertImVec4ToSnesColor(color_vec);
253 ->set_area_specific_bg_color(new_snes_color.snes());
254
255 // Update ROM
256 int rom_address =
258 (*rom_)[rom_address] = new_snes_color.snes() & 0xFF;
259 (*rom_)[rom_address + 1] = (new_snes_color.snes() >> 8) & 0xFF;
260 }
261
262 Text("SNES Color: 0x%04X", current_color);
263 }
264}
265
267 bool& show_overlay_editor) {
268 (void)show_overlay_editor; // Used by caller for window state
269 if (!overworld_->is_loaded()) {
270 Text("No overworld loaded");
271 return;
272 }
273
275
276 ImGui::TextColored(ImVec4(0.4f, 0.8f, 1.0f, 1.0f),
277 ICON_MD_LAYERS " Visual Effects Configuration");
278 ImGui::Text("Map: 0x%02X", current_map);
279 Separator();
280
281 if (rom_version == zelda3::OverworldVersion::kVanilla) {
282 ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.4f, 1.0f), ICON_MD_INFO
283 " Enhanced overlay editing requires ZSCustomOverworld v1+");
284 ImGui::Separator();
285 ImGui::TextWrapped(
286 "Subscreen overlays are a vanilla feature used for atmospheric effects "
287 "like fog, rain, and forest canopy. ZSCustomOverworld expands this by "
288 "allowing per-area overlay configuration and additional customization.");
289 return;
290 }
291
292 // Help section
293 if (ImGui::CollapsingHeader(ICON_MD_HELP_OUTLINE " What are Visual Effects?",
294 ImGuiTreeNodeFlags_DefaultOpen)) {
295 ImGui::Indent();
296 ImGui::TextWrapped(
297 "Visual effects (subscreen overlays) are semi-transparent layers drawn "
298 "on top of or behind your map. They reference special area maps "
299 "(0x80-0x9F) "
300 "for their tile16 graphics data.");
301 ImGui::Spacing();
302 ImGui::Text("Common uses:");
303 ImGui::BulletText("Fog effects (Lost Woods, Skull Woods)");
304 ImGui::BulletText("Rain (Misery Mire)");
305 ImGui::BulletText("Forest canopy (Lost Woods)");
306 ImGui::BulletText("Sky backgrounds (Death Mountain)");
307 ImGui::BulletText("Under bridge views");
308 ImGui::Unindent();
309 ImGui::Separator();
310 }
311
312 // Read enable flag from ROM (not static - must reflect current ROM state)
313 bool use_subscreen_overlay =
315 if (ImGui::Checkbox(ICON_MD_VISIBILITY " Enable Visual Effect for This Area",
316 &use_subscreen_overlay)) {
317 // Update ROM data when checkbox is toggled
319 use_subscreen_overlay ? 0x01 : 0x00;
320 }
321 if (ImGui::IsItemHovered()) {
322 ImGui::SetTooltip("Enable/disable visual effect overlay for this map area");
323 }
324
325 if (use_subscreen_overlay) {
326 ImGui::Spacing();
327 uint16_t current_overlay =
328 overworld_->overworld_map(current_map)->subscreen_overlay();
329 if (gui::InputHexWord(ICON_MD_PHOTO " Visual Effect Map ID",
330 &current_overlay, kInputFieldSize + 30)) {
332 ->set_subscreen_overlay(current_overlay);
333
334 // Update ROM
335 int rom_address =
337 (*rom_)[rom_address] = current_overlay & 0xFF;
338 (*rom_)[rom_address + 1] = (current_overlay >> 8) & 0xFF;
339 }
340 if (ImGui::IsItemHovered()) {
341 ImGui::SetTooltip(
342 "ID of the special area map (0x80-0x9F) to use for\n"
343 "visual effects. That map's tile16 data will be drawn\n"
344 "as a semi-transparent layer on this area.");
345 }
346
347 // Show description
348 std::string overlay_desc = GetOverlayDescription(current_overlay);
349 ImGui::TextColored(ImVec4(0.4f, 1.0f, 0.4f, 1.0f), ICON_MD_INFO " %s",
350 overlay_desc.c_str());
351
352 ImGui::Separator();
353 if (ImGui::CollapsingHeader(ICON_MD_LIGHTBULB
354 " Common Visual Effect IDs")) {
355 ImGui::Indent();
356 ImGui::BulletText("0x0093 - Triforce Room Curtain");
357 ImGui::BulletText("0x0094 - Under the Bridge");
358 ImGui::BulletText("0x0095 - Sky Background (LW Death Mountain)");
359 ImGui::BulletText("0x0096 - Pyramid Background");
360 ImGui::BulletText("0x0097 - Fog Overlay (Master Sword Area)");
361 ImGui::BulletText("0x009C - Lava Background (DW Death Mountain)");
362 ImGui::BulletText("0x009D - Fog Overlay (Lost/Skull Woods)");
363 ImGui::BulletText("0x009E - Tree Canopy (Forest)");
364 ImGui::BulletText("0x009F - Rain Effect (Misery Mire)");
365 ImGui::BulletText("0x00FF - No Overlay (Disabled)");
366 ImGui::Unindent();
367 }
368 } else {
369 ImGui::Spacing();
370 ImGui::TextColored(ImVec4(0.6f, 0.6f, 0.6f, 1.0f), ICON_MD_BLOCK
371 " No visual effects enabled for this area");
372 }
373}
374
376 gui::Canvas& canvas, int current_map, bool current_map_lock,
377 bool& show_map_properties_panel, bool& show_custom_bg_color_editor,
378 bool& show_overlay_editor, int current_mode) {
379 (void)current_map; // Used for future context-sensitive menu items
380 // Clear any existing context menu items
381 canvas.ClearContextMenuItems();
382
383 // Add entity insertion submenu (only in MOUSE mode)
384 if (current_mode == 0 && entity_insert_callback_) { // 0 = EditingMode::MOUSE
385 gui::CanvasMenuItem entity_menu;
386 entity_menu.label = ICON_MD_ADD_LOCATION " Insert Entity";
387
388 // Entrance submenu item
389 gui::CanvasMenuItem entrance_item;
390 entrance_item.label = ICON_MD_DOOR_FRONT " Entrance";
391 entrance_item.callback = [this]() {
393 entity_insert_callback_("entrance");
394 }
395 };
396 entity_menu.subitems.push_back(entrance_item);
397
398 // Hole submenu item
399 gui::CanvasMenuItem hole_item;
400 hole_item.label = ICON_MD_CYCLONE " Hole";
401 hole_item.callback = [this]() {
404 }
405 };
406 entity_menu.subitems.push_back(hole_item);
407
408 // Exit submenu item
409 gui::CanvasMenuItem exit_item;
410 exit_item.label = ICON_MD_DOOR_BACK " Exit";
411 exit_item.callback = [this]() {
414 }
415 };
416 entity_menu.subitems.push_back(exit_item);
417
418 // Item submenu item
419 gui::CanvasMenuItem item_item;
420 item_item.label = ICON_MD_GRASS " Item";
421 item_item.callback = [this]() {
424 }
425 };
426 entity_menu.subitems.push_back(item_item);
427
428 // Sprite submenu item
429 gui::CanvasMenuItem sprite_item;
430 sprite_item.label = ICON_MD_PEST_CONTROL_RODENT " Sprite";
431 sprite_item.callback = [this]() {
433 entity_insert_callback_("sprite");
434 }
435 };
436 entity_menu.subitems.push_back(sprite_item);
437
438 canvas.AddContextMenuItem(entity_menu);
439
440 // Add "Edit Tile16" option in MOUSE mode
442 gui::CanvasMenuItem tile16_edit_item;
443 tile16_edit_item.label = ICON_MD_GRID_VIEW " Edit Tile16";
444 tile16_edit_item.callback = [this]() {
447 }
448 };
449 canvas.AddContextMenuItem(tile16_edit_item);
450 }
451 }
452
453 // Add overworld-specific context menu items
454 gui::CanvasMenuItem lock_item;
455 lock_item.label = current_map_lock ? "Unlock Map" : "Lock to This Map";
456 lock_item.callback = [&current_map_lock]() {
457 current_map_lock = !current_map_lock;
458 };
459 canvas.AddContextMenuItem(lock_item);
460
461 // Area Configuration
462 gui::CanvasMenuItem properties_item;
463 properties_item.label = ICON_MD_TUNE " Area Configuration";
464 properties_item.callback = [&show_map_properties_panel]() {
465 show_map_properties_panel = true;
466 };
467 canvas.AddContextMenuItem(properties_item);
468
469 // Custom overworld features (only show if v3+)
472 // Custom Background Color
473 gui::CanvasMenuItem bg_color_item;
474 bg_color_item.label = ICON_MD_FORMAT_COLOR_FILL " Custom Background Color";
475 bg_color_item.callback = [&show_custom_bg_color_editor]() {
476 show_custom_bg_color_editor = true;
477 };
478 canvas.AddContextMenuItem(bg_color_item);
479
480 // Visual Effects Editor
481 gui::CanvasMenuItem overlay_item;
482 overlay_item.label = ICON_MD_LAYERS " Visual Effects";
483 overlay_item.callback = [&show_overlay_editor]() {
484 show_overlay_editor = true;
485 };
486 canvas.AddContextMenuItem(overlay_item);
487 }
488
489 // Canvas controls
490 gui::CanvasMenuItem reset_view_item;
491 reset_view_item.label = ICON_MD_RESTORE " Reset View";
492 reset_view_item.callback = [&canvas]() {
493 canvas.set_global_scale(1.0f);
494 canvas.set_scrolling(ImVec2(0, 0));
495 };
496 canvas.AddContextMenuItem(reset_view_item);
497
498 gui::CanvasMenuItem zoom_in_item;
499 zoom_in_item.label = ICON_MD_ZOOM_IN " Zoom In";
500 zoom_in_item.callback = [&canvas]() {
501 float scale = std::min(kOverworldMaxZoom,
503 canvas.set_global_scale(scale);
504 };
505 canvas.AddContextMenuItem(zoom_in_item);
506
507 gui::CanvasMenuItem zoom_out_item;
508 zoom_out_item.label = ICON_MD_ZOOM_OUT " Zoom Out";
509 zoom_out_item.callback = [&canvas]() {
510 float scale = std::max(kOverworldMinZoom,
512 canvas.set_global_scale(scale);
513 };
514 canvas.AddContextMenuItem(zoom_out_item);
515}
516
517// Private method implementations
518void MapPropertiesSystem::DrawGraphicsPopup(int current_map, int game_state) {
519 if (ImGui::BeginPopup(
522 .c_str())) {
523 ImGui::PushID("GraphicsPopup"); // Fix ImGui duplicate ID warnings
524
525 // Use theme-aware spacing instead of hardcoded constants
527 float padding = gui::LayoutHelpers::GetButtonPadding();
528 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(spacing, spacing));
529 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(padding, padding));
530
531 ImGui::Text("Graphics Settings");
532 ImGui::Separator();
533
534 // Area Graphics
535 if (gui::InputHexByte(ICON_MD_IMAGE " Area Graphics",
537 ->mutable_area_graphics(),
539 // CORRECT ORDER: Properties first, then graphics reload
540
541 // 1. Propagate properties to siblings FIRST (calls LoadAreaGraphics on
542 // siblings)
544
545 // 2. Force immediate refresh of current map
546 (*maps_bmp_)[current_map].set_modified(true);
547 overworld_->mutable_overworld_map(current_map)->LoadAreaGraphics();
548
549 // 3. Refresh siblings immediately
550 RefreshSiblingMapGraphics(current_map);
551
552 // 4. Update tile selector
554
555 // 5. Final refresh
557 }
558 HOVER_HINT("Main tileset graphics for this map area");
559
560 // Sprite Graphics
561 if (gui::InputHexByte(absl::StrFormat(ICON_MD_PETS " Sprite GFX (%s)",
562 kGameStateNames[game_state])
563 .c_str(),
565 ->mutable_sprite_graphics(game_state),
567 ForceRefreshGraphics(current_map);
570 }
571 HOVER_HINT("Sprite graphics sheet for current game state");
572
573 auto rom_version_gfx = zelda3::OverworldVersionHelper::GetVersion(*rom_);
575 if (gui::InputHexByte(ICON_MD_ANIMATION " Animated GFX",
577 ->mutable_animated_gfx(),
579 ForceRefreshGraphics(current_map);
583 }
584 HOVER_HINT("Animated tile graphics (water, lava, etc.)");
585 }
586
587 // Custom Tile Graphics - Only available for v1+ ROMs
589 rom_version_gfx)) {
590 ImGui::Separator();
591 ImGui::Text(ICON_MD_GRID_VIEW " Custom Tile Graphics");
592 ImGui::Separator();
593
594 // Show the 8 custom graphics IDs in a 2-column layout for density
595 if (BeginTable("CustomTileGraphics", 2, ImGuiTableFlags_SizingFixedFit)) {
596 for (int i = 0; i < 8; i++) {
597 TableNextColumn();
598 std::string label = absl::StrFormat(ICON_MD_LAYERS " Sheet %d", i);
599 if (gui::InputHexByte(label.c_str(),
601 ->mutable_custom_tileset(i),
602 90.f)) {
603 ForceRefreshGraphics(current_map);
607 }
608 if (ImGui::IsItemHovered()) {
609 ImGui::SetTooltip("Custom graphics sheet %d (0x00-0xFF)", i);
610 }
611 }
612 ImGui::EndTable();
613 }
614 } else if (rom_version_gfx == zelda3::OverworldVersion::kVanilla) {
615 ImGui::Separator();
616 ImGui::TextColored(ImVec4(0.6f, 0.6f, 0.6f, 1.0f),
617 ICON_MD_INFO " Custom Tile Graphics");
618 ImGui::TextWrapped(
619 "Custom tile graphics require ZSCustomOverworld v1+.\n"
620 "Upgrade your ROM to access 8 customizable graphics sheets.");
621 }
622
623 ImGui::PopStyleVar(2); // Pop the 2 style variables we pushed
624 ImGui::PopID(); // Pop GraphicsPopup ID scope
625 ImGui::EndPopup();
626 }
627}
628
629void MapPropertiesSystem::DrawPalettesPopup(int current_map, int game_state,
630 bool& show_custom_bg_color_editor) {
631 if (ImGui::BeginPopup(
634 .c_str())) {
635 ImGui::PushID("PalettesPopup"); // Fix ImGui duplicate ID warnings
636
637 // Use theme-aware spacing instead of hardcoded constants
639 float padding = gui::LayoutHelpers::GetButtonPadding();
640 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(spacing, spacing));
641 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(padding, padding));
642
643 ImGui::Text("Palette Settings");
644 ImGui::Separator();
645
646 // Area Palette
647 if (gui::InputHexByte(ICON_MD_PALETTE " Area Palette",
649 ->mutable_area_palette(),
652 auto status = RefreshMapPalette();
654 }
655 HOVER_HINT("Main color palette for background tiles");
656
657 // Read fresh to reflect ROM upgrades
658 auto rom_version_pal = zelda3::OverworldVersionHelper::GetVersion(*rom_);
660 rom_version_pal)) {
661 if (gui::InputHexByte(ICON_MD_COLOR_LENS " Main Palette",
663 ->mutable_main_palette(),
666 auto status = RefreshMapPalette();
668 }
669 HOVER_HINT("Extended main palette (ZSCustomOverworld v2+)");
670 }
671
672 // Sprite Palette
673 if (gui::InputHexByte(absl::StrFormat(ICON_MD_COLORIZE " Sprite Pal (%s)",
674 kGameStateNames[game_state])
675 .c_str(),
677 ->mutable_sprite_palette(game_state),
681 }
682 HOVER_HINT("Color palette for sprites in current game state");
683
684 ImGui::Separator();
685 if (ImGui::Button(ICON_MD_FORMAT_COLOR_FILL " Custom Background Color",
686 ImVec2(-1, 0))) {
687 show_custom_bg_color_editor = !show_custom_bg_color_editor;
688 }
689 HOVER_HINT("Open custom background color editor (v2+)");
690
691 ImGui::PopStyleVar(2); // Pop the 2 style variables we pushed
692 ImGui::PopID(); // Pop PalettesPopup ID scope
693 ImGui::EndPopup();
694 }
695}
696
698 bool& show_map_properties_panel,
699 bool& show_overlay_preview,
700 int& game_state) {
701 if (ImGui::BeginPopup(
704 .c_str())) {
705 ImGui::PushID("ConfigPopup"); // Fix ImGui duplicate ID warnings
706
707 // Use theme-aware spacing instead of hardcoded constants
709 float padding = gui::LayoutHelpers::GetButtonPadding();
710 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(spacing, spacing));
711 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(padding, padding));
712
713 ImGui::Text(ICON_MD_TUNE " Area Configuration");
714 ImGui::Separator();
715
716 // Basic Properties in 2-column layout for density
717 if (BeginTable("BasicProps", 2, ImGuiTableFlags_SizingFixedFit)) {
718 // Message ID
719 TableNextColumn();
720 ImGui::Text(ICON_MD_MESSAGE " Message");
721 TableNextColumn();
722 if (gui::InputHexWordCustom("##MsgId",
724 ->mutable_message_id(),
728 }
729 if (ImGui::IsItemHovered()) {
730 ImGui::SetTooltip("Message ID shown when entering this area");
731 }
732
733 // Game State
734 TableNextColumn();
735 ImGui::Text(ICON_MD_GAMEPAD " Game State");
736 TableNextColumn();
737 ImGui::SetNextItemWidth(kComboGameStateWidth);
738 if (ImGui::Combo("##GameState", &game_state, kGameStateNames, 3)) {
741 }
742 if (ImGui::IsItemHovered()) {
743 ImGui::SetTooltip(
744 "Affects sprite graphics/palettes based on story progress");
745 }
746
747 ImGui::EndTable();
748 }
749
750 // Area Configuration Section
751 ImGui::Separator();
752 ImGui::Text(ICON_MD_ASPECT_RATIO " Area Configuration");
753 ImGui::Separator();
754
755 // ALL ROMs support Small/Large. Only v3+ supports Wide/Tall.
756 uint8_t asm_version = (*rom_)[zelda3::OverworldCustomASMHasBeenApplied];
757
758 int current_area_size =
759 static_cast<int>(overworld_->overworld_map(current_map)->area_size());
760 ImGui::SetNextItemWidth(kComboAreaSizeWidth);
761
762 if (asm_version >= 3 && asm_version != 0xFF) {
763 // v3+ ROM: Show all 4 area size options
764 if (ImGui::Combo(ICON_MD_PHOTO_SIZE_SELECT_LARGE " Size",
765 &current_area_size, kAreaSizeNames, 4)) {
766 auto status = overworld_->ConfigureMultiAreaMap(
767 current_map, static_cast<zelda3::AreaSizeEnum>(current_area_size));
768 if (status.ok()) {
769 RefreshSiblingMapGraphics(current_map, true);
771 }
772 }
773 HOVER_HINT("Map area size (1x1, 2x2, 2x1, 1x2 screens)");
774 } else {
775 // Vanilla/v1/v2 ROM: Show only Small/Large
776 const char* limited_names[] = {"Small (1x1)", "Large (2x2)"};
777 int limited_size = (current_area_size == 0 || current_area_size == 1)
778 ? current_area_size
779 : 0;
780
781 if (ImGui::Combo(ICON_MD_PHOTO_SIZE_SELECT_LARGE " Size", &limited_size,
782 limited_names, 2)) {
783 auto size = (limited_size == 1) ? zelda3::AreaSizeEnum::LargeArea
785 auto status = overworld_->ConfigureMultiAreaMap(current_map, size);
786 if (status.ok()) {
787 RefreshSiblingMapGraphics(current_map, true);
789 }
790 }
791 HOVER_HINT("Small (1x1) and Large (2x2) maps. Wide/Tall require v3+");
792 }
793
794 // Visual Effects Section
795 ImGui::Separator();
796 ImGui::Text(ICON_MD_AUTO_FIX_HIGH " Visual Effects");
797 ImGui::Separator();
798
799 DrawMosaicControls(current_map);
800 DrawOverlayControls(current_map, show_overlay_preview);
801
802 // Advanced Options Section
803 ImGui::Separator();
804 if (ImGui::Button(ICON_MD_OPEN_IN_NEW " Full Configuration Panel",
805 ImVec2(-1, 0))) {
806 show_map_properties_panel = true;
807 ImGui::CloseCurrentPopup();
808 }
809 HOVER_HINT("Open detailed area configuration with all settings tabs");
810
811 ImGui::PopStyleVar(2); // Pop the 2 style variables we pushed
812 ImGui::PopID(); // Pop ConfigPopup ID scope
813 ImGui::EndPopup();
814 }
815}
816
818 if (BeginTable("BasicProperties", 2,
819 ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit)) {
820 ImGui::TableSetupColumn("Property", ImGuiTableColumnFlags_WidthFixed, 180);
821 ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch);
822
823 TableNextColumn();
824 ImGui::Text(ICON_MD_IMAGE " Area Graphics");
825 TableNextColumn();
826 if (gui::InputHexByte("##AreaGfx",
828 ->mutable_area_graphics(),
830 // CORRECT ORDER: Properties first, then graphics reload
832 (*maps_bmp_)[current_map].set_modified(true);
833 overworld_->mutable_overworld_map(current_map)->LoadAreaGraphics();
834 RefreshSiblingMapGraphics(current_map);
837 }
838 if (ImGui::IsItemHovered()) {
839 ImGui::SetTooltip("Main tileset graphics for this map area");
840 }
841
842 TableNextColumn();
843 ImGui::Text(ICON_MD_PALETTE " Area Palette");
844 TableNextColumn();
845 if (gui::InputHexByte("##AreaPal",
847 ->mutable_area_palette(),
850 auto status = RefreshMapPalette();
852 }
853 if (ImGui::IsItemHovered()) {
854 ImGui::SetTooltip("Color palette for background tiles");
855 }
856
857 TableNextColumn();
858 ImGui::Text(ICON_MD_MESSAGE " Message ID");
859 TableNextColumn();
860 if (gui::InputHexWord("##MsgId",
862 ->mutable_message_id(),
863 kInputFieldSize + 20)) {
866 }
867 if (ImGui::IsItemHovered()) {
868 ImGui::SetTooltip("Message displayed when entering this area");
869 }
870
871 TableNextColumn();
872 ImGui::Text(ICON_MD_BLUR_ON " Mosaic Effect");
873 TableNextColumn();
874 if (ImGui::Checkbox(
875 "##mosaic",
876 overworld_->mutable_overworld_map(current_map)->mutable_mosaic())) {
879 }
880 if (ImGui::IsItemHovered()) {
881 ImGui::SetTooltip("Enable pixelated mosaic transition effect");
882 }
883
884 // Add music editing controls with icons
885 TableNextColumn();
886 ImGui::Text(ICON_MD_MUSIC_NOTE " Music (Beginning)");
887 TableNextColumn();
888 if (gui::InputHexByte("##Music0",
890 ->mutable_area_music(0),
893 }
894 if (ImGui::IsItemHovered()) {
895 ImGui::SetTooltip("Music track before rescuing Zelda");
896 }
897
898 TableNextColumn();
899 ImGui::Text(ICON_MD_MUSIC_NOTE " Music (Zelda)");
900 TableNextColumn();
901 if (gui::InputHexByte("##Music1",
903 ->mutable_area_music(1),
906 }
907 if (ImGui::IsItemHovered()) {
908 ImGui::SetTooltip("Music track after rescuing Zelda");
909 }
910
911 TableNextColumn();
912 ImGui::Text(ICON_MD_MUSIC_NOTE " Music (Master Sword)");
913 TableNextColumn();
914 if (gui::InputHexByte("##Music2",
916 ->mutable_area_music(2),
919 }
920 if (ImGui::IsItemHovered()) {
921 ImGui::SetTooltip("Music track after obtaining Master Sword");
922 }
923
924 TableNextColumn();
925 ImGui::Text(ICON_MD_MUSIC_NOTE " Music (Agahnim)");
926 TableNextColumn();
927 if (gui::InputHexByte("##Music3",
929 ->mutable_area_music(3),
932 }
933 if (ImGui::IsItemHovered()) {
934 ImGui::SetTooltip("Music track after defeating Agahnim (Dark World)");
935 }
936
937 ImGui::EndTable();
938 }
939}
940
942 if (BeginTable("SpriteProperties", 2,
943 ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit)) {
944 ImGui::TableSetupColumn("Property", ImGuiTableColumnFlags_WidthFixed, 180);
945 ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch);
946
947 TableNextColumn();
948 ImGui::Text(ICON_MD_GAMEPAD " Game State");
949 TableNextColumn();
950 static int game_state = 0;
951 ImGui::SetNextItemWidth(120.f);
952 if (ImGui::Combo("##GameState", &game_state, kGameStateNames, 3)) {
955 }
956 if (ImGui::IsItemHovered()) {
957 ImGui::SetTooltip("Affects which sprite graphics/palettes are used");
958 }
959
960 TableNextColumn();
961 ImGui::Text(ICON_MD_PETS " Sprite Graphics 1");
962 TableNextColumn();
963 if (gui::InputHexByte("##SprGfx1",
965 ->mutable_sprite_graphics(1),
969 }
970 if (ImGui::IsItemHovered()) {
971 ImGui::SetTooltip("First sprite graphics sheet for Zelda rescued state");
972 }
973
974 TableNextColumn();
975 ImGui::Text(ICON_MD_PETS " Sprite Graphics 2");
976 TableNextColumn();
977 if (gui::InputHexByte("##SprGfx2",
979 ->mutable_sprite_graphics(2),
983 }
984 if (ImGui::IsItemHovered()) {
985 ImGui::SetTooltip(
986 "Second sprite graphics sheet for Master Sword obtained state");
987 }
988
989 TableNextColumn();
990 ImGui::Text(ICON_MD_COLORIZE " Sprite Palette 1");
991 TableNextColumn();
992 if (gui::InputHexByte("##SprPal1",
994 ->mutable_sprite_palette(1),
998 }
999 if (ImGui::IsItemHovered()) {
1000 ImGui::SetTooltip("Color palette for sprites - Zelda rescued state");
1001 }
1002
1003 TableNextColumn();
1004 ImGui::Text(ICON_MD_COLORIZE " Sprite Palette 2");
1005 TableNextColumn();
1006 if (gui::InputHexByte("##SprPal2",
1007 overworld_->mutable_overworld_map(current_map)
1008 ->mutable_sprite_palette(2),
1009 kInputFieldSize)) {
1012 }
1013 if (ImGui::IsItemHovered()) {
1014 ImGui::SetTooltip(
1015 "Color palette for sprites - Master Sword obtained state");
1016 }
1017
1018 ImGui::EndTable();
1019 }
1020}
1021
1023 if (BeginTable("CustomFeatures", 2,
1024 ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit)) {
1025 ImGui::TableSetupColumn("Property", ImGuiTableColumnFlags_WidthFixed, 180);
1026 ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch);
1027
1028 TableNextColumn();
1029 ImGui::Text(ICON_MD_PHOTO_SIZE_SELECT_LARGE " Area Size");
1030 TableNextColumn();
1031 // ALL ROMs support Small/Large. Only v3+ supports Wide/Tall.
1032 auto rom_version_basic = zelda3::OverworldVersionHelper::GetVersion(*rom_);
1033
1034 int current_area_size =
1035 static_cast<int>(overworld_->overworld_map(current_map)->area_size());
1036 ImGui::SetNextItemWidth(130.f);
1037
1038 if (zelda3::OverworldVersionHelper::SupportsAreaEnum(rom_version_basic)) {
1039 // v3+ ROM: Show all 4 area size options
1040 static const char* all_sizes[] = {"Small (1x1)", "Large (2x2)",
1041 "Wide (2x1)", "Tall (1x2)"};
1042 if (ImGui::Combo("##AreaSize", &current_area_size, all_sizes, 4)) {
1043 auto status = overworld_->ConfigureMultiAreaMap(
1044 current_map, static_cast<zelda3::AreaSizeEnum>(current_area_size));
1045 if (status.ok()) {
1046 RefreshSiblingMapGraphics(current_map, true);
1048 }
1049 }
1050 if (ImGui::IsItemHovered()) {
1051 ImGui::SetTooltip(
1052 "Map size: Small (1x1), Large (2x2), Wide (2x1), Tall (1x2)");
1053 }
1054 } else {
1055 // Vanilla/v1/v2 ROM: Show only Small/Large
1056 static const char* limited_sizes[] = {"Small (1x1)", "Large (2x2)"};
1057 int limited_size = (current_area_size == 0 || current_area_size == 1)
1058 ? current_area_size
1059 : 0;
1060
1061 if (ImGui::Combo("##AreaSize", &limited_size, limited_sizes, 2)) {
1062 auto size = (limited_size == 1) ? zelda3::AreaSizeEnum::LargeArea
1064 auto status = overworld_->ConfigureMultiAreaMap(current_map, size);
1065 if (status.ok()) {
1066 RefreshSiblingMapGraphics(current_map, true);
1068 }
1069 }
1070 if (ImGui::IsItemHovered()) {
1071 ImGui::SetTooltip(
1072 "Map size: Small (1x1), Large (2x2). Wide/Tall require v3+");
1073 }
1074 }
1075
1077 rom_version_basic)) {
1078 TableNextColumn();
1079 ImGui::Text(ICON_MD_COLOR_LENS " Main Palette");
1080 TableNextColumn();
1081 if (gui::InputHexByte("##MainPal",
1082 overworld_->mutable_overworld_map(current_map)
1083 ->mutable_main_palette(),
1084 kInputFieldSize)) {
1086 auto status = RefreshMapPalette();
1088 }
1089 if (ImGui::IsItemHovered()) {
1090 ImGui::SetTooltip("Extended main palette (ZSCustomOverworld v2+)");
1091 }
1092 }
1093
1095 rom_version_basic)) {
1096 TableNextColumn();
1097 ImGui::Text(ICON_MD_ANIMATION " Animated GFX");
1098 TableNextColumn();
1099 if (gui::InputHexByte("##AnimGfx",
1100 overworld_->mutable_overworld_map(current_map)
1101 ->mutable_animated_gfx(),
1102 kInputFieldSize)) {
1105 }
1106 if (ImGui::IsItemHovered()) {
1107 ImGui::SetTooltip("Animated tile graphics ID (water, lava, etc.)");
1108 }
1109
1110 TableNextColumn();
1111 ImGui::Text(ICON_MD_LAYERS " Subscreen Overlay");
1112 TableNextColumn();
1113 if (gui::InputHexWord("##SubOverlay",
1114 overworld_->mutable_overworld_map(current_map)
1115 ->mutable_subscreen_overlay(),
1116 kInputFieldSize + 20)) {
1119 }
1120 if (ImGui::IsItemHovered()) {
1121 ImGui::SetTooltip("Visual effects overlay ID (fog, rain, backgrounds)");
1122 }
1123 }
1124
1125 ImGui::EndTable();
1126 }
1127}
1128
1131
1132 // Only show custom tile graphics for v1+ ROMs
1134 ImGui::Text(ICON_MD_GRID_VIEW " Custom Tile Graphics (8 sheets)");
1135 Separator();
1136
1137 if (BeginTable("TileGraphics", 2,
1138 ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit)) {
1139 ImGui::TableSetupColumn("Property", ImGuiTableColumnFlags_WidthFixed,
1140 180);
1141 ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch);
1142
1143 for (int i = 0; i < 8; i++) {
1144 TableNextColumn();
1145 ImGui::Text(ICON_MD_LAYERS " Sheet %d", i);
1146 TableNextColumn();
1147 if (gui::InputHexByte(absl::StrFormat("##TileGfx%d", i).c_str(),
1148 overworld_->mutable_overworld_map(current_map)
1149 ->mutable_custom_tileset(i),
1150 kInputFieldSize)) {
1151 overworld_->mutable_overworld_map(current_map)->LoadAreaGraphics();
1152 ForceRefreshGraphics(current_map);
1153 RefreshSiblingMapGraphics(current_map);
1157 }
1158 if (ImGui::IsItemHovered()) {
1159 ImGui::SetTooltip("Custom graphics sheet %d (0x00-0xFF)", i);
1160 }
1161 }
1162
1163 ImGui::EndTable();
1164 }
1165
1166 Separator();
1167 ImGui::TextWrapped(
1168 "These 8 sheets allow custom tile graphics per map. "
1169 "Each sheet references a graphics ID loaded into VRAM.");
1170 } else {
1171 // Vanilla ROM - show info message
1172 ImGui::TextColored(ImVec4(0.6f, 0.6f, 0.6f, 1.0f),
1173 ICON_MD_INFO " Custom Tile Graphics");
1174 ImGui::Separator();
1175 ImGui::TextWrapped(
1176 "Custom tile graphics are not available in vanilla ROMs.\n\n"
1177 "To enable this feature, upgrade your ROM to ZSCustomOverworld v1+, "
1178 "which provides 8 customizable graphics sheets per map for advanced "
1179 "tileset customization.");
1180 }
1181}
1182
1184 ImGui::Text(ICON_MD_MUSIC_NOTE " Music Settings for Game States");
1185 Separator();
1186
1187 if (BeginTable("MusicSettings", 2,
1188 ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit)) {
1189 ImGui::TableSetupColumn("Game State", ImGuiTableColumnFlags_WidthFixed,
1190 220);
1191 ImGui::TableSetupColumn("Music Track ID",
1192 ImGuiTableColumnFlags_WidthStretch);
1193
1194 const char* music_state_names[] = {
1195 ICON_MD_PLAY_ARROW " Beginning (Pre-Zelda)",
1196 ICON_MD_FAVORITE " Zelda Rescued",
1197 ICON_MD_OFFLINE_BOLT " Master Sword Obtained",
1198 ICON_MD_CASTLE " Agahnim Defeated"};
1199
1200 const char* music_descriptions[] = {
1201 "Music before rescuing Zelda from the castle",
1202 "Music after rescuing Zelda from Hyrule Castle",
1203 "Music after obtaining the Master Sword from the Lost Woods",
1204 "Music after defeating Agahnim (Dark World music)"};
1205
1206 for (int i = 0; i < 4; i++) {
1207 TableNextColumn();
1208 ImGui::Text("%s", music_state_names[i]);
1209
1210 TableNextColumn();
1211 if (gui::InputHexByte(absl::StrFormat("##Music%d", i).c_str(),
1212 overworld_->mutable_overworld_map(current_map)
1213 ->mutable_area_music(i),
1214 kInputFieldSize)) {
1216
1217 // Update the ROM directly since music is not automatically saved
1218 int music_address = 0;
1219 switch (i) {
1220 case 0:
1221 music_address = zelda3::kOverworldMusicBeginning + current_map;
1222 break;
1223 case 1:
1224 music_address = zelda3::kOverworldMusicZelda + current_map;
1225 break;
1226 case 2:
1227 music_address = zelda3::kOverworldMusicMasterSword + current_map;
1228 break;
1229 case 3:
1230 music_address = zelda3::kOverworldMusicAgahnim + current_map;
1231 break;
1232 }
1233
1234 if (music_address > 0) {
1235 (*rom_)[music_address] =
1236 *overworld_->mutable_overworld_map(current_map)
1237 ->mutable_area_music(i);
1238 }
1239 }
1240 if (ImGui::IsItemHovered()) {
1241 ImGui::SetTooltip("%s", music_descriptions[i]);
1242 }
1243 }
1244
1245 ImGui::EndTable();
1246 }
1247
1248 Separator();
1249 ImGui::TextWrapped(
1250 "Music tracks control the background music for different "
1251 "game progression states on this overworld map.");
1252
1253 // Show common music track IDs for reference in a collapsing section
1254 Separator();
1255 if (ImGui::CollapsingHeader(ICON_MD_HELP_OUTLINE " Common Music Track IDs",
1256 ImGuiTreeNodeFlags_DefaultOpen)) {
1257 ImGui::Indent();
1258 ImGui::BulletText("0x02 - Overworld Theme");
1259 ImGui::BulletText("0x05 - Kakariko Village");
1260 ImGui::BulletText("0x07 - Lost Woods");
1261 ImGui::BulletText("0x09 - Dark World Theme");
1262 ImGui::BulletText("0x0F - Ganon's Tower");
1263 ImGui::BulletText("0x11 - Death Mountain");
1264 ImGui::Unindent();
1265 }
1266}
1267
1273
1279
1282 return refresh_map_palette_();
1283 }
1284 return absl::OkStatus();
1285}
1286
1289 return refresh_tile16_blockset_();
1290 }
1291 return absl::OkStatus();
1292}
1293
1296 force_refresh_graphics_(map_index);
1297 }
1298}
1299
1301 bool include_self) {
1302 if (!overworld_ || !maps_bmp_ || map_index < 0 ||
1303 map_index >= zelda3::kNumOverworldMaps) {
1304 return;
1305 }
1306
1307 auto* map = overworld_->mutable_overworld_map(map_index);
1308 if (map->area_size() == zelda3::AreaSizeEnum::SmallArea) {
1309 return; // No siblings for small areas
1310 }
1311
1312 int parent_id = map->parent();
1313 std::vector<int> siblings;
1314
1315 switch (map->area_size()) {
1317 siblings = {parent_id, parent_id + 1, parent_id + 8, parent_id + 9};
1318 break;
1320 siblings = {parent_id, parent_id + 1};
1321 break;
1323 siblings = {parent_id, parent_id + 8};
1324 break;
1325 default:
1326 return;
1327 }
1328
1329 for (int sibling : siblings) {
1330 if (sibling >= 0 && sibling < zelda3::kNumOverworldMaps) {
1331 // Skip self unless include_self is true
1332 if (sibling == map_index && !include_self) {
1333 continue;
1334 }
1335
1336 // Mark as modified FIRST
1337 (*maps_bmp_)[sibling].set_modified(true);
1338
1339 // Load graphics from ROM
1340 overworld_->mutable_overworld_map(sibling)->LoadAreaGraphics();
1341
1342 // CRITICAL FIX: Force immediate refresh on the sibling
1343 // This will trigger the callback to OverworldEditor's
1344 // RefreshChildMapOnDemand
1345 ForceRefreshGraphics(sibling);
1346 }
1347 }
1348
1349 // After marking all siblings, trigger a refresh
1350 // This ensures all marked maps get processed
1352}
1353
1357 ImGui::Separator();
1358 ImGui::Text("Mosaic Effects (per direction):");
1359
1360 auto* current_map_ptr = overworld_->mutable_overworld_map(current_map);
1361 std::array<bool, 4> mosaic_expanded = current_map_ptr->mosaic_expanded();
1362 const char* direction_names[] = {"North", "South", "East", "West"};
1363
1364 for (int i = 0; i < 4; i++) {
1365 if (ImGui::Checkbox(direction_names[i], &mosaic_expanded[i])) {
1366 current_map_ptr->set_mosaic_expanded(i, mosaic_expanded[i]);
1369 }
1370 }
1371 } else {
1372 if (ImGui::Checkbox(
1373 "Mosaic Effect",
1374 overworld_->mutable_overworld_map(current_map)->mutable_mosaic())) {
1377 }
1378 }
1379}
1380
1382 bool& show_overlay_preview) {
1384
1385 // Determine if this is a special overworld map (0x80-0x9F)
1386 bool is_special_overworld_map = (current_map >= 0x80 && current_map < 0xA0);
1387
1388 if (is_special_overworld_map) {
1389 // Special overworld maps (0x80-0x9F) serve as visual effect sources
1390 ImGui::TextColored(ImVec4(0.4f, 0.8f, 1.0f, 1.0f),
1391 ICON_MD_INFO " Special Area Map (0x%02X)", current_map);
1392 ImGui::Separator();
1393 ImGui::TextWrapped(
1394 "This is a special area map (0x80-0x9F) used as a visual effect "
1395 "source. These maps provide the graphics data for subscreen overlays "
1396 "like fog, rain, forest canopy, and sky backgrounds that appear on "
1397 "normal maps (0x00-0x7F).");
1398 ImGui::Spacing();
1399 ImGui::TextWrapped(
1400 "You can edit the tile16 data here to customize how the visual effects "
1401 "appear when referenced by other maps.");
1402 } else {
1403 // Light World (0x00-0x3F) and Dark World (0x40-0x7F) maps support subscreen
1404 // overlays
1405
1406 // Comprehensive help section
1407 ImGui::TextColored(ImVec4(0.4f, 0.8f, 1.0f, 1.0f),
1408 ICON_MD_HELP_OUTLINE " Visual Effects Overview");
1409 ImGui::SameLine();
1410 if (ImGui::Button(ICON_MD_INFO "##HelpButton")) {
1411 ImGui::OpenPopup(
1414 .c_str());
1415 }
1416
1417 if (ImGui::BeginPopup(
1420 .c_str())) {
1421 ImGui::Text(ICON_MD_HELP " Understanding Overlay Types");
1422 ImGui::Separator();
1423
1424 ImGui::TextColored(ImVec4(0.4f, 1.0f, 0.4f, 1.0f), ICON_MD_LAYERS
1425 " 1. Subscreen Overlays (Visual Effects)");
1426 ImGui::Indent();
1427 ImGui::BulletText("Displayed as semi-transparent layers");
1428 ImGui::BulletText("Reference special area maps (0x80-0x9F)");
1429 ImGui::BulletText("Examples: fog, rain, forest canopy, sky");
1430 ImGui::BulletText("Purely visual - don't affect collision");
1431 ImGui::Unindent();
1432
1433 ImGui::Spacing();
1434 ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.4f, 1.0f),
1435 ICON_MD_EDIT_NOTE " 2. Map Overlays (Interactive)");
1436 ImGui::Indent();
1437 ImGui::BulletText("Dynamic tile16 changes on the map");
1438 ImGui::BulletText("Used for bridges appearing, holes opening");
1439 ImGui::BulletText("Stored as tile16 ID arrays");
1440 ImGui::BulletText("Affect collision and interaction");
1441 ImGui::BulletText("Triggered by game events/progression");
1442 ImGui::Unindent();
1443
1444 ImGui::Separator();
1445 ImGui::TextWrapped(
1446 "Note: Subscreen overlays are what you configure here. "
1447 "Map overlays are event-driven and edited separately.");
1448
1449 ImGui::EndPopup();
1450 }
1451 ImGui::Separator();
1452
1453 // Subscreen Overlay Section
1454 ImGui::Text(ICON_MD_LAYERS " Subscreen Overlay (Visual Effects)");
1455
1456 uint16_t current_overlay =
1457 overworld_->mutable_overworld_map(current_map)->subscreen_overlay();
1458 if (gui::InputHexWord("Visual Effect Map ID", &current_overlay,
1459 kInputFieldSize + 20)) {
1460 overworld_->mutable_overworld_map(current_map)
1461 ->set_subscreen_overlay(current_overlay);
1464 }
1465 if (ImGui::IsItemHovered()) {
1466 ImGui::SetTooltip(
1467 "References a special area map (0x80-0x9F) for visual effects.\n"
1468 "The referenced map's tile16 data is drawn as a semi-transparent\n"
1469 "layer on top of or behind this area for atmospheric effects.");
1470 }
1471
1472 // Show subscreen overlay description with color coding
1473 std::string overlay_desc = GetOverlayDescription(current_overlay);
1474 if (current_overlay == 0x00FF) {
1475 ImGui::TextColored(ImVec4(0.6f, 0.6f, 0.6f, 1.0f), ICON_MD_CHECK " %s",
1476 overlay_desc.c_str());
1477 } else if (current_overlay >= 0x80 && current_overlay < 0xA0) {
1478 ImGui::TextColored(ImVec4(0.4f, 1.0f, 0.4f, 1.0f),
1479 ICON_MD_VISIBILITY " %s", overlay_desc.c_str());
1480 } else {
1481 ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.4f, 1.0f),
1482 ICON_MD_HELP_OUTLINE " %s", overlay_desc.c_str());
1483 }
1484
1485 // Preview checkbox with better labeling
1486 ImGui::Spacing();
1487 if (ImGui::Checkbox(ICON_MD_PREVIEW " Preview Visual Effect on Map",
1488 &show_overlay_preview)) {
1489 // Toggle subscreen overlay preview
1490 }
1491 if (ImGui::IsItemHovered()) {
1492 ImGui::SetTooltip(
1493 "Shows a semi-transparent preview of the visual effect overlay\n"
1494 "drawn on top of the current map in the editor canvas.\n\n"
1495 "This preview shows how the subscreen overlay will appear in-game.");
1496 }
1497
1498 ImGui::Separator();
1499
1500 // Interactive/Dynamic Map Overlay Section (for vanilla ROMs)
1501 if (rom_version == zelda3::OverworldVersion::kVanilla) {
1502 ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.4f, 1.0f),
1503 ICON_MD_EDIT_NOTE " Map Overlay (Interactive)");
1504 ImGui::SameLine();
1505 if (ImGui::Button(ICON_MD_INFO "##MapOverlayHelp")) {
1506 ImGui::OpenPopup(
1509 .c_str());
1510 }
1511 if (ImGui::BeginPopup(
1514 .c_str())) {
1515 ImGui::Text(ICON_MD_HELP " Map Overlays (Interactive Tile Changes)");
1516 ImGui::Separator();
1517 ImGui::TextWrapped(
1518 "Map overlays are different from visual effect overlays. "
1519 "They contain tile16 data that dynamically replaces tiles on "
1520 "the map based on game events or progression.");
1521 ImGui::Spacing();
1522 ImGui::Text("Common uses:");
1523 ImGui::BulletText("Bridges appearing over water");
1524 ImGui::BulletText("Holes revealing secret passages");
1525 ImGui::BulletText("Rocks/bushes being moved");
1526 ImGui::BulletText("Environmental changes from story events");
1527 ImGui::Spacing();
1528 ImGui::TextWrapped(
1529 "These are triggered by game code and stored as separate "
1530 "tile data arrays in the ROM. ZSCustomOverworld v3+ provides "
1531 "extended control over these features.");
1532 ImGui::EndPopup();
1533 }
1534
1535 auto* current_map_ptr = overworld_->overworld_map(current_map);
1536 ImGui::Spacing();
1537 if (current_map_ptr->has_overlay()) {
1538 ImGui::TextColored(ImVec4(0.4f, 1.0f, 0.4f, 1.0f),
1539 ICON_MD_CHECK " Overlay ID: 0x%04X",
1540 current_map_ptr->overlay_id());
1541 ImGui::Text(ICON_MD_STORAGE " Data Size: %d bytes",
1542 static_cast<int>(current_map_ptr->overlay_data().size()));
1543 ImGui::TextColored(ImVec4(0.8f, 0.8f, 0.4f, 1.0f),
1544 ICON_MD_INFO " Read-only in vanilla ROM");
1545 } else {
1546 ImGui::TextColored(ImVec4(0.6f, 0.6f, 0.6f, 1.0f),
1547 ICON_MD_BLOCK " No map overlay data for this area");
1548 }
1549 }
1550
1551 // Show version and capability info
1552 ImGui::Separator();
1553 if (rom_version == zelda3::OverworldVersion::kVanilla) {
1554 ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f),
1555 ICON_MD_INFO " Vanilla ROM");
1556 ImGui::BulletText("Visual effects use maps 0x80-0x9F");
1557 ImGui::BulletText("Map overlays are read-only");
1558 } else {
1559 const char* version_name =
1561 ImGui::TextColored(ImVec4(0.4f, 1.0f, 0.4f, 1.0f), ICON_MD_UPGRADE " %s",
1562 version_name);
1563 ImGui::BulletText("Enhanced visual effect control");
1565 ImGui::BulletText("Extended overlay system");
1566 ImGui::BulletText("Custom area sizes support");
1567 }
1568 }
1569 }
1570}
1571
1572std::string MapPropertiesSystem::GetOverlayDescription(uint16_t overlay_id) {
1573 if (overlay_id == 0x0093) {
1574 return "Triforce Room Curtain";
1575 } else if (overlay_id == 0x0094) {
1576 return "Under the Bridge";
1577 } else if (overlay_id == 0x0095) {
1578 return "Sky Background (LW Death Mountain)";
1579 } else if (overlay_id == 0x0096) {
1580 return "Pyramid Background";
1581 } else if (overlay_id == 0x0097) {
1582 return "First Fog Overlay (Master Sword Area)";
1583 } else if (overlay_id == 0x009C) {
1584 return "Lava Background (DW Death Mountain)";
1585 } else if (overlay_id == 0x009D) {
1586 return "Second Fog Overlay (Lost Woods/Skull Woods)";
1587 } else if (overlay_id == 0x009E) {
1588 return "Tree Canopy (Forest)";
1589 } else if (overlay_id == 0x009F) {
1590 return "Rain Effect (Misery Mire)";
1591 } else if (overlay_id == 0x00FF) {
1592 return "No Overlay";
1593 } else {
1594 return "Custom overlay";
1595 }
1596}
1597
1599 int current_world,
1600 bool show_overlay_preview) {
1601 gfx::ScopedTimer timer("map_properties_draw_overlay_preview");
1602
1603 if (!show_overlay_preview || !maps_bmp_ || !canvas_)
1604 return;
1605
1606 // Get subscreen overlay information based on ROM version and map type
1607 uint16_t overlay_id = 0x00FF;
1608 bool has_subscreen_overlay = false;
1609
1610 bool is_special_overworld_map = (current_map >= 0x80 && current_map < 0xA0);
1611
1612 if (is_special_overworld_map) {
1613 // Special overworld maps (0x80-0x9F) do not support subscreen overlays
1614 return;
1615 }
1616
1617 // Light World (0x00-0x3F) and Dark World (0x40-0x7F) maps support subscreen
1618 // overlays for all versions
1619 overlay_id = overworld_->overworld_map(current_map)->subscreen_overlay();
1620 has_subscreen_overlay = (overlay_id != 0x00FF);
1621
1622 if (!has_subscreen_overlay)
1623 return;
1624
1625 // Map subscreen overlay ID to special area map for bitmap
1626 int overlay_map_index = -1;
1627 if (overlay_id >= 0x80 && overlay_id < 0xA0) {
1628 overlay_map_index = overlay_id;
1629 }
1630
1631 if (overlay_map_index < 0 || overlay_map_index >= zelda3::kNumOverworldMaps)
1632 return;
1633
1634 // Get the subscreen overlay map's bitmap
1635 const auto& overlay_bitmap = (*maps_bmp_)[overlay_map_index];
1636 if (!overlay_bitmap.is_active() || !overlay_bitmap.texture())
1637 return;
1638
1639 // Calculate position for subscreen overlay preview on the current map
1640 int current_map_x = current_map % 8;
1641 int current_map_y = current_map / 8;
1642 if (current_world == 1) {
1643 current_map_x = (current_map - 0x40) % 8;
1644 current_map_y = (current_map - 0x40) / 8;
1645 } else if (current_world == 2) {
1646 current_map_x = (current_map - 0x80) % 8;
1647 current_map_y = (current_map - 0x80) / 8;
1648 }
1649
1650 int scale = static_cast<int>(canvas_->global_scale());
1651 int map_x = current_map_x * kOverworldMapSize * scale;
1652 int map_y = current_map_y * kOverworldMapSize * scale;
1653
1654 // Determine if this is a background or foreground subscreen overlay
1655 bool is_background_overlay =
1656 (overlay_id == 0x0095 || overlay_id == 0x0096 || overlay_id == 0x009C);
1657
1658 // Set alpha for semi-transparent preview
1659 ImU32 overlay_color =
1660 is_background_overlay ? IM_COL32(255, 255, 255, 128)
1661 : // Background subscreen overlays - lighter
1662 IM_COL32(255, 255, 255,
1663 180); // Foreground subscreen overlays - more opaque
1664
1665 // Draw the subscreen overlay bitmap with semi-transparency
1666 canvas_->draw_list()->AddImage(
1667 (ImTextureID)(intptr_t)overlay_bitmap.texture(), ImVec2(map_x, map_y),
1668 ImVec2(map_x + kOverworldMapSize * scale,
1669 map_y + kOverworldMapSize * scale),
1670 ImVec2(0, 0), ImVec2(1, 1), overlay_color);
1671}
1672
1674 if (ImGui::BeginPopup(
1677 .c_str())) {
1678 ImGui::PushID("ViewPopup"); // Fix ImGui duplicate ID warnings
1679
1680 // Use theme-aware spacing instead of hardcoded constants
1682 float padding = gui::LayoutHelpers::GetButtonPadding();
1683 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(spacing, spacing));
1684 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(padding, padding));
1685
1686 ImGui::Text("View Controls");
1687 ImGui::Separator();
1688
1689 // Horizontal layout for view controls
1690 if (ImGui::Button(ICON_MD_ZOOM_OUT, ImVec2(kIconButtonWidth, 0))) {
1691 float new_scale = std::max(kOverworldMinZoom,
1693 canvas_->set_global_scale(new_scale);
1694 }
1695 HOVER_HINT("Zoom out on the canvas");
1696 ImGui::SameLine();
1697 if (ImGui::Button(ICON_MD_ZOOM_IN, ImVec2(kIconButtonWidth, 0))) {
1698 float new_scale = std::min(kOverworldMaxZoom,
1700 canvas_->set_global_scale(new_scale);
1701 }
1702 HOVER_HINT("Zoom in on the canvas");
1703 ImGui::SameLine();
1704 if (ImGui::Button(ICON_MD_OPEN_IN_FULL, ImVec2(kIconButtonWidth, 0))) {
1706 }
1707 HOVER_HINT("Reset zoom to 100%");
1708
1709 ImGui::PopStyleVar(2); // Pop the 2 style variables we pushed
1710 ImGui::PopID(); // Pop ViewPopup ID scope
1711 ImGui::EndPopup();
1712 }
1713}
1714
1716 if (ImGui::BeginPopup(
1719 .c_str())) {
1720 ImGui::PushID("QuickPopup"); // Fix ImGui duplicate ID warnings
1721
1722 // Use theme-aware spacing instead of hardcoded constants
1724 float padding = gui::LayoutHelpers::GetButtonPadding();
1725 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(spacing, spacing));
1726 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(padding, padding));
1727
1728 ImGui::Text("Quick Access");
1729 ImGui::Separator();
1730
1731 // Horizontal layout for quick access buttons
1732 if (ImGui::Button(ICON_MD_GRID_VIEW, ImVec2(kIconButtonWidth, 0))) {
1733 // This would need to be connected to the Tile16 editor toggle
1734 // For now, just show the option
1735 }
1736 HOVER_HINT("Open Tile16 Editor (Ctrl+T)");
1737 ImGui::SameLine();
1738
1739 if (ImGui::Button(ICON_MD_CONTENT_COPY, ImVec2(kIconButtonWidth, 0))) {
1740 // This would need to be connected to the copy map function
1741 // For now, just show the option
1742 }
1743 HOVER_HINT("Copy current map to clipboard");
1744 ImGui::SameLine();
1745
1746 if (ImGui::Button(ICON_MD_LOCK, ImVec2(kIconButtonWidth, 0))) {
1747 // This would need to be connected to the map lock toggle
1748 // For now, just show the option
1749 }
1750 HOVER_HINT("Lock/unlock current map (Ctrl+L)");
1751
1752 ImGui::PopStyleVar(2); // Pop the 2 style variables we pushed
1753 ImGui::PopID(); // Pop QuickPopup ID scope
1754 ImGui::EndPopup();
1755 }
1756}
1757
1758} // namespace editor
1759} // namespace yaze
std::array< gfx::Bitmap, zelda3::kNumOverworldMaps > * maps_bmp_
std::function< void()> edit_tile16_callback_
void SetupCanvasContextMenu(gui::Canvas &canvas, int current_map, bool current_map_lock, bool &show_map_properties_panel, bool &show_custom_bg_color_editor, bool &show_overlay_editor, int current_mode=0)
void DrawOverlayEditor(int current_map, bool &show_overlay_editor)
void DrawPropertiesPopup(int current_map, bool &show_map_properties_panel, bool &show_overlay_preview, int &game_state)
RefreshPaletteCallback refresh_tile16_blockset_
void DrawOverlayPreviewOnMap(int current_map, int current_world, bool show_overlay_preview)
void DrawMosaicControls(int current_map)
void DrawMapPropertiesPanel(int current_map, bool &show_map_properties_panel)
std::function< void(const std::string &) entity_insert_callback_)
void DrawSpritePropertiesTab(int current_map)
RefreshPaletteCallback refresh_map_palette_
void DrawBasicPropertiesTab(int current_map)
void DrawCustomFeaturesTab(int current_map)
void DrawPalettesPopup(int current_map, int game_state, bool &show_custom_bg_color_editor)
void DrawCustomBackgroundColorEditor(int current_map, bool &show_custom_bg_color_editor)
void DrawCanvasToolbar(int &current_world, int &current_map, bool &current_map_lock, bool &show_map_properties_panel, bool &show_custom_bg_color_editor, bool &show_overlay_editor, bool &show_overlay_preview, int &game_state, EditingMode &current_mode, EntityEditMode &entity_edit_mode)
void RefreshSiblingMapGraphics(int map_index, bool include_self=false)
void DrawTileGraphicsTab(int current_map)
ForceRefreshGraphicsCallback force_refresh_graphics_
std::string GetOverlayDescription(uint16_t overlay_id)
void DrawOverlayControls(int current_map, bool &show_overlay_preview)
void DrawGraphicsPopup(int current_map, int game_state)
RAII timer for automatic timing management.
SNES Color container.
Definition snes_color.h:110
constexpr uint16_t snes() const
Get SNES 15-bit color.
Definition snes_color.h:193
Modern, robust canvas for drawing and manipulating graphics.
Definition canvas.h:150
void set_scrolling(ImVec2 scroll)
Definition canvas.h:446
auto global_scale() const
Definition canvas.h:494
void SetUsageMode(CanvasUsage usage)
Definition canvas.cc:237
auto draw_list() const
Definition canvas.h:442
void ClearContextMenuItems()
Definition canvas.cc:776
void AddContextMenuItem(const gui::CanvasMenuItem &item)
Definition canvas.cc:753
void set_global_scale(float scale)
Definition canvas.h:453
static float GetButtonPadding()
static float GetStandardSpacing()
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.
static const char * GetVersionName(OverworldVersion version)
Get human-readable version name for display/logging.
auto is_loaded() const
Definition overworld.h:528
auto overworld_map(int i) const
Definition overworld.h:473
auto mutable_overworld_map(int i)
Definition overworld.h:479
absl::Status ConfigureMultiAreaMap(int parent_index, AreaSizeEnum size)
Configure a multi-area map structure (Large/Wide/Tall)
Definition overworld.cc:306
struct snes_color snes_color
SNES color in 15-bit RGB format (BGR555)
#define ICON_MD_BLOCK
Definition icons.h:269
#define ICON_MD_GRID_VIEW
Definition icons.h:897
#define ICON_MD_COLORIZE
Definition icons.h:441
#define ICON_MD_INFO
Definition icons.h:993
#define ICON_MD_STORAGE
Definition icons.h:1865
#define ICON_MD_PETS
Definition icons.h:1431
#define ICON_MD_UPGRADE
Definition icons.h:2047
#define ICON_MD_LOCK_OPEN
Definition icons.h:1142
#define ICON_MD_LOCK
Definition icons.h:1140
#define ICON_MD_LIGHTBULB
Definition icons.h:1083
#define ICON_MD_PHOTO_SIZE_SELECT_LARGE
Definition icons.h:1459
#define ICON_MD_OFFLINE_BOLT
Definition icons.h:1344
#define ICON_MD_PLAY_ARROW
Definition icons.h:1479
#define ICON_MD_CHECK
Definition icons.h:397
#define ICON_MD_DRAW
Definition icons.h:625
#define ICON_MD_TUNE
Definition icons.h:2022
#define ICON_MD_ZOOM_OUT
Definition icons.h:2196
#define ICON_MD_OPEN_IN_FULL
Definition icons.h:1353
#define ICON_MD_MESSAGE
Definition icons.h:1201
#define ICON_MD_VISIBILITY
Definition icons.h:2101
#define ICON_MD_GRASS
Definition icons.h:891
#define ICON_MD_FORMAT_COLOR_FILL
Definition icons.h:830
#define ICON_MD_DOOR_BACK
Definition icons.h:612
#define ICON_MD_CASTLE
Definition icons.h:380
#define ICON_MD_AUTO_FIX_HIGH
Definition icons.h:218
#define ICON_MD_MUSIC_NOTE
Definition icons.h:1264
#define ICON_MD_RESTORE
Definition icons.h:1605
#define ICON_MD_ASPECT_RATIO
Definition icons.h:192
#define ICON_MD_LAYERS
Definition icons.h:1068
#define ICON_MD_ANIMATION
Definition icons.h:157
#define ICON_MD_DOOR_FRONT
Definition icons.h:613
#define ICON_MD_BLUR_ON
Definition icons.h:281
#define ICON_MD_IMAGE
Definition icons.h:982
#define ICON_MD_PREVIEW
Definition icons.h:1512
#define ICON_MD_FAVORITE
Definition icons.h:727
#define ICON_MD_HELP_OUTLINE
Definition icons.h:935
#define ICON_MD_ADD_LOCATION
Definition icons.h:100
#define ICON_MD_ZOOM_IN
Definition icons.h:2194
#define ICON_MD_MOUSE
Definition icons.h:1251
#define ICON_MD_EDIT_NOTE
Definition icons.h:650
#define ICON_MD_PALETTE
Definition icons.h:1370
#define ICON_MD_OPEN_IN_NEW
Definition icons.h:1354
#define ICON_MD_PEST_CONTROL_RODENT
Definition icons.h:1430
#define ICON_MD_CONTENT_COPY
Definition icons.h:465
#define ICON_MD_COLOR_LENS
Definition icons.h:440
#define ICON_MD_PHOTO
Definition icons.h:1451
#define ICON_MD_CYCLONE
Definition icons.h:514
#define ICON_MD_GAMEPAD
Definition icons.h:866
#define ICON_MD_HELP
Definition icons.h:933
#define HOVER_HINT(string)
Definition macro.h:24
constexpr const char * kAreaSizeNames[]
constexpr unsigned int kOverworldMapSize
constexpr float kOverworldMaxZoom
constexpr float kTableColumnWorld
constexpr float kOverworldMinZoom
constexpr const char * kWorldNames[]
constexpr float kHexByteInputWidth
constexpr float kComboWorldWidth
constexpr float kTableColumnMap
constexpr float kTableColumnAreaSize
constexpr float kOverworldZoomStep
constexpr float kComboGameStateWidth
constexpr float kInputFieldSize
Definition entity.cc:23
constexpr const char * kGameStateNames[]
Definition ui_constants.h:8
constexpr float kIconButtonWidth
constexpr float kHexWordInputWidth
constexpr float kComboAreaSizeWidth
constexpr float kTableColumnLock
constexpr const char * kOverworld
Definition popup_id.h:53
constexpr const char * kPalettesPopup
Definition popup_id.h:75
constexpr const char * kGraphicsPopup
Definition popup_id.h:74
constexpr const char * kOverlayTypesHelp
Definition popup_id.h:79
constexpr const char * kInteractiveOverlayHelp
Definition popup_id.h:80
constexpr const char * kViewPopup
Definition popup_id.h:77
constexpr const char * kQuickPopup
Definition popup_id.h:78
constexpr const char * kConfigPopup
Definition popup_id.h:76
bool InputHexWord(const char *label, uint16_t *data, float input_width, bool no_step)
Definition input.cc:344
bool ToggleButton(const char *label, bool active, const ImVec2 &size)
ImVec4 ConvertSnesColorToImVec4(const gfx::SnesColor &color)
Convert SnesColor to standard ImVec4 for display.
Definition color.cc:19
bool InputHexWordCustom(const char *label, uint16_t *data, float input_width)
Definition input.cc:726
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.
Definition popup_id.h:23
gfx::SnesColor ConvertImVec4ToSnesColor(const ImVec4 &color)
Convert standard ImVec4 to SnesColor.
Definition color.cc:32
bool InputHexByte(const char *label, uint8_t *data, float input_width, bool no_step)
Definition input.cc:370
constexpr int OverworldCustomAreaSpecificBGEnabled
constexpr int kNumOverworldMaps
Definition common.h:85
constexpr int kOverworldMusicBeginning
Definition overworld.h:120
AreaSizeEnum
Area size enumeration for v3+ ROMs.
constexpr int OverworldCustomASMHasBeenApplied
Definition common.h:89
constexpr int kOverworldMusicAgahnim
Definition overworld.h:123
@ kVanilla
0xFF in ROM, no ZScream ASM applied
constexpr int kOverworldMusicMasterSword
Definition overworld.h:122
constexpr int kOverworldMusicZelda
Definition overworld.h:121
constexpr int OverworldCustomSubscreenOverlayEnabled
constexpr int OverworldCustomAreaSpecificBGPalette
constexpr int OverworldCustomSubscreenOverlayArray
SNES color in 15-bit RGB format (BGR555)
Definition yaze.h:218
Declarative menu item definition.
Definition canvas_menu.h:64
std::vector< CanvasMenuItem > subitems
Definition canvas_menu.h:91
std::function< void()> callback
Definition canvas_menu.h:75