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