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