yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
dungeon_room_selector.cc
Go to the documentation of this file.
2#include "util/i18n/tr.h"
3
4#include <algorithm>
5#include <map>
6
7#include "absl/strings/str_format.h"
15#include "app/gui/core/icons.h"
16#include "app/gui/core/input.h"
19#include "imgui/imgui.h"
20#include "util/hex.h"
22#include "zelda3/dungeon/room.h"
26
27namespace yaze::editor {
28
29using ImGui::BeginChild;
30using ImGui::EndChild;
31using ImGui::SameLine;
32
34 // Legacy combined view - prefer using DrawRoomSelector() and
35 // DrawEntranceSelector() separately via their own EditorPanels
37}
38
40 RoomSelectionIntent single_click_intent) {
41 DrawRoomSelectorInternal(single_click_intent, true);
42}
43
45 RoomSelectionIntent single_click_intent) {
46 DrawRoomSelectorInternal(single_click_intent, false);
47}
48
52
53 for (int i = 0; i < zelda3::kNumberOfRooms; ++i) {
54 std::string display_name =
56 if (room_filter_.PassFilter(display_name.c_str()) &&
58 filtered_room_indices_.push_back(i);
59 }
60 }
61}
62
65 return true;
66 if (!rooms_ || room_id < 0 || room_id >= static_cast<int>(rooms_->size())) {
67 return true;
68 }
69 const auto* room = rooms_->GetIfLoaded(room_id);
70 if (room == nullptr) {
71 return false;
72 }
73 switch (entity_type_filter_) {
75 return !room->GetSprites().empty();
76 case kFilterHasItems:
77 return !room->GetPotItems().empty();
79 return !room->GetTileObjects().empty();
80 default:
81 return true;
82 }
83}
84
86 RoomSelectionIntent single_click_intent, bool show_room_id_input) {
87 if (!rom_ || !rom_->is_loaded()) {
88 ImGui::Text(tr("ROM not loaded"));
89 return;
90 }
91
92 if (show_room_id_input) {
93 if (gui::InputHexWord("Room ID", &current_room_id_, 50.f, true)) {
95 current_room_id_ = static_cast<uint16_t>(zelda3::kNumberOfRooms - 1);
96 }
97
98 // Publish selection changed event
99 if (auto* bus = ContentRegistry::Context::event_bus()) {
100 bus->Publish(SelectionChangedEvent::CreateSingle("dungeon_room",
102 }
103
104 // Callback support
107 }
108 }
109 ImGui::Separator();
110 }
111
112 room_filter_.Draw("Filter", ImGui::GetContentRegionAvail().x);
113
114 // View mode + entity-type filter row
115 {
116 // View mode toggle
117 if (ImGui::SmallButton(view_mode_ == kViewList ? ICON_MD_LIST " List"
119 " Grouped")) {
121 }
122 if (ImGui::IsItemHovered()) {
123 ImGui::SetTooltip(
124 tr("Toggle between flat list and dungeon-grouped view"));
125 }
126 ImGui::SameLine(0, 12);
127
128 // Entity-type filter chips (compact, touch-friendly)
129 const char* labels[] = {"All", "Sprites", "Items", "Objects"};
132 for (int idx = 0; idx < 4; ++idx) {
133 if (idx > 0)
134 ImGui::SameLine();
135 bool selected = (entity_type_filter_ == values[idx]);
136 if (selected) {
137 ImGui::PushStyleColor(ImGuiCol_Button,
138 ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive));
139 }
140 if (ImGui::SmallButton(labels[idx])) {
141 entity_type_filter_ = values[idx];
142 }
143 if (selected) {
144 ImGui::PopStyleColor();
145 }
146 }
147 }
148
149 // Rebuild every frame so renamed room labels appear immediately.
150 std::string current_filter(room_filter_.InputBuf);
151 if (current_filter != last_room_filter_) {
152 last_room_filter_ = current_filter;
153 }
155
156 // Increase row height on touch devices for easier tapping
157 const bool is_touch = gui::LayoutHelpers::IsTouchDevice();
158 std::optional<gui::StyleVarGuard> touch_pad_guard;
159 if (is_touch) {
160 float touch_pad = std::max(
161 6.0f, (gui::LayoutHelpers::GetMinTouchTarget() - ImGui::GetFontSize()) *
162 0.5f);
163 touch_pad_guard.emplace(ImGuiStyleVar_CellPadding,
164 ImVec2(ImGui::GetStyle().CellPadding.x, touch_pad));
165 }
166
167 // Dispatch to appropriate view mode
168 if (view_mode_ == kViewGrouped) {
169 DrawGroupedRoomList(single_click_intent);
170 return;
171 }
172
173 // === Flat list view (original) ===
174 if (ImGui::BeginTable("RoomList", 2,
175 ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders |
176 ImGuiTableFlags_RowBg |
177 ImGuiTableFlags_Resizable)) {
178 ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed, 40.0f);
179 ImGui::TableSetupColumn("Name");
180 ImGui::TableHeadersRow();
181
182 // Use ImGuiListClipper for virtualized rendering
183 ImGuiListClipper clipper;
184 clipper.Begin(static_cast<int>(filtered_room_indices_.size()));
185
186 while (clipper.Step()) {
187 for (int row = clipper.DisplayStart; row < clipper.DisplayEnd; ++row) {
188 int room_id = filtered_room_indices_[row];
189 std::string display_name =
191
192 ImGui::TableNextRow();
193 ImGui::TableNextColumn();
194
195 char label[32];
196 snprintf(label, sizeof(label), "%03X", room_id);
197 if (ImGui::Selectable(label, current_room_id_ == room_id,
198 ImGuiSelectableFlags_SpanAllColumns |
199 ImGuiSelectableFlags_AllowDoubleClick)) {
200 current_room_id_ = room_id;
201
202 // Publish selection changed event
203 if (auto* bus = ContentRegistry::Context::event_bus()) {
204 bus->Publish(
205 SelectionChangedEvent::CreateSingle("dungeon_room", room_id));
206 }
207
208 if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
210 room_intent_callback_(room_id,
212 } else if (room_selected_callback_) {
214 }
215 } else {
217 room_intent_callback_(room_id, single_click_intent);
218 } else if (room_selected_callback_) {
220 }
221 }
222 }
223
224 // Context menu
225 if (ImGui::BeginPopupContextItem()) {
226 if (ImGui::MenuItem(tr("Open in Workbench"))) {
227 current_room_id_ = room_id;
229 room_intent_callback_(room_id,
231 } else if (room_selected_callback_) {
233 }
234 }
235 if (ImGui::MenuItem(tr("Open as Panel"))) {
236 current_room_id_ = room_id;
238 room_intent_callback_(room_id,
240 } else if (room_selected_callback_) {
242 }
243 }
244 ImGui::Separator();
245 char id_buf[16];
246 snprintf(id_buf, sizeof(id_buf), "0x%03X", room_id);
247 if (ImGui::MenuItem(tr("Copy Room ID"))) {
248 ImGui::SetClipboardText(id_buf);
249 }
250 ImGui::EndPopup();
251 }
252
253 // Tooltip with room name and thumbnail
254 if (ImGui::IsItemHovered()) {
255 ImGui::BeginTooltip();
256 ImGui::Text("%s", display_name.c_str());
257 if (rooms_) {
258 auto* loaded_room = rooms_->GetIfLoaded(room_id);
259 if (loaded_room != nullptr) {
260 ImGui::TextDisabled(tr("Blockset: %d | Palette: %d"),
261 loaded_room->blockset(),
262 loaded_room->palette());
264 *loaded_room, room_tooltip_composite_);
267 if (bmp.is_active() && bmp.texture() != 0) {
268 ImGui::Image((ImTextureID)(intptr_t)bmp.texture(),
269 ImVec2(64, 64));
270 }
271 }
272 }
273 ImGui::EndTooltip();
274 }
275
276 ImGui::TableNextColumn();
277 ImGui::TextUnformatted(display_name.c_str());
278 }
279 }
280
281 ImGui::EndTable();
282 }
283}
284
286 constexpr int kNumSpawnPoints = zelda3::kNumDungeonSpawnPoints;
287 constexpr int kNumEntrances = zelda3::kNumRegularDungeonEntrances;
288 constexpr int kTotalEntries = zelda3::kNumDungeonEntranceSlots;
289
291 filtered_entrance_indices_.reserve(kTotalEntries);
292
293 for (int i = 0; i < kTotalEntries; i++) {
294 std::string display_name;
295
296 if (i < kNumSpawnPoints) {
297 display_name = absl::StrFormat("Spawn Point %d", i);
298 } else {
299 int entrance_id = i - kNumSpawnPoints;
300 if (entrance_id < kNumEntrances) {
301 display_name = zelda3::GetEntranceLabel(entrance_id);
302 } else {
303 display_name = absl::StrFormat("Unknown Entrance %d", i);
304 }
305 }
306
307 int room_id = 0;
308 if (i < kNumSpawnPoints && spawn_points_) {
309 room_id = (*spawn_points_)[i].room_id;
310 } else if (entrances_ && i < static_cast<int>(entrances_->size())) {
311 room_id = (*entrances_)[i].room_;
312 }
313
314 char filter_text[256];
315 snprintf(filter_text, sizeof(filter_text), "%s %03X", display_name.c_str(),
316 room_id);
317
318 if (entrance_filter_.PassFilter(filter_text)) {
319 filtered_entrance_indices_.push_back(i);
320 }
321 }
322}
323
327
331
333 if (!rom_ || !rom_->is_loaded()) {
334 ImGui::Text(tr("ROM not loaded"));
335 return;
336 }
337
338 if (!entrances_) {
339 ImGui::Text(tr("Entrances not loaded"));
340 return;
341 }
342
343 if (show_properties) {
344 if (current_entrance_id_ < 0 ||
345 current_entrance_id_ >= static_cast<int>(entrances_->size())) {
347 }
348 auto& current_entrance = (*entrances_)[current_entrance_id_];
349 const bool properties_editable =
351 bool changed = false;
352
353 // Keep the full property editor in the standalone entrance panel.
354 if (!properties_editable) {
355 ImGui::TextWrapped(tr(kDungeonSpawnReadOnlyReason));
356 }
357 ImGui::BeginDisabled(!properties_editable);
358 if (ImGui::BeginTable("EntranceProps", 4, ImGuiTableFlags_Borders)) {
359 ImGui::TableSetupColumn("Core", ImGuiTableColumnFlags_WidthStretch);
360 ImGui::TableSetupColumn("Position", ImGuiTableColumnFlags_WidthStretch);
361 ImGui::TableSetupColumn("Camera", ImGuiTableColumnFlags_WidthStretch);
362 ImGui::TableSetupColumn("Scroll", ImGuiTableColumnFlags_WidthStretch);
363 ImGui::TableHeadersRow();
364
365 ImGui::TableNextRow();
366 ImGui::TableNextColumn();
367 ImGui::Text(tr("Entr ID: %04X"), current_entrance.entrance_id_);
368 changed |= gui::InputHexWord("Room ID", &current_entrance.room_);
369 changed |= gui::InputHexByte("Dungeon", &current_entrance.dungeon_id_);
370 changed |= gui::InputHexByte("Music", &current_entrance.music_);
371
372 ImGui::TableNextColumn();
373 changed |= gui::InputHexWord("Player X", &current_entrance.x_position_);
374 changed |= gui::InputHexWord("Player Y", &current_entrance.y_position_);
375 changed |= gui::InputHexByte("Blockset", &current_entrance.blockset_);
376 changed |= gui::InputHexByte("Floor", &current_entrance.floor_);
377
378 ImGui::TableNextColumn();
379 changed |=
380 gui::InputHexWord("Cam Trg X", &current_entrance.camera_trigger_x_);
381 changed |=
382 gui::InputHexWord("Cam Trg Y", &current_entrance.camera_trigger_y_);
383 changed |= gui::InputHexWord("Exit", &current_entrance.exit_);
384
385 ImGui::TableNextColumn();
386 changed |= gui::InputHexWord("Scroll X", &current_entrance.camera_x_);
387 changed |= gui::InputHexWord("Scroll Y", &current_entrance.camera_y_);
388
389 ImGui::EndTable();
390 }
391 ImGui::EndDisabled();
392
393 ImGui::Separator();
394 if (ImGui::CollapsingHeader(tr("Camera Boundaries"))) {
395 ImGui::Text(tr(" North East South West"));
396 ImGui::BeginDisabled(!properties_editable);
397 ImGui::Text(tr("Quadrant "));
398 SameLine();
399 changed |= gui::InputHexByte("##QN",
400 &current_entrance.camera_boundary_qn_, 40.f);
401 SameLine();
402 changed |= gui::InputHexByte("##QE",
403 &current_entrance.camera_boundary_qe_, 40.f);
404 SameLine();
405 changed |= gui::InputHexByte("##QS",
406 &current_entrance.camera_boundary_qs_, 40.f);
407 SameLine();
408 changed |= gui::InputHexByte("##QW",
409 &current_entrance.camera_boundary_qw_, 40.f);
410
411 ImGui::Text(tr("Full Room "));
412 SameLine();
413 changed |= gui::InputHexByte("##FN",
414 &current_entrance.camera_boundary_fn_, 40.f);
415 SameLine();
416 changed |= gui::InputHexByte("##FE",
417 &current_entrance.camera_boundary_fe_, 40.f);
418 SameLine();
419 changed |= gui::InputHexByte("##FS",
420 &current_entrance.camera_boundary_fs_, 40.f);
421 SameLine();
422 changed |= gui::InputHexByte("##FW",
423 &current_entrance.camera_boundary_fw_, 40.f);
424 ImGui::EndDisabled();
425 }
427 changed);
428 ImGui::Separator();
429 }
430
431 entrance_filter_.Draw("Filter", ImGui::GetContentRegionAvail().x);
432
433 // Rebuild cache if filter changed
434 std::string current_filter(entrance_filter_.InputBuf);
435 if (current_filter != last_entrance_filter_ ||
437 last_entrance_filter_ = current_filter;
439 }
440
441 constexpr int kNumSpawnPoints = zelda3::kNumDungeonSpawnPoints;
442
443 if (ImGui::BeginTable("EntranceList", 3,
444 ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders |
445 ImGuiTableFlags_RowBg |
446 ImGuiTableFlags_Resizable)) {
447 ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed, 40.0f);
448 ImGui::TableSetupColumn("Room", ImGuiTableColumnFlags_WidthFixed, 50.0f);
449 ImGui::TableSetupColumn("Name");
450 ImGui::TableHeadersRow();
451
452 // Use ImGuiListClipper for virtualized rendering
453 ImGuiListClipper clipper;
454 clipper.Begin(static_cast<int>(filtered_entrance_indices_.size()));
455
456 while (clipper.Step()) {
457 for (int row = clipper.DisplayStart; row < clipper.DisplayEnd; ++row) {
458 int i = filtered_entrance_indices_[row];
459 std::string display_name;
460
461 if (i < kNumSpawnPoints) {
462 display_name = absl::StrFormat("Spawn Point %d", i);
463 } else {
464 int entrance_id = i - kNumSpawnPoints;
465 display_name = zelda3::GetEntranceLabel(entrance_id);
466 }
467
468 int room_id = 0;
469 if (i < kNumSpawnPoints && spawn_points_) {
470 room_id = (*spawn_points_)[i].room_id;
471 } else if (i < static_cast<int>(entrances_->size())) {
472 room_id = (*entrances_)[i].room_;
473 }
474
475 ImGui::TableNextRow();
476 ImGui::TableNextColumn();
477
478 char label[32];
479 snprintf(label, sizeof(label), "%02X", i);
480 if (ImGui::Selectable(label, current_entrance_id_ == i,
481 ImGuiSelectableFlags_SpanAllColumns)) {
483 if (i < static_cast<int>(entrances_->size())) {
484 // Publish selection changed event
485 if (auto* bus = ContentRegistry::Context::event_bus()) {
486 bus->Publish(
487 SelectionChangedEvent::CreateSingle("dungeon_entrance", i));
488 }
489
490 // Legacy callback support
493 } else if (room_selected_callback_) {
495 }
496 }
497 }
498
499 ImGui::TableNextColumn();
500 ImGui::Text("%03X", room_id);
501
502 ImGui::TableNextColumn();
503 ImGui::TextUnformatted(display_name.c_str());
504 }
505 }
506
507 ImGui::EndTable();
508 }
509}
510
511} // namespace yaze::editor
512
513// ============================================================================
514// Grouped view + Blockset group name helper (appended)
515// ============================================================================
516
517namespace yaze::editor {
518
519const char* DungeonRoomSelector::GetBlocksetGroupName(uint8_t blockset) {
520 // ALttP blockset -> dungeon name mapping (vanilla ROM)
521 switch (blockset) {
522 case 0:
523 return "Hyrule Castle";
524 case 1:
525 return "Eastern Palace";
526 case 2:
527 return "Desert Palace";
528 case 3:
529 return "Tower of Hera";
530 case 4:
531 return "Agahnim's Tower";
532 case 5:
533 return "Palace of Darkness";
534 case 6:
535 return "Swamp Palace";
536 case 7:
537 return "Skull Woods";
538 case 8:
539 return "Thieves' Town";
540 case 9:
541 return "Ice Palace";
542 case 10:
543 return "Misery Mire";
544 case 11:
545 return "Turtle Rock";
546 case 12:
547 return "Ganon's Tower";
548 case 13:
549 return "Cave";
550 case 14:
551 return "Sanctuary / Church";
552 case 15:
553 return "Houses / Interior";
554 case 16:
555 return "Shops";
556 case 17:
557 return "Fairy Fountain";
558 case 18:
559 return "Underground";
560 case 19:
561 return "Master Sword";
562 case 20:
563 return "Fortune Teller";
564 case 21:
565 return "Tower (Ganon)";
566 case 22:
567 return "Chris Houlihan";
568 case 23:
569 return "Links House";
570 case 24:
571 return "Tomb";
572 default:
573 return "Unknown";
574 }
575}
576
578 RoomSelectionIntent single_click_intent) {
579 // Build groups from filtered rooms
580 // Group key = blockset (if rooms loaded), else "Unloaded"
581 struct GroupInfo {
582 std::string name;
583 std::vector<int> room_ids;
584 };
585 std::map<int, GroupInfo> groups;
586
587 for (int room_id : filtered_room_indices_) {
588 int key = 255; // Unknown/unloaded
589 std::string group_name = "Unloaded";
590 if (rooms_ && room_id >= 0 && room_id < static_cast<int>(rooms_->size())) {
591 size_t dungeon_index = 0;
592 if (const auto* dungeon = dungeon_project_labels::FindDungeonForRoom(
593 project_, room_id, &dungeon_index)) {
594 key = -static_cast<int>(dungeon_index) - 1;
595 group_name = dungeon_project_labels::FormatDungeonName(*dungeon);
596 } else if (auto* loaded_room = rooms_->GetIfLoaded(room_id)) {
597 key = loaded_room->blockset();
598 group_name = GetBlocksetGroupName(static_cast<uint8_t>(key));
599 }
600 }
601 auto& g = groups[key];
602 g.name = group_name;
603 g.room_ids.push_back(room_id);
604 }
605
606 // Draw as scrollable child with collapsible tree nodes
607 if (ImGui::BeginChild("##GroupedRoomList", ImVec2(0, 0), false,
608 ImGuiWindowFlags_None)) {
609 for (auto& [key, group] : groups) {
610 char header[64];
611 snprintf(header, sizeof(header), "%s (%zu rooms)##grp%d",
612 group.name.c_str(), group.room_ids.size(), key);
613
614 // Auto-open the group that contains the currently selected room
615 bool has_current =
616 std::find(group.room_ids.begin(), group.room_ids.end(),
617 static_cast<int>(current_room_id_)) != group.room_ids.end();
618 if (has_current) {
619 ImGui::SetNextItemOpen(
620 true, pending_scroll_room_id_ == static_cast<int>(current_room_id_)
621 ? ImGuiCond_Always
622 : ImGuiCond_Once);
623 }
624
625 if (ImGui::CollapsingHeader(header)) {
626 for (int room_id : group.room_ids) {
627 std::string display_name =
629 char label[64];
630 snprintf(label, sizeof(label), "%03X %s##r%d", room_id,
631 display_name.c_str(), room_id);
632
633 const bool is_current = current_room_id_ == room_id;
634 if (ImGui::Selectable(label, is_current,
635 ImGuiSelectableFlags_AllowDoubleClick)) {
636 current_room_id_ = room_id;
637
638 if (auto* bus = ContentRegistry::Context::event_bus()) {
639 bus->Publish(
640 SelectionChangedEvent::CreateSingle("dungeon_room", room_id));
641 }
642
643 if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
645 room_intent_callback_(room_id,
647 } else if (room_selected_callback_) {
649 }
650 } else {
652 room_intent_callback_(room_id, single_click_intent);
653 } else if (room_selected_callback_) {
655 }
656 }
657 }
658 if (is_current && pending_scroll_room_id_ == room_id) {
659 ImGui::SetScrollHereY(0.5f);
661 }
662
663 // Tooltip with thumbnail
664 if (ImGui::IsItemHovered() && rooms_) {
665 auto* loaded_room = rooms_->GetIfLoaded(room_id);
666 if (loaded_room != nullptr) {
667 ImGui::BeginTooltip();
668 ImGui::Text("%s", display_name.c_str());
669 ImGui::TextDisabled(tr("Blockset: %d | Palette: %d"),
670 loaded_room->blockset(),
671 loaded_room->palette());
673 *loaded_room, room_tooltip_composite_);
676 if (bmp.is_active() && bmp.texture() != 0) {
677 ImGui::Image((ImTextureID)(intptr_t)bmp.texture(),
678 ImVec2(64, 64));
679 }
680 ImGui::EndTooltip();
681 }
682 }
683 }
684 }
685 }
686 }
687 ImGui::EndChild();
688}
689
690} // namespace yaze::editor
bool is_loaded() const
Definition rom.h:155
std::array< zelda3::DungeonSpawnPoint, zelda3::kNumDungeonSpawnPoints > * spawn_points_
void DrawEntranceSelectorInternal(bool show_properties)
void DrawGroupedRoomList(RoomSelectionIntent single_click_intent)
void DrawRoomSelectorInternal(RoomSelectionIntent single_click_intent, bool show_room_id_input)
void DrawRoomSelector(RoomSelectionIntent single_click_intent=RoomSelectionIntent::kFocusInWorkbench)
std::array< zelda3::RoomEntrance, zelda3::kNumDungeonEntranceSlots > * entrances_
void DrawRoomBrowser(RoomSelectionIntent single_click_intent=RoomSelectionIntent::kFocusInWorkbench)
bool PassesEntityTypeFilter(int room_id) const
std::function< void(int)> room_selected_callback_
std::function< void(int, RoomSelectionIntent)> room_intent_callback_
const project::YazeProject * project_
static const char * GetBlocksetGroupName(uint8_t blockset)
std::function< void(int)> entrance_selected_callback_
zelda3::Room * GetIfLoaded(int room_id)
void ProcessTextureQueue(IRenderer *renderer)
Definition arena.cc:211
static Arena & Get()
Definition arena.cc:24
static float GetMinTouchTarget()
const std::vector< zelda3::Sprite > & GetSprites() const
Definition room.h:276
#define ICON_MD_LIST
Definition icons.h:1094
#define ICON_MD_FOLDER
Definition icons.h:809
::yaze::EventBus * event_bus()
Get the current EventBus instance.
std::string GetRoomLabel(const project::YazeProject *project, int room_id)
const core::DungeonEntry * FindDungeonForRoom(const project::YazeProject *project, int room_id, size_t *dungeon_index=nullptr)
std::string FormatDungeonName(const core::DungeonEntry &dungeon)
Editors are the view controllers for the application.
bool CanEditDungeonEntrance(int slot_index, const zelda3::RoomEntrance &entrance)
constexpr char kDungeonSpawnReadOnlyReason[]
bool MarkDungeonEntranceDirtyIfEditable(int slot_index, zelda3::RoomEntrance &entrance, bool properties_changed)
gfx::Bitmap & PrepareCanonicalRoomComposite(zelda3::Room &room, RoomCompositeOutput &output)
RoomSelectionIntent
Intent for room selection in the dungeon editor.
void EnsureCompositeBitmapTextureQueued(Bitmap &composite)
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.
constexpr int kNumRegularDungeonEntrances
constexpr int kNumDungeonSpawnPoints
constexpr int kNumberOfRooms
constexpr int kNumDungeonEntranceSlots
static SelectionChangedEvent CreateSingle(const std::string &src, int id, size_t session=0)