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