yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
dungeon_entrances_panel.h
Go to the documentation of this file.
1#ifndef YAZE_APP_EDITOR_DUNGEON_PANELS_DUNGEON_ENTRANCES_PANEL_H_
2#define YAZE_APP_EDITOR_DUNGEON_PANELS_DUNGEON_ENTRANCES_PANEL_H_
3
4#include <algorithm>
5#include <array>
6#include <cstdio>
7#include <functional>
8#include <memory>
9#include <optional>
10#include <string>
11#include <utility>
12
13#include "absl/status/status.h"
14#include "absl/strings/str_format.h"
20#include "app/gui/core/icons.h"
21#include "app/gui/core/input.h"
22#include "imgui/imgui.h"
23#include "util/i18n/tr.h"
24#include "zelda3/common.h"
27
28namespace yaze {
29namespace editor {
30
41 public:
43 std::array<zelda3::RoomEntrance, zelda3::kNumDungeonEntranceSlots>*
44 entrances,
45 std::array<zelda3::DungeonSpawnPoint, zelda3::kNumDungeonSpawnPoints>*
46 spawn_points,
47 int* current_entrance_id, std::function<void(int)> on_entrance_selected)
48 : entrances_(entrances),
49 spawn_points_(spawn_points),
50 current_entrance_id_(current_entrance_id),
51 on_entrance_selected_(std::move(on_entrance_selected)) {}
52
53 // ==========================================================================
54 // WindowContent Identity
55 // ==========================================================================
56
57 std::string GetId() const override { return "dungeon.entrance_properties"; }
58 std::string GetDisplayName() const override { return "Entrances"; }
59 std::string GetIcon() const override { return ICON_MD_DOOR_FRONT; }
60 std::string GetEditorCategory() const override { return "Dungeon"; }
61 int GetPriority() const override { return 26; }
62 std::string GetWorkflowGroup() const override { return "Core"; }
63
65 std::function<std::optional<DungeonEntranceCameraState>(int)>;
66 using CameraRepairCallback = std::function<absl::Status(int)>;
67 using EntrancePropertyEditCallback = std::function<void(
68 int, const zelda3::RoomEntrance&, const zelda3::RoomEntrance&)>;
69 using SpawnPropertyEditCallback = std::function<void(
71
73 SpawnPropertyEditCallback spawn_callback) {
74 entrance_property_edit_callback_ = std::move(entrance_callback);
75 spawn_property_edit_callback_ = std::move(spawn_callback);
76 }
77
78 std::shared_ptr<const int> property_edit_gesture() const {
80 }
81
83 CameraRepairCallback repair_callback,
84 std::function<void()> data_changed_callback = {}) {
85 camera_state_provider_ = std::move(state_provider);
86 camera_repair_callback_ = std::move(repair_callback);
87 camera_data_changed_callback_ = std::move(data_changed_callback);
88 }
90 if (!navigation_shortcut_focus_ || ImGui::GetCurrentContext() == nullptr) {
91 return false;
92 }
93 return navigation_shortcut_last_draw_frame_ >= ImGui::GetFrameCount() - 1;
94 }
95
96 void SetGraphicsPreview(std::function<DungeonRenderContext()> context,
97 std::function<bool()> automatic,
98 std::function<void(int)> select) {
99 preview_context_ = std::move(context);
100 preview_automatic_ = std::move(automatic);
101 select_preview_ = std::move(select);
102 }
103
104 // Shared preview selector: regular IDs are not the combined list slots,
105 // whose first entries are dedicated spawn points.
106 void DrawGraphicsContext(float width = -1.0f) {
108 return;
109 if (*current_entrance_id_ < 0 ||
110 *current_entrance_id_ >= static_cast<int>(entrances_->size()))
112 const int selected = *current_entrance_id_;
113 const uint8_t main_gfx = selected < zelda3::kNumDungeonSpawnPoints
114 ? (*spawn_points_)[selected].main_gfx
115 : (*entrances_)[selected].blockset_;
116 std::string preview = absl::StrFormat(
117 "%s / GFX %02X", DungeonEntrancePreviewLabel(selected), main_gfx);
118 const bool automatic = preview_automatic_ && preview_automatic_();
119 if (preview_context_) {
120 const auto context = preview_context_();
121 const std::string name =
122 automatic && !context.owner_name.empty() ? context.owner_name
123 : context.entrance_slot >= zelda3::kNumDungeonSpawnPoints
124 ? zelda3::GetEntranceLabel(context.entrance_slot -
126 : DungeonEntrancePreviewLabel(context.entrance_slot);
127 preview =
128 context.uses_entrance()
129 ? absl::StrFormat(
130 "%s: %s / %s", automatic ? "Auto" : "Manual", name,
131 DungeonEntrancePreviewLabel(context.entrance_slot))
132 : absl::StrFormat(
133 "Auto: %s / GFX %02X",
135 ? "Choose entrance"
136 : "Room fallback",
137 context.effective_blockset());
138 }
139 ImGui::SetNextItemWidth(width);
140 const bool open = ImGui::BeginCombo(
141 "##GraphicsEntrance", preview.c_str(),
142 select_preview_ ? ImGuiComboFlags_HeightLarge : ImGuiComboFlags_None);
143 gui::AutoRegisterLastItem("combo", "Graphics entrance");
144 if (ImGui::IsItemHovered())
145 ImGui::SetTooltip(
146 "Auto follows the room's dungeon. Choosing an entrance pins its "
147 "graphics until you choose Auto again. This changes only the "
148 "preview.");
149 if (open) {
150 if (select_preview_) {
151 if (ImGui::Selectable("Auto (follow room)", automatic))
152 select_preview_(-1);
153 gui::AutoRegisterLastItem("selectable", "Auto (follow room)");
154 ImGui::Separator();
155 // Keep Auto visible while the entrance list scrolls to a manual choice.
156 ImGui::BeginChild("##EntranceChoices",
157 ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 8));
158 }
159 for (int slot = 0; slot < static_cast<int>(entrances_->size()); ++slot) {
160 const std::string entry = DungeonEntrancePreviewLabel(slot);
161 const std::string label =
163 ? entry + " - " +
166 : entry;
167 if (ImGui::Selectable(label.c_str(), !automatic && slot == selected)) {
168 if (select_preview_) {
169 select_preview_(slot);
170 ImGui::CloseCurrentPopup();
171 } else {
172 *current_entrance_id_ = slot;
175 }
176 }
177 gui::AutoRegisterLastItem("selectable", entry);
178 if (ImGui::IsItemHovered() && slot >= zelda3::kNumDungeonSpawnPoints)
179 ImGui::SetTooltip("%s", zelda3::GetEntranceLabel(
181 .c_str());
182 if (slot == selected) {
183 ImGui::SetItemDefaultFocus();
184 if (select_preview_ && ImGui::IsWindowAppearing())
185 ImGui::SetScrollHereY(0.5f);
186 }
187 }
188 if (select_preview_)
189 ImGui::EndChild();
190 ImGui::EndCombo();
191 }
192 }
193
196 !preview_context_().uses_entrance()) {
197 ImGui::TextWrapped("Choose an entrance to edit its properties.");
198 return;
199 }
201 *current_entrance_id_ >= 0 &&
202 *current_entrance_id_ < static_cast<int>(entrances_->size()))
204 }
205
206 // ==========================================================================
207 // WindowContent Drawing
208 // ==========================================================================
209
210 void Draw(bool* p_open) override {
211 (void)p_open;
213 return;
214 if (*current_entrance_id_ < 0 ||
215 *current_entrance_id_ >= static_cast<int>(entrances_->size())) {
217 }
218 navigation_shortcut_last_draw_frame_ = ImGui::GetFrameCount();
220 ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows);
221
222 const bool split_layout = ImGui::GetContentRegionAvail().x >= 620.0f;
223 if (split_layout &&
224 ImGui::BeginTable("##EntranceNavigator", 2,
225 ImGuiTableFlags_Resizable |
226 ImGuiTableFlags_BordersInnerV |
227 ImGuiTableFlags_SizingStretchProp)) {
228 ImGui::TableSetupColumn("Entrances", ImGuiTableColumnFlags_WidthFixed,
229 300.0f);
230 ImGui::TableSetupColumn("Properties", ImGuiTableColumnFlags_WidthStretch);
231 ImGui::TableNextRow();
232 ImGui::TableNextColumn();
234 ImGui::TableNextColumn();
236 ImGui::EndTable();
237 return;
238 }
239
240 if (ImGui::BeginTabBar("##EntranceNavigatorTabs")) {
241 if (ImGui::BeginTabItem(ICON_MD_DOOR_FRONT " Entrances")) {
243 ImGui::EndTabItem();
244 }
245 if (ImGui::BeginTabItem(ICON_MD_TUNE " Properties")) {
247 ImGui::EndTabItem();
248 }
249 ImGui::EndTabBar();
250 }
251 }
252
253 private:
255 if (!camera_repair_preview_.has_value()) {
256 return;
257 }
258 const auto validation =
260 if (!ImGui::BeginPopupModal("Camera Repair Preview##DungeonEntrance",
261 nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
262 return;
263 }
264
265 ImGui::TextWrapped(
266 "Review the derived camera values before changing this entrance. "
267 "The repair is one undoable action.");
268 ImGui::Separator();
269 if (ImGui::BeginTable(
270 "##CameraRepairValues", 3,
271 ImGuiTableFlags_BordersInner | ImGuiTableFlags_SizingFixedFit)) {
272 ImGui::TableSetupColumn("Field");
273 ImGui::TableSetupColumn("Current");
274 ImGui::TableSetupColumn("Derived");
275 ImGui::TableHeadersRow();
276 const auto draw_word_row = [](const char* label, uint16_t current,
277 uint16_t derived) {
278 ImGui::TableNextRow();
279 ImGui::TableNextColumn();
280 ImGui::TextUnformatted(label);
281 ImGui::TableNextColumn();
282 ImGui::Text("0x%04X", current);
283 ImGui::TableNextColumn();
284 ImGui::Text("0x%04X", derived);
285 };
286 draw_word_row("Scroll X", camera_repair_preview_->camera_x,
287 validation.expected.camera_x);
288 draw_word_row("Scroll Y", camera_repair_preview_->camera_y,
289 validation.expected.camera_y);
290 draw_word_row("Trigger X", camera_repair_preview_->trigger_x,
291 validation.expected.trigger_x);
292 draw_word_row("Trigger Y", camera_repair_preview_->trigger_y,
293 validation.expected.trigger_y);
294 ImGui::TableNextRow();
295 ImGui::TableNextColumn();
296 ImGui::TextUnformatted("Quadrant");
297 ImGui::TableNextColumn();
298 ImGui::Text("0x%02X", camera_repair_preview_->quadrant);
299 ImGui::TableNextColumn();
300 if (validation.quadrant_repair_safe) {
301 ImGui::Text("0x%02X", validation.expected.quadrant);
302 } else {
303 ImGui::TextUnformatted("Preserved");
304 }
305 ImGui::EndTable();
306 }
307 ImGui::TextWrapped(
308 "Boundary pages: [%02X %02X %02X %02X | %02X %02X %02X %02X] "
309 "-> [%02X %02X %02X %02X | %02X %02X %02X %02X]",
310 camera_repair_preview_->boundaries[0],
311 camera_repair_preview_->boundaries[1],
312 camera_repair_preview_->boundaries[2],
313 camera_repair_preview_->boundaries[3],
314 camera_repair_preview_->boundaries[4],
315 camera_repair_preview_->boundaries[5],
316 camera_repair_preview_->boundaries[6],
317 camera_repair_preview_->boundaries[7],
318 validation.expected.boundaries[0], validation.expected.boundaries[1],
319 validation.expected.boundaries[2], validation.expected.boundaries[3],
320 validation.expected.boundaries[4], validation.expected.boundaries[5],
321 validation.expected.boundaries[6], validation.expected.boundaries[7]);
322 if (!camera_repair_error_.empty()) {
323 ImGui::TextWrapped("Repair failed: %s", camera_repair_error_.c_str());
324 }
325
326 ImGui::BeginDisabled(!validation.can_repair() || !camera_repair_callback_);
327 if (ImGui::Button("Apply Safe Repair")) {
328 const absl::Status status =
331 : absl::FailedPreconditionError(
332 "Camera repair callback is unavailable");
333 if (status.ok()) {
336 camera_repair_error_.clear();
337 ImGui::CloseCurrentPopup();
338 } else {
339 camera_repair_error_ = std::string(status.message());
340 }
341 }
342 gui::AutoRegisterLastItem("button", "ApplyCameraRepair");
343 ImGui::EndDisabled();
344 ImGui::SameLine();
345 if (ImGui::Button("Cancel")) {
348 camera_repair_error_.clear();
349 ImGui::CloseCurrentPopup();
350 }
351 ImGui::EndPopup();
352 }
353
354 void DrawCameraSafety(int slot_index) {
356 return;
357 }
358 const auto state = camera_state_provider_(slot_index);
359 if (!state.has_value()) {
360 ImGui::TextDisabled("Camera validation unavailable.");
361 return;
362 }
363 const auto validation = ValidateDungeonEntranceCamera(*state);
364 ImGui::SeparatorText("Camera safety");
365 if (!validation.geometry_valid()) {
366 ImGui::TextColored(ImVec4(0.95f, 0.35f, 0.30f, 1.0f),
367 "Unsafe camera geometry");
368 for (const auto& error : validation.errors) {
369 ImGui::BulletText("%s", error.c_str());
370 }
371 if (validation.can_repair()) {
372 ImGui::TextWrapped(
373 "The room and player position are valid, so a previewed repair "
374 "can replace these derived fields.");
375 }
376 } else if (validation.matches_derived()) {
377 ImGui::TextColored(ImVec4(0.35f, 0.80f, 0.45f, 1.0f),
378 "Camera values match the player position.");
379 } else {
380 ImGui::TextWrapped(
381 "Camera geometry is safe, but %zu derived value group(s) differ.",
382 validation.differences.size());
383 for (const auto& difference : validation.differences) {
384 ImGui::BulletText("%s", difference.c_str());
385 }
386 }
387
388 ImGui::BeginDisabled(!validation.can_repair() || !camera_repair_callback_);
389 if (ImGui::Button("Preview Camera Repair")) {
390 camera_repair_preview_ = *state;
391 camera_repair_slot_ = slot_index;
392 camera_repair_error_.clear();
393 ImGui::OpenPopup("Camera Repair Preview##DungeonEntrance");
394 }
395 gui::AutoRegisterLastItem("button", "PreviewCameraRepair");
396 ImGui::EndDisabled();
398 }
399
411
412 bool DrawCameraBoundaries(const char* table_id,
413 const std::array<uint8_t*, 8>& boundaries) {
414 bool changed = false;
415 if (!ImGui::BeginTable(table_id, 5,
416 ImGuiTableFlags_BordersInnerV |
417 ImGuiTableFlags_SizingStretchSame)) {
418 return false;
419 }
420 ImGui::TableSetupColumn("Mode", ImGuiTableColumnFlags_WidthFixed, 70.0f);
421 ImGui::TableSetupColumn("North");
422 ImGui::TableSetupColumn("East");
423 ImGui::TableSetupColumn("South");
424 ImGui::TableSetupColumn("West");
425 ImGui::TableHeadersRow();
426 constexpr std::array<int, 4> kQuadrantOrder = {0, 6, 2, 4};
427 constexpr std::array<int, 4> kFullRoomOrder = {1, 7, 3, 5};
428 const auto draw_row = [&changed, &boundaries](
429 const char* label,
430 const std::array<int, 4>& order) {
431 ImGui::TableNextRow();
432 ImGui::TableNextColumn();
433 ImGui::TextUnformatted(label);
434 for (const int index : order) {
435 ImGui::TableNextColumn();
436 ImGui::PushID(index);
437 changed |= gui::InputHexByte(
438 "##Boundary", boundaries[index],
439 std::max(36.0f, ImGui::GetContentRegionAvail().x), true);
440 ImGui::PopID();
441 }
442 };
443 draw_row("Quadrant", kQuadrantOrder);
444 draw_row("Full room", kFullRoomOrder);
445 ImGui::EndTable();
446 return changed;
447 }
448
450 entrance_filter_.Draw(ICON_MD_SEARCH " Filter",
451 ImGui::GetContentRegionAvail().x);
452 constexpr int kNumSpawnPoints = zelda3::kNumDungeonSpawnPoints;
453 constexpr int kNumEntrances = zelda3::kNumRegularDungeonEntrances;
454 constexpr int kTotalEntries = zelda3::kNumDungeonEntranceSlots;
455
456 if (ImGui::BeginChild("##EntrancesList", ImVec2(0, 0), true,
457 ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
458 for (int i = 0; i < kTotalEntries; i++) {
459 std::string entrance_name;
460 if (i < kNumSpawnPoints) {
461 entrance_name = absl::StrFormat("Spawn Point %d", i);
462 } else {
463 const int entrance_id = i - kNumSpawnPoints;
464 entrance_name = entrance_id < kNumEntrances
465 ? zelda3::GetEntranceLabel(entrance_id)
466 : absl::StrFormat("Unknown %d", i);
467 }
468
469 const int room_id = i < kNumSpawnPoints ? (*spawn_points_)[i].room_id
470 : (*entrances_)[i].room_;
471 const std::string room_name = zelda3::GetRoomLabel(room_id);
472 const std::string label = absl::StrFormat(
473 "[%02X] %s -> %s (%03X)", i, entrance_name, room_name, room_id);
474 if (!entrance_filter_.PassFilter(label.c_str())) {
475 continue;
476 }
477
478 const bool is_selected = (*current_entrance_id_ == i);
479 if (ImGui::Selectable(label.c_str(), is_selected)) {
483 }
484 }
485 }
486 }
487 ImGui::EndChild();
488 }
489
490 void DrawSpawnPointProperties(int slot_index) {
491 auto& spawn = (*spawn_points_)[slot_index];
492 const auto before = spawn;
493 const bool properties_editable =
494 CanEditDungeonSpawnPoint(slot_index, spawn);
495 bool changed = false;
496
497 ImGui::Text(tr("Spawn Point %d"), slot_index);
498 if (!properties_editable) {
499 ImGui::TextWrapped(tr(
500 "Spawn point data is unavailable; reload the ROM before editing."));
501 }
502 ImGui::BeginDisabled(!properties_editable);
503
504 changed |= gui::InputHexWord("Entrance ID", &spawn.entrance_id);
505 changed |= gui::InputHexWord("Room ID", &spawn.room_id);
506 RegisterPropertyInput("Room ID", "SpawnRoomId");
507 ImGui::SameLine();
508 changed |= gui::InputHexByte("Dungeon ID", &spawn.dungeon_id, 50.f, true);
509
510 changed |= gui::InputHexByte("Main GFX", &spawn.main_gfx, 50.f, true);
511 RegisterPropertyInput("Main GFX", "SpawnMainGfx");
512 ImGui::SameLine();
513 changed |= gui::InputHexByte("Song", &spawn.song, 50.f, true);
514 ImGui::SameLine();
515 changed |= gui::InputHexByte("Floor", &spawn.floor);
516
517 ImGui::Separator();
518
519 changed |= gui::InputHexWord("Player X", &spawn.x_coordinate);
520 RegisterPropertyInput("Player X", "SpawnPlayerX");
521 ImGui::SameLine();
522 changed |= gui::InputHexWord("Player Y", &spawn.y_coordinate);
523 RegisterPropertyInput("Player Y", "SpawnPlayerY");
524
525 changed |= gui::InputHexWord("Overworld Door Tilemap",
526 &spawn.overworld_door_tilemap, 70.f, true);
527 ImGui::EndDisabled();
528
529 CommitSpawnPropertyEdit(slot_index, before, changed);
530 DrawCameraSafety(slot_index);
531
532 const auto camera_before = spawn;
533 changed = false;
534 const bool camera_open = ImGui::CollapsingHeader("Advanced camera data");
535 gui::AutoRegisterLastItem("header", "AdvancedCameraData");
536 if (camera_open) {
537 ImGui::BeginDisabled(!properties_editable);
538 changed |= gui::InputHexWord("Camera Trigger X", &spawn.camera_trigger_x);
539 RegisterPropertyInput("Camera Trigger X", "SpawnCameraTriggerX");
540 ImGui::SameLine();
541 changed |= gui::InputHexWord("Camera Trigger Y", &spawn.camera_trigger_y);
542 RegisterPropertyInput("Camera Trigger Y", "SpawnCameraTriggerY");
543
544 changed |=
545 gui::InputHexWord("Horizontal Scroll", &spawn.horizontal_scroll);
546 ImGui::SameLine();
547 changed |= gui::InputHexWord("Vertical Scroll", &spawn.vertical_scroll);
548
549 changed |= gui::InputHexByte("Layer", &spawn.layer, 50.f, true);
550 ImGui::SameLine();
551 changed |=
552 gui::InputHexByte("Spawn Quadrant", &spawn.quadrant, 50.f, true);
553 ImGui::SameLine();
554 changed |= gui::InputHexByte("Scroll Controller",
555 &spawn.camera_scroll_controller, 50.f, true);
556
557 ImGui::SeparatorText(tr("Camera Boundaries"));
558 changed |= DrawCameraBoundaries("##SpawnCameraBoundaries",
559 {&spawn.camera_scroll_boundaries[0],
560 &spawn.camera_scroll_boundaries[1],
561 &spawn.camera_scroll_boundaries[2],
562 &spawn.camera_scroll_boundaries[3],
563 &spawn.camera_scroll_boundaries[4],
564 &spawn.camera_scroll_boundaries[5],
565 &spawn.camera_scroll_boundaries[6],
566 &spawn.camera_scroll_boundaries[7]});
567 ImGui::EndDisabled();
568 }
569
570 CommitSpawnPropertyEdit(slot_index, camera_before, changed);
571 }
572
573 void DrawRegularEntranceProperties(int slot_index) {
574 auto& entrance = (*entrances_)[slot_index];
575 const auto before = entrance;
576 const bool properties_editable =
577 CanEditDungeonEntrance(slot_index, entrance);
578 bool changed = false;
579
580 ImGui::Text(tr("Entrance ID: %04X"), entrance.entrance_id_);
581 ImGui::BeginDisabled(!properties_editable);
582 changed |= gui::InputHexWord("Room ID", &entrance.room_);
583 RegisterPropertyInput("Room ID", "RegularRoomId");
584 ImGui::SameLine();
585 changed |=
586 gui::InputHexByte("Dungeon ID", &entrance.dungeon_id_, 50.f, true);
587
588 changed |= gui::InputHexByte("Blockset", &entrance.blockset_, 50.f, true);
589 RegisterPropertyInput("Blockset", "RegularBlockset");
590 ImGui::SameLine();
591 changed |= gui::InputHexByte("Music", &entrance.music_, 50.f, true);
592 ImGui::SameLine();
593 changed |= gui::InputHexByte("Floor", &entrance.floor_);
594
595 ImGui::Separator();
596
597 changed |= gui::InputHexWord("Player X ", &entrance.x_position_);
598 RegisterPropertyInput("Player X ", "RegularPlayerX");
599 ImGui::SameLine();
600 changed |= gui::InputHexWord("Player Y ", &entrance.y_position_);
601 RegisterPropertyInput("Player Y ", "RegularPlayerY");
602
603 changed |= gui::InputHexWord("Exit", &entrance.exit_, 50.f, true);
604 ImGui::EndDisabled();
605
606 CommitEntrancePropertyEdit(slot_index, before, changed);
607 DrawCameraSafety(slot_index);
608
609 const auto camera_before = entrance;
610 changed = false;
611 const bool camera_open = ImGui::CollapsingHeader("Advanced camera data");
612 gui::AutoRegisterLastItem("header", "AdvancedCameraData");
613 if (camera_open) {
614 ImGui::BeginDisabled(!properties_editable);
615 changed |=
616 gui::InputHexWord("Camera Trigger X", &entrance.camera_trigger_x_);
617 RegisterPropertyInput("Camera Trigger X", "RegularCameraTriggerX");
618 ImGui::SameLine();
619 changed |=
620 gui::InputHexWord("Camera Trigger Y", &entrance.camera_trigger_y_);
621 RegisterPropertyInput("Camera Trigger Y", "RegularCameraTriggerY");
622
623 changed |= gui::InputHexWord("Scroll X", &entrance.camera_x_);
624 ImGui::SameLine();
625 changed |= gui::InputHexWord("Scroll Y", &entrance.camera_y_);
626 changed |= gui::InputHexByte("Scroll Quadrant",
627 &entrance.scroll_quadrant_, 50.f, true);
628
629 ImGui::SeparatorText(tr("Camera Boundaries"));
630 changed |= DrawCameraBoundaries(
631 "##RegularCameraBoundaries",
632 {&entrance.camera_boundary_qn_, &entrance.camera_boundary_fn_,
633 &entrance.camera_boundary_qs_, &entrance.camera_boundary_fs_,
634 &entrance.camera_boundary_qw_, &entrance.camera_boundary_fw_,
635 &entrance.camera_boundary_qe_, &entrance.camera_boundary_fe_});
636 ImGui::EndDisabled();
637 }
638
639 CommitEntrancePropertyEdit(slot_index, camera_before, changed);
640 }
641
642 void CommitEntrancePropertyEdit(int slot_index,
643 const zelda3::RoomEntrance& before,
644 bool changed) {
645 auto& entrance = (*entrances_)[slot_index];
646 if (!MarkDungeonEntranceDirtyIfEditable(slot_index, entrance, changed)) {
647 return;
648 }
651 entrance_property_edit_callback_(slot_index, before, entrance);
652 }
655 }
656 }
657
658 void CommitSpawnPropertyEdit(int slot_index,
659 const zelda3::DungeonSpawnPoint& before,
660 bool changed) {
661 auto& spawn = (*spawn_points_)[slot_index];
662 if (!MarkDungeonSpawnPointDirtyIfEditable(slot_index, spawn, changed)) {
663 return;
664 }
667 spawn_property_edit_callback_(slot_index, before, spawn);
668 }
671 }
672 }
673
675 void RegisterPropertyInput(const char* field_label, const char* widget_label);
676
677 std::array<zelda3::RoomEntrance, zelda3::kNumDungeonEntranceSlots>*
678 entrances_ = nullptr;
679 std::array<zelda3::DungeonSpawnPoint, zelda3::kNumDungeonSpawnPoints>*
680 spawn_points_ = nullptr;
681 int* current_entrance_id_ = nullptr;
682 std::function<void(int)> on_entrance_selected_;
684 std::function<bool()> preview_automatic_;
685 std::function<void(int)> select_preview_;
691 std::shared_ptr<const int> property_edit_gesture_;
692 std::function<void()> camera_data_changed_callback_;
693 std::optional<DungeonEntranceCameraState> camera_repair_preview_;
698 ImGuiTextFilter entrance_filter_;
699};
700
701} // namespace editor
702} // namespace yaze
703
704#endif // YAZE_APP_EDITOR_DUNGEON_PANELS_DUNGEON_ENTRANCES_PANEL_H_
WindowContent for displaying and editing dungeon entrances.
void SetPropertyEditCallbacks(EntrancePropertyEditCallback entrance_callback, SpawnPropertyEditCallback spawn_callback)
DungeonEntrancesPanel(std::array< zelda3::RoomEntrance, zelda3::kNumDungeonEntranceSlots > *entrances, std::array< zelda3::DungeonSpawnPoint, zelda3::kNumDungeonSpawnPoints > *spawn_points, int *current_entrance_id, std::function< void(int)> on_entrance_selected)
std::optional< DungeonEntranceCameraState > camera_repair_preview_
void Draw(bool *p_open) override
Draw the panel content.
std::function< void(int)> on_entrance_selected_
void SetCameraTools(CameraStateProvider state_provider, CameraRepairCallback repair_callback, std::function< void()> data_changed_callback={})
int GetPriority() const override
Get display priority for menu ordering.
SpawnPropertyEditCallback spawn_property_edit_callback_
void RegisterPropertyInput(const char *field_label, const char *widget_label)
std::function< std::optional< DungeonEntranceCameraState >(int)> CameraStateProvider
void CommitEntrancePropertyEdit(int slot_index, const zelda3::RoomEntrance &before, bool changed)
std::array< zelda3::DungeonSpawnPoint, zelda3::kNumDungeonSpawnPoints > * spawn_points_
std::function< void( int, const zelda3::DungeonSpawnPoint &, const zelda3::DungeonSpawnPoint &)> SpawnPropertyEditCallback
std::string GetDisplayName() const override
Human-readable name shown in menus and title bars.
std::string GetWorkflowGroup() const override
Optional workflow group for hack-centric actions.
std::array< zelda3::RoomEntrance, zelda3::kNumDungeonEntranceSlots > * entrances_
std::shared_ptr< const int > property_edit_gesture() const
std::string GetEditorCategory() const override
Editor category this panel belongs to.
EntrancePropertyEditCallback entrance_property_edit_callback_
std::string GetId() const override
Unique identifier for this panel.
std::string GetIcon() const override
Material Design icon for this panel.
std::function< DungeonRenderContext()> preview_context_
void SetGraphicsPreview(std::function< DungeonRenderContext()> context, std::function< bool()> automatic, std::function< void(int)> select)
std::function< absl::Status(int)> CameraRepairCallback
std::function< void( int, const zelda3::RoomEntrance &, const zelda3::RoomEntrance &)> EntrancePropertyEditCallback
std::shared_ptr< const int > property_edit_gesture_
bool DrawCameraBoundaries(const char *table_id, const std::array< uint8_t *, 8 > &boundaries)
void CommitSpawnPropertyEdit(int slot_index, const zelda3::DungeonSpawnPoint &before, bool changed)
Base interface for all logical window content components.
RAII scope that enables automatic widget registration.
Dungeon Room Entrance or Spawn Point.
#define ICON_MD_SEARCH
Definition icons.h:1673
#define ICON_MD_TUNE
Definition icons.h:2022
#define ICON_MD_DOOR_FRONT
Definition icons.h:613
std::string DungeonEntrancePreviewLabel(int slot)
bool CanEditDungeonEntrance(int slot_index, const zelda3::RoomEntrance &entrance)
bool CanEditDungeonSpawnPoint(int slot_index, const zelda3::DungeonSpawnPoint &spawn)
bool MarkDungeonEntranceDirtyIfEditable(int slot_index, zelda3::RoomEntrance &entrance, bool properties_changed)
bool MarkDungeonSpawnPointDirtyIfEditable(int slot_index, zelda3::DungeonSpawnPoint &spawn, bool properties_changed)
DungeonEntranceCameraValidation ValidateDungeonEntranceCamera(const DungeonEntranceCameraState &state)
void AutoRegisterLastItem(const std::string &widget_type, const std::string &explicit_label, const std::string &description)
Automatically register the last ImGui item.
bool InputHexWord(const char *label, uint16_t *data, float input_width, bool no_step)
Definition input.cc:489
bool InputHexByte(const char *label, uint8_t *data, float input_width, bool no_step)
Definition input.cc:510
std::string GetEntranceLabel(int id)
Convenience function to get an entrance label.
std::string GetRoomLabel(int id)
Convenience function to get a room label.
constexpr int kNumRegularDungeonEntrances
constexpr int kNumDungeonSpawnPoints
constexpr int kNumDungeonEntranceSlots
Automatic widget registration helpers for ImGui Test Engine integration.