yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
entity.cc
Go to the documentation of this file.
2
8#include "imgui.h"
9#include "util/hex.h"
10#include "zelda3/common.h"
12
13namespace yaze {
14namespace editor {
15
16using ImGui::BeginChild;
17using ImGui::Button;
18using ImGui::Checkbox;
19using ImGui::EndChild;
20using ImGui::SameLine;
21using ImGui::Selectable;
22using ImGui::Text;
23
24constexpr float kInputFieldSize = 30.f;
25
27 ImVec2 canvas_p0, ImVec2 scrolling,
28 float scale) {
29 // Get the mouse position relative to the canvas
30 const ImGuiIO& io = ImGui::GetIO();
31 const ImVec2 origin(canvas_p0.x + scrolling.x, canvas_p0.y + scrolling.y);
32 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
33
34 // Scale entity bounds to match canvas zoom level
35 float scaled_x = entity.x_ * scale;
36 float scaled_y = entity.y_ * scale;
37 float scaled_size = 16.0f * scale;
38
39 // Check if the mouse is hovering over the scaled entity bounds
40 return mouse_pos.x >= scaled_x && mouse_pos.x <= scaled_x + scaled_size &&
41 mouse_pos.y >= scaled_y && mouse_pos.y <= scaled_y + scaled_size;
42}
43
45 const gui::CanvasRuntime& rt) {
46 // Use runtime geometry to compute mouse position relative to canvas
47 const ImGuiIO& io = ImGui::GetIO();
48 const ImVec2 origin(rt.canvas_p0.x + rt.scrolling.x,
49 rt.canvas_p0.y + rt.scrolling.y);
50 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
51
52 // Scale entity bounds to match canvas zoom level
53 float scaled_x = entity.x_ * rt.scale;
54 float scaled_y = entity.y_ * rt.scale;
55 float scaled_size = 16.0f * rt.scale;
56
57 // Check if the mouse is hovering over the scaled entity bounds
58 return mouse_pos.x >= scaled_x && mouse_pos.x <= scaled_x + scaled_size &&
59 mouse_pos.y >= scaled_y && mouse_pos.y <= scaled_y + scaled_size;
60}
61
62void MoveEntityOnGrid(zelda3::GameEntity* entity, ImVec2 canvas_p0,
63 ImVec2 scrolling, bool free_movement, float scale) {
64 // Get the mouse position relative to the canvas
65 const ImGuiIO& io = ImGui::GetIO();
66 const ImVec2 origin(canvas_p0.x + scrolling.x, canvas_p0.y + scrolling.y);
67 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
68
69 // Convert screen position to world position accounting for scale
70 float world_x = mouse_pos.x / scale;
71 float world_y = mouse_pos.y / scale;
72
73 // Calculate the new position on the 16x16 or 8x8 grid (in world coordinates)
74 int grid_size = free_movement ? 8 : 16;
75 int new_x = static_cast<int>(world_x) / grid_size * grid_size;
76 int new_y = static_cast<int>(world_y) / grid_size * grid_size;
77
78 // Update the entity position
79 entity->set_x(new_x);
80 entity->set_y(new_y);
81}
82
84 bool set_done = false;
85 if (set_done) {
86 set_done = false;
87 }
88 if (ImGui::BeginPopup("Entrance Inserter")) {
89 static int entrance_id = 0;
90 if (ImGui::IsWindowAppearing()) {
91 entrance_id = 0;
92 }
93
94 gui::InputHex("Entrance ID", &entrance_id);
95
96 if (Button(ICON_MD_DONE)) {
97 set_done = true;
98 ImGui::CloseCurrentPopup();
99 }
100
101 SameLine();
102 if (Button(ICON_MD_CANCEL)) {
103 ImGui::CloseCurrentPopup();
104 }
105
106 ImGui::EndPopup();
107 }
108 return set_done;
109}
110
112 static bool set_done = false;
113 if (set_done) {
114 set_done = false;
115 return true;
116 }
117
118 if (ImGui::BeginPopupModal(
120 .c_str(),
121 NULL, ImGuiWindowFlags_AlwaysAutoResize)) {
122 if (ImGui::IsWindowAppearing()) {
123 // Reset state if needed
124 }
125
126 ImGui::Text("Entrance ID: %d", entrance.entrance_id_);
127 ImGui::Separator();
128
129 gui::InputHexWord("Map ID", &entrance.map_id_);
130 gui::InputHexByte("Entrance ID", &entrance.entrance_id_,
131 kInputFieldSize + 20);
132 gui::InputHex("X Position", &entrance.x_);
133 gui::InputHex("Y Position", &entrance.y_);
134
135 ImGui::Checkbox("Is Hole", &entrance.is_hole_);
136
137 ImGui::Separator();
138
139 if (Button("Save")) {
140 set_done = true;
141 ImGui::CloseCurrentPopup();
142 }
143 ImGui::SameLine();
144 if (Button("Delete")) {
145 entrance.deleted = true;
146 set_done = true;
147 ImGui::CloseCurrentPopup();
148 }
149 ImGui::SameLine();
150 if (Button("Cancel")) {
151 ImGui::CloseCurrentPopup();
152 }
153
154 ImGui::EndPopup();
155 }
156 return set_done;
157}
158
160 if (ImGui::BeginPopup("Exit Inserter")) {
161 static int exit_id = 0;
162 static int room_id = 0;
163 static int x_pos = 0;
164 static int y_pos = 0;
165
166 if (ImGui::IsWindowAppearing()) {
167 exit_id = 0;
168 room_id = 0;
169 x_pos = 0;
170 y_pos = 0;
171 }
172
173 ImGui::Text("Insert New Exit");
174 ImGui::Separator();
175
176 gui::InputHex("Exit ID", &exit_id);
177 gui::InputHex("Room ID", &room_id);
178 gui::InputHex("X Position", &x_pos);
179 gui::InputHex("Y Position", &y_pos);
180
181 if (Button("Create Exit")) {
182 // This would need to be connected to the overworld editor to actually
183 // create the exit
184 ImGui::CloseCurrentPopup();
185 }
186
187 SameLine();
188 if (Button("Cancel")) {
189 ImGui::CloseCurrentPopup();
190 }
191
192 ImGui::EndPopup();
193 }
194}
195
197 static bool set_done = false;
198 if (set_done) {
199 set_done = false;
200 return true;
201 }
202 if (ImGui::BeginPopupModal(
203 gui::MakePopupId(gui::EditorNames::kOverworld, "Exit Editor").c_str(),
204 NULL, ImGuiWindowFlags_AlwaysAutoResize)) {
205 // Normal door: None = 0, Wooden = 1, Bombable = 2
206 static int doorType = 0;
207 // Fancy door: None = 0, Sanctuary = 1, Palace = 2
208 static int fancyDoorType = 0;
209
210 static int xPos = 0;
211 static int yPos = 0;
212
213 // Special overworld exit properties
214 static int centerY = 0;
215 static int centerX = 0;
216 static int unk1 = 0;
217 static int unk2 = 0;
218 static int linkPosture = 0;
219 static int spriteGFX = 0;
220 static int bgGFX = 0;
221 static int palette = 0;
222 static int sprPal = 0;
223 static int top = 0;
224 static int bottom = 0;
225 static int left = 0;
226 static int right = 0;
227 static int leftEdgeOfMap = 0;
228 static bool special_exit = false;
229 static bool show_properties = false;
230
231 if (ImGui::IsWindowAppearing()) {
232 // Reset state from entity
233 doorType = exit.door_type_1_;
234 fancyDoorType = exit.door_type_2_;
235 xPos = 0; // Unknown mapping
236 yPos = 0; // Unknown mapping
237
238 // Reset other static vars to avoid pollution
239 centerY = 0; centerX = 0; unk1 = 0; unk2 = 0;
240 linkPosture = 0; spriteGFX = 0; bgGFX = 0;
241 palette = 0; sprPal = 0; top = 0; bottom = 0;
242 left = 0; right = 0; leftEdgeOfMap = 0;
243 special_exit = false;
244 show_properties = false;
245 }
246
247 gui::InputHexWord("Room", &exit.room_id_);
248 SameLine();
249 gui::InputHex("Entity ID", &exit.entity_id_, 4);
250 gui::InputHexWord("Map", &exit.map_id_);
251 SameLine();
252 Checkbox("Automatic", &exit.is_automatic_);
253
254 gui::InputHex("X Positon", &exit.x_);
255 SameLine();
256 gui::InputHex("Y Position", &exit.y_);
257
258 gui::InputHexWord("X Camera", &exit.x_camera_);
259 SameLine();
260 gui::InputHexWord("Y Camera", &exit.y_camera_);
261
262 gui::InputHexWord("X Scroll", &exit.x_scroll_);
263 SameLine();
264 gui::InputHexWord("Y Scroll", &exit.y_scroll_);
265
266 ImGui::Separator();
267
268 Checkbox("Show properties", &show_properties);
269 if (show_properties) {
270 Text("Deleted? %s", exit.deleted_ ? "true" : "false");
271 Text("Hole? %s", exit.is_hole_ ? "true" : "false");
272 Text("Auto-calc scroll/camera? %s",
273 exit.is_automatic_ ? "true" : "false");
274 Text("Map ID: 0x%02X", exit.map_id_);
275 Text("Game coords: (%d, %d)", exit.game_x_, exit.game_y_);
276 }
277
278 gui::TextWithSeparators("Unimplemented below");
279
280 if (ImGui::RadioButton("None", &doorType, 0)) exit.door_type_1_ = doorType;
281 SameLine();
282 if (ImGui::RadioButton("Wooden", &doorType, 1)) exit.door_type_1_ = doorType;
283 SameLine();
284 if (ImGui::RadioButton("Bombable", &doorType, 2)) exit.door_type_1_ = doorType;
285
286 // If door type is not None, input positions
287 if (doorType != 0) {
288 gui::InputHex("Door X pos", &xPos);
289 gui::InputHex("Door Y pos", &yPos);
290 }
291
292 if (ImGui::RadioButton("None##Fancy", &fancyDoorType, 0)) exit.door_type_2_ = fancyDoorType;
293 SameLine();
294 if (ImGui::RadioButton("Sanctuary", &fancyDoorType, 1)) exit.door_type_2_ = fancyDoorType;
295 SameLine();
296 if (ImGui::RadioButton("Palace", &fancyDoorType, 2)) exit.door_type_2_ = fancyDoorType;
297
298 // If fancy door type is not None, input positions
299 if (fancyDoorType != 0) {
300 // Placeholder for fancy door's X position
301 gui::InputHex("Fancy Door X pos", &xPos);
302 // Placeholder for fancy door's Y position
303 gui::InputHex("Fancy Door Y pos", &yPos);
304 }
305
306 Checkbox("Special exit", &special_exit);
307 if (special_exit) {
308 gui::InputHex("Center X", &centerX);
309
310 gui::InputHex("Center Y", &centerY);
311 gui::InputHex("Unk1", &unk1);
312 gui::InputHex("Unk2", &unk2);
313
314 gui::InputHex("Link's posture", &linkPosture);
315 gui::InputHex("Sprite GFX", &spriteGFX);
316 gui::InputHex("BG GFX", &bgGFX);
317 gui::InputHex("Palette", &palette);
318 gui::InputHex("Spr Pal", &sprPal);
319
320 gui::InputHex("Top", &top);
321 gui::InputHex("Bottom", &bottom);
322 gui::InputHex("Left", &left);
323 gui::InputHex("Right", &right);
324
325 gui::InputHex("Left edge of map", &leftEdgeOfMap);
326 }
327
328 if (Button(ICON_MD_DONE)) {
329 set_done = true; // FIX: Save changes when Done is clicked
330 ImGui::CloseCurrentPopup();
331 }
332
333 SameLine();
334
335 if (Button(ICON_MD_CANCEL)) {
336 // FIX: Discard changes - don't set set_done
337 ImGui::CloseCurrentPopup();
338 }
339
340 SameLine();
341 if (Button(ICON_MD_DELETE)) {
342 exit.deleted_ = true;
343 set_done = true; // FIX: Save deletion when Delete is clicked
344 ImGui::CloseCurrentPopup();
345 }
346
347 ImGui::EndPopup();
348 }
349
350 return set_done;
351}
352
354 // Contents of the Context Menu
355 if (ImGui::BeginPopup("Item Inserter")) {
356 static size_t new_item_id = 0;
357 Text("Add Item");
358 BeginChild("ScrollRegion", ImVec2(150, 150), true,
359 ImGuiWindowFlags_AlwaysVerticalScrollbar);
360 for (size_t i = 0; i < zelda3::kSecretItemNames.size(); i++) {
361 if (Selectable(zelda3::kSecretItemNames[i].c_str(), i == new_item_id)) {
362 new_item_id = i;
363 }
364 }
365 EndChild();
366
367 if (Button(ICON_MD_DONE)) {
368 // Add the new item to the overworld
369 new_item_id = 0;
370 ImGui::CloseCurrentPopup();
371 }
372 SameLine();
373
374 if (Button(ICON_MD_CANCEL)) {
375 ImGui::CloseCurrentPopup();
376 }
377
378 ImGui::EndPopup();
379 }
380}
381
382// TODO: Implement deleting OverworldItem objects, currently only hides them
384 bool set_done = false;
385 if (ImGui::BeginPopupModal(
386 gui::MakePopupId(gui::EditorNames::kOverworld, "Item Editor").c_str(),
387 NULL, ImGuiWindowFlags_AlwaysAutoResize)) {
388 BeginChild("ScrollRegion", ImVec2(150, 150), true,
389 ImGuiWindowFlags_AlwaysVerticalScrollbar);
390 ImGui::BeginGroup();
391 for (size_t i = 0; i < zelda3::kSecretItemNames.size(); i++) {
392 if (Selectable(zelda3::kSecretItemNames[i].c_str(), item.id_ == i)) {
393 item.id_ = i;
394 item.entity_id_ = i;
395 item.UpdateMapProperties(item.map_id_, nullptr);
396 }
397 }
398 ImGui::EndGroup();
399 EndChild();
400
401 if (Button(ICON_MD_DONE)) {
402 set_done = true;
403 ImGui::CloseCurrentPopup();
404 }
405 SameLine();
406 if (Button(ICON_MD_CLOSE)) {
407 ImGui::CloseCurrentPopup();
408 }
409 SameLine();
410 if (Button(ICON_MD_DELETE)) {
411 item.deleted = true;
412 set_done = true;
413 ImGui::CloseCurrentPopup();
414 }
415
416 ImGui::EndPopup();
417 }
418 return set_done;
419}
420
421const ImGuiTableSortSpecs* SpriteItem::s_current_sort_specs = nullptr;
422
423void DrawSpriteTable(std::function<void(int)> onSpriteSelect, int& selected_id) {
424 static ImGuiTextFilter filter;
425 static std::vector<SpriteItem> items;
426
427 // Initialize items if empty
428 if (items.empty()) {
429 for (int i = 0; i < 256; ++i) {
430 items.push_back(SpriteItem{i, zelda3::kSpriteDefaultNames[i].data()});
431 }
432 }
433
434 filter.Draw("Filter", 180);
435
436 if (ImGui::BeginTable("##sprites", 2,
437 ImGuiTableFlags_Sortable | ImGuiTableFlags_Resizable)) {
438 ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_DefaultSort, 0.0f,
440 ImGui::TableSetupColumn("Name", 0, 0.0f, SpriteItemColumnID_Name);
441 ImGui::TableHeadersRow();
442
443 // Handle sorting
444 if (ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs()) {
445 if (sort_specs->SpecsDirty) {
446 SpriteItem::SortWithSortSpecs(sort_specs, items);
447 sort_specs->SpecsDirty = false;
448 }
449 }
450
451 // Display filtered and sorted items
452 for (const auto& item : items) {
453 if (filter.PassFilter(item.name)) {
454 ImGui::TableNextRow();
455 ImGui::TableSetColumnIndex(0);
456 Text("%d", item.id);
457 ImGui::TableSetColumnIndex(1);
458
459 if (Selectable(item.name, selected_id == item.id,
460 ImGuiSelectableFlags_SpanAllColumns)) {
461 selected_id = item.id;
462 onSpriteSelect(item.id);
463 }
464 }
465 }
466 ImGui::EndTable();
467 }
468}
469
471 if (ImGui::BeginPopup("Sprite Inserter")) {
472 static int new_sprite_id = 0;
473 static int x_pos = 0;
474 static int y_pos = 0;
475
476 if (ImGui::IsWindowAppearing()) {
477 new_sprite_id = 0;
478 x_pos = 0;
479 y_pos = 0;
480 }
481
482 ImGui::Text("Add New Sprite");
483 ImGui::Separator();
484
485 BeginChild("ScrollRegion", ImVec2(250, 200), true,
486 ImGuiWindowFlags_AlwaysVerticalScrollbar);
487 DrawSpriteTable([](int selected_id) { new_sprite_id = selected_id; }, new_sprite_id);
488 EndChild();
489
490 ImGui::Separator();
491 ImGui::Text("Position:");
492 gui::InputHex("X Position", &x_pos);
493 gui::InputHex("Y Position", &y_pos);
494
495 if (Button("Add Sprite")) {
496 // This would need to be connected to the overworld editor to actually
497 // create the sprite
498 new_sprite_id = 0;
499 x_pos = 0;
500 y_pos = 0;
501 ImGui::CloseCurrentPopup();
502 }
503 SameLine();
504
505 if (Button("Cancel")) {
506 ImGui::CloseCurrentPopup();
507 }
508
509 ImGui::EndPopup();
510 }
511}
512
514 static bool set_done = false;
515 if (set_done) {
516 set_done = false;
517 return true;
518 }
519 if (ImGui::BeginPopupModal(
521 .c_str(),
522 NULL, ImGuiWindowFlags_AlwaysAutoResize)) {
523 static int selected_id = 0;
524 if (ImGui::IsWindowAppearing()) {
525 selected_id = sprite.id();
526 }
527
528 BeginChild("ScrollRegion", ImVec2(350, 350), true,
529 ImGuiWindowFlags_AlwaysVerticalScrollbar);
530 ImGui::BeginGroup();
531 Text("%s", sprite.name().c_str());
532
533 DrawSpriteTable([&sprite](int id) {
534 sprite.set_id(id);
535 sprite.UpdateMapProperties(sprite.map_id(), nullptr);
536 }, selected_id);
537 ImGui::EndGroup();
538 EndChild();
539
540 if (Button(ICON_MD_DONE)) {
541 set_done = true; // FIX: Save changes when Done is clicked
542 ImGui::CloseCurrentPopup();
543 }
544 SameLine();
545 if (Button(ICON_MD_CLOSE)) {
546 // FIX: Discard changes - don't set set_done
547 ImGui::CloseCurrentPopup();
548 }
549 SameLine();
550 if (Button(ICON_MD_DELETE)) {
551 sprite.set_deleted(true);
552 set_done = true; // FIX: Save deletion when Delete is clicked
553 ImGui::CloseCurrentPopup();
554 }
555
556 ImGui::EndPopup();
557 }
558 return set_done;
559}
560
562 zelda3::DiggableTiles* diggable_tiles,
563 const std::vector<gfx::Tile16>& tiles16,
564 const std::array<uint8_t, 0x200>& all_tiles_types) {
565 static bool set_done = false;
566 if (set_done) {
567 set_done = false;
568 return true;
569 }
570
571 if (ImGui::BeginPopupModal(
572 gui::MakePopupId(gui::EditorNames::kOverworld, "Diggable Tiles Editor")
573 .c_str(),
574 NULL, ImGuiWindowFlags_AlwaysAutoResize)) {
575 static ImGuiTextFilter filter;
576 static int patch_mode = 0; // 0=Vanilla, 1=ZS Compatible, 2=Custom
577 static zelda3::DiggableTilesPatchConfig patch_config;
578
579 // Stats header
580 int diggable_count = diggable_tiles->GetDiggableCount();
581 Text("Diggable Tiles: %d / 512", diggable_count);
582 ImGui::Separator();
583
584 // Filter
585 filter.Draw("Filter by Tile ID", 200);
586 SameLine();
587 if (Button("Clear Filter")) {
588 filter.Clear();
589 }
590
591 // Scrollable tile list
592 BeginChild("TileList", ImVec2(400, 300), true,
593 ImGuiWindowFlags_AlwaysVerticalScrollbar);
594
595 // Display tiles in a grid-like format
596 int cols = 8;
597 int col = 0;
598 for (uint16_t tile_id = 0;
599 tile_id < zelda3::kMaxDiggableTileId && tile_id < tiles16.size();
600 ++tile_id) {
601 char id_str[16];
602 snprintf(id_str, sizeof(id_str), "$%03X", tile_id);
603
604 if (!filter.PassFilter(id_str)) {
605 continue;
606 }
607
608 bool is_diggable = diggable_tiles->IsDiggable(tile_id);
609 bool would_be_diggable = zelda3::DiggableTiles::IsTile16Diggable(
610 tiles16[tile_id], all_tiles_types);
611
612 // Color coding: green if auto-detected, yellow if manually set
613 std::optional<gui::StyleColorGuard> dig_color;
614 if (is_diggable) {
615 if (would_be_diggable) {
616 dig_color.emplace(ImGuiCol_Text, ImVec4(0.2f, 0.8f, 0.2f, 1.0f));
617 } else {
618 dig_color.emplace(ImGuiCol_Text, ImVec4(0.8f, 0.8f, 0.2f, 1.0f));
619 }
620 }
621
622 if (Checkbox(id_str, &is_diggable)) {
623 diggable_tiles->SetDiggable(tile_id, is_diggable);
624 }
625
626 dig_color.reset();
627
628 if (ImGui::IsItemHovered()) {
629 ImGui::SetTooltip("Tile $%03X - %s",
630 tile_id,
631 would_be_diggable ? "Auto-detected as diggable"
632 : "Manually configured");
633 }
634
635 col++;
636 if (col < cols) {
637 SameLine();
638 } else {
639 col = 0;
640 }
641 }
642
643 EndChild();
644
645 ImGui::Separator();
646
647 // Action buttons
648 if (Button(ICON_MD_AUTO_FIX_HIGH " Auto-Detect")) {
649 diggable_tiles->Clear();
650 for (uint16_t tile_id = 0;
651 tile_id < zelda3::kMaxDiggableTileId && tile_id < tiles16.size();
652 ++tile_id) {
653 if (zelda3::DiggableTiles::IsTile16Diggable(tiles16[tile_id],
654 all_tiles_types)) {
655 diggable_tiles->SetDiggable(tile_id, true);
656 }
657 }
658 }
659 if (ImGui::IsItemHovered()) {
660 ImGui::SetTooltip(
661 "Set diggable status based on tile types.\n"
662 "A tile is diggable if all 4 component tiles\n"
663 "have type 0x48 or 0x4A (diggable ground).");
664 }
665
666 SameLine();
667 if (Button(ICON_MD_RESTART_ALT " Vanilla Defaults")) {
668 diggable_tiles->SetVanillaDefaults();
669 }
670 if (ImGui::IsItemHovered()) {
671 ImGui::SetTooltip("Reset to vanilla diggable tiles:\n$034, $035, $071, "
672 "$0DA, $0E1, $0E2, $0F8, $10D, $10E, $10F");
673 }
674
675 SameLine();
676 if (Button(ICON_MD_CLEAR " Clear All")) {
677 diggable_tiles->Clear();
678 }
679
680 ImGui::Separator();
681
682 // Patch export section
683 if (ImGui::CollapsingHeader("ASM Patch Export")) {
684 ImGui::Indent();
685
686 Text("Patch Mode:");
687 ImGui::RadioButton("Vanilla", &patch_mode, 0);
688 SameLine();
689 ImGui::RadioButton("ZS Compatible", &patch_mode, 1);
690 SameLine();
691 ImGui::RadioButton("Custom", &patch_mode, 2);
692
693 patch_config.use_zs_compatible_mode = (patch_mode == 1);
694
695 if (patch_mode == 2) {
696 // Custom address inputs
697 static int hook_addr = patch_config.hook_address;
698 static int table_addr = patch_config.table_address;
699 static int freespace_addr = patch_config.freespace_address;
700
701 gui::InputHex("Hook Address", &hook_addr);
702 gui::InputHex("Table Address", &table_addr);
703 gui::InputHex("Freespace", &freespace_addr);
704
705 patch_config.hook_address = hook_addr;
706 patch_config.table_address = table_addr;
707 patch_config.freespace_address = freespace_addr;
708 }
709
710 if (Button(ICON_MD_FILE_DOWNLOAD " Export .asm Patch")) {
711 // TODO: Open file dialog and export
712 // For now, generate to a default location
713 std::string patch_content =
715 patch_config);
716 // Would normally open a save dialog here
717 }
718
719 ImGui::Unindent();
720 }
721
722 ImGui::Separator();
723
724 // Save/Cancel buttons
725 if (Button(ICON_MD_DONE " Save")) {
726 set_done = true;
727 ImGui::CloseCurrentPopup();
728 }
729 SameLine();
730 if (Button(ICON_MD_CANCEL " Cancel")) {
731 ImGui::CloseCurrentPopup();
732 }
733
734 ImGui::EndPopup();
735 }
736
737 return set_done;
738}
739
740} // namespace editor
741} // namespace yaze
static std::string GeneratePatch(const DiggableTiles &diggable_tiles, const DiggableTilesPatchConfig &config={})
Generate ASM patch code for the diggable tiles table.
Manages diggable tile state as a 512-bit bitfield.
int GetDiggableCount() const
Get the count of tiles marked as diggable.
void SetVanillaDefaults()
Reset to vanilla diggable tiles.
void SetDiggable(uint16_t tile_id, bool diggable)
Set or clear the diggable bit for a Map16 tile ID.
static bool IsTile16Diggable(const gfx::Tile16 &tile16, const std::array< uint8_t, 0x200 > &all_tiles_types)
Check if a Tile16 should be diggable based on its component tiles.
bool IsDiggable(uint16_t tile_id) const
Check if a Map16 tile ID is marked as diggable.
void Clear()
Clear all diggable bits.
Base class for all overworld and dungeon entities.
Definition common.h:31
auto set_x(int x)
Definition common.h:62
auto set_y(int y)
Definition common.h:63
Represents an overworld exit that transitions from dungeon to overworld.
void UpdateMapProperties(uint16_t room_map_id, const void *context=nullptr) override
Update entity properties based on map position.
A class for managing sprites in the overworld and underworld.
Definition sprite.h:35
auto id() const
Definition sprite.h:95
void UpdateMapProperties(uint16_t map_id, const void *context=nullptr) override
Update entity properties based on map position.
Definition sprite.cc:324
auto map_id() const
Definition sprite.h:101
auto set_id(uint8_t id)
Definition sprite.h:96
auto set_deleted(bool deleted)
Definition sprite.h:113
#define ICON_MD_CANCEL
Definition icons.h:364
#define ICON_MD_FILE_DOWNLOAD
Definition icons.h:744
#define ICON_MD_AUTO_FIX_HIGH
Definition icons.h:218
#define ICON_MD_CLEAR
Definition icons.h:416
#define ICON_MD_DONE
Definition icons.h:607
#define ICON_MD_DELETE
Definition icons.h:530
#define ICON_MD_CLOSE
Definition icons.h:418
#define ICON_MD_RESTART_ALT
Definition icons.h:1602
void DrawExitInserterPopup()
Definition entity.cc:159
bool DrawSpriteEditorPopup(zelda3::Sprite &sprite)
Definition entity.cc:513
void DrawItemInsertPopup()
Definition entity.cc:353
bool DrawEntranceInserterPopup()
Definition entity.cc:83
void DrawSpriteInserterPopup()
Definition entity.cc:470
void DrawSpriteTable(std::function< void(int)> onSpriteSelect, int &selected_id)
Definition entity.cc:423
bool DrawOverworldEntrancePopup(zelda3::OverworldEntrance &entrance)
Definition entity.cc:111
bool DrawItemEditorPopup(zelda3::OverworldItem &item)
Definition entity.cc:383
bool DrawDiggableTilesEditorPopup(zelda3::DiggableTiles *diggable_tiles, const std::vector< gfx::Tile16 > &tiles16, const std::array< uint8_t, 0x200 > &all_tiles_types)
Draw popup dialog for editing diggable tiles configuration.
Definition entity.cc:561
void MoveEntityOnGrid(zelda3::GameEntity *entity, ImVec2 canvas_p0, ImVec2 scrolling, bool free_movement, float scale)
Move entity to grid-aligned position based on mouse.
Definition entity.cc:62
constexpr float kInputFieldSize
Definition entity.cc:24
@ SpriteItemColumnID_ID
Definition entity.h:69
@ SpriteItemColumnID_Name
Definition entity.h:70
bool DrawExitEditorPopup(zelda3::OverworldExit &exit)
Definition entity.cc:196
bool IsMouseHoveringOverEntity(const zelda3::GameEntity &entity, ImVec2 canvas_p0, ImVec2 scrolling, float scale)
Check if mouse is hovering over an entity.
Definition entity.cc:26
constexpr const char * kOverworld
Definition popup_id.h:53
bool InputHexWord(const char *label, uint16_t *data, float input_width, bool no_step)
Definition input.cc:344
bool InputHex(const char *label, uint64_t *data)
Definition input.cc:325
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
bool InputHexByte(const char *label, uint8_t *data, float input_width, bool no_step)
Definition input.cc:370
void TextWithSeparators(const absl::string_view &text)
Definition style.cc:1315
const std::vector< std::string > kSecretItemNames
const std::string kSpriteDefaultNames[256]
Definition sprite.cc:13
constexpr int kMaxDiggableTileId
static const ImGuiTableSortSpecs * s_current_sort_specs
Definition entity.h:77
static void SortWithSortSpecs(ImGuiTableSortSpecs *sort_specs, std::vector< SpriteItem > &items)
Definition entity.h:79
Configuration for diggable tiles ASM patch generation.