yaze 0.2.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
dungeon_editor.cc
Go to the documentation of this file.
1#include "dungeon_editor.h"
2
3#include "absl/container/flat_hash_map.h"
5#include "app/gfx/arena.h"
7#include "app/gui/canvas.h"
8#include "app/gui/color.h"
9#include "app/gui/icons.h"
10#include "app/gui/input.h"
11#include "app/rom.h"
13#include "imgui/imgui.h"
14#include "imgui_memory_editor.h"
15#include "util/hex.h"
16
17namespace yaze {
18namespace editor {
19
20using core::Renderer;
21
22using ImGui::BeginChild;
23using ImGui::BeginTabBar;
24using ImGui::BeginTabItem;
25using ImGui::BeginTable;
26using ImGui::Button;
27using ImGui::EndChild;
28using ImGui::EndTabBar;
29using ImGui::EndTabItem;
30using ImGui::RadioButton;
31using ImGui::SameLine;
32using ImGui::TableHeadersRow;
33using ImGui::TableNextColumn;
34using ImGui::TableNextRow;
35using ImGui::TableSetupColumn;
36using ImGui::Text;
37
38constexpr ImGuiTableFlags kDungeonObjectTableFlags =
39 ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable |
40 ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter |
41 ImGuiTableFlags_BordersV;
42
44
45absl::Status DungeonEditor::Load() {
46 auto dungeon_man_pal_group = rom()->palette_group().dungeon_main;
47
48 for (int i = 0; i < 0x100 + 40; i++) {
50
51 auto room_size = zelda3::CalculateRoomSize(rom_, i);
52 room_size_pointers_.push_back(room_size.room_size_pointer);
53 room_sizes_.push_back(room_size.room_size);
54 if (room_size.room_size_pointer != 0x0A8000) {
55 room_size_addresses_[i] = room_size.room_size_pointer;
56 }
57
58 rooms_[i].LoadObjects();
59
60 auto dungeon_palette_ptr = rom()->paletteset_ids[rooms_[i].palette][0];
61 auto palette_id = rom()->ReadWord(0xDEC4B + dungeon_palette_ptr);
62 if (palette_id.status() != absl::OkStatus()) {
63 continue;
64 }
65 int p_id = palette_id.value() / 180;
66 auto color = dungeon_man_pal_group[p_id][3];
67 room_palette_[rooms_[i].palette] = color.rgb();
68 }
69
71 // LoadRoomEntrances
72 for (int i = 0; i < 0x07; ++i) {
73 entrances_[i] = zelda3::RoomEntrance(rom(), i, true);
74 }
75
76 for (int i = 0; i < 0x85; ++i) {
77 entrances_[i + 0x07] = zelda3::RoomEntrance(rom(), i, false);
78 }
79
80 // Load the palette group and palette for the dungeon
81 full_palette_ = dungeon_man_pal_group[current_palette_group_id_];
84
86 is_loaded_ = true;
87 return absl::OkStatus();
88}
89
90absl::Status DungeonEditor::Update() {
93 refresh_graphics_ = false;
94 }
95
96 if (ImGui::BeginTabBar("##DungeonEditorTabBar")) {
97 if (ImGui::BeginTabItem("Room Editor")) {
99 ImGui::EndTabItem();
100 }
101 if (ImGui::BeginTabItem("Usage Statistics")) {
102 if (is_loaded_) {
104 }
105 ImGui::EndTabItem();
106 }
107 ImGui::EndTabBar();
108 }
109
110 return absl::OkStatus();
111}
112
114 std::for_each_n(
115 rooms_[current_room_id_].blocks().begin(), 8,
116 [this](int block) -> absl::Status {
117 gfx::Arena::Get().gfx_sheets()[block].SetPaletteWithTransparent(
120 &gfx::Arena::Get().gfx_sheets()[block]);
121 return absl::OkStatus();
122 });
123
124 auto sprites_aux1_pal_group = rom()->palette_group().sprites_aux1;
125 std::for_each_n(
126 rooms_[current_room_id_].blocks().begin() + 8, 8,
127 [this, &sprites_aux1_pal_group](int block) -> absl::Status {
128 gfx::Arena::Get().gfx_sheets()[block].SetPaletteWithTransparent(
129 sprites_aux1_pal_group[current_palette_id_], 0);
131 &gfx::Arena::Get().gfx_sheets()[block]);
132 return absl::OkStatus();
133 });
134 return absl::OkStatus();
135}
136
138 std::map<int, std::vector<int>> rooms_by_bank;
139 for (const auto &room : room_size_addresses_) {
140 int bank = room.second >> 16;
141 rooms_by_bank[bank].push_back(room.second);
142 }
143
144 // Process and calculate room sizes within each bank
145 for (auto &bank_rooms : rooms_by_bank) {
146 // Sort the rooms within this bank
147 std::sort(bank_rooms.second.begin(), bank_rooms.second.end());
148
149 for (size_t i = 0; i < bank_rooms.second.size(); ++i) {
150 int room_ptr = bank_rooms.second[i];
151
152 // Identify the room ID for the current room pointer
153 int room_id =
154 std::find_if(room_size_addresses_.begin(), room_size_addresses_.end(),
155 [room_ptr](const auto &entry) {
156 return entry.second == room_ptr;
157 })
158 ->first;
159
160 if (room_ptr != 0x0A8000) {
161 if (i < bank_rooms.second.size() - 1) {
162 // Calculate size as difference between current room and next room
163 // in the same bank
164 room_sizes_[room_id] = bank_rooms.second[i + 1] - room_ptr;
165 } else {
166 // Calculate size for the last room in this bank
167 int bank_end_address = (bank_rooms.first << 16) | 0xFFFF;
168 room_sizes_[room_id] = bank_end_address - room_ptr + 1;
169 }
170 total_room_size_ += room_sizes_[room_id];
171 } else {
172 // Room with address 0x0A8000
173 room_sizes_[room_id] = 0x00;
174 }
175 }
176 }
177}
178
180 DrawToolset();
181
182 if (palette_showing_) {
183 ImGui::Begin("Palette Editor", &palette_showing_, 0);
184 auto dungeon_main_pal_group = rom()->palette_group().dungeon_main;
185 current_palette_ = dungeon_main_pal_group[current_palette_group_id_];
188 ImGui::End();
189 }
190
191 if (BeginTable("#DungeonEditTable", 3, kDungeonTableFlags, ImVec2(0, 0))) {
192 TableSetupColumn("Room Selector");
193 TableSetupColumn("Canvas", ImGuiTableColumnFlags_WidthStretch,
194 ImGui::GetContentRegionAvail().x);
195 TableSetupColumn("Object Selector");
196 TableHeadersRow();
197 TableNextRow();
198
199 TableNextColumn();
200 if (ImGui::BeginTabBar("##DungeonRoomTabBar")) {
201 TAB_ITEM("Rooms");
203 END_TAB_ITEM();
204 TAB_ITEM("Entrances");
206 END_TAB_ITEM();
207 ImGui::EndTabBar();
208 }
209
210 TableNextColumn();
212
213 TableNextColumn();
215 ImGui::EndTable();
216 }
217 return absl::OkStatus();
218}
219
221 if (BeginTable("DWToolset", 13, ImGuiTableFlags_SizingFixedFit,
222 ImVec2(0, 0))) {
223 TableSetupColumn("#undoTool");
224 TableSetupColumn("#redoTool");
225 TableSetupColumn("#separator");
226 TableSetupColumn("#anyTool");
227
228 TableSetupColumn("#bg1Tool");
229 TableSetupColumn("#bg2Tool");
230 TableSetupColumn("#bg3Tool");
231 TableSetupColumn("#separator");
232 TableSetupColumn("#spriteTool");
233 TableSetupColumn("#itemTool");
234 TableSetupColumn("#doorTool");
235 TableSetupColumn("#blockTool");
236
237 TableNextColumn();
238 if (Button(ICON_MD_UNDO)) {
240 }
241
242 TableNextColumn();
243 if (Button(ICON_MD_REDO)) {
245 }
246
247 TableNextColumn();
248 Text(ICON_MD_MORE_VERT);
249
250 TableNextColumn();
253 }
254
255 TableNextColumn();
256 if (RadioButton(ICON_MD_FILTER_1, background_type_ == kBackground1)) {
258 }
259
260 TableNextColumn();
261 if (RadioButton(ICON_MD_FILTER_2, background_type_ == kBackground2)) {
263 }
264
265 TableNextColumn();
266 if (RadioButton(ICON_MD_FILTER_3, background_type_ == kBackground3)) {
268 }
269
270 TableNextColumn();
271 Text(ICON_MD_MORE_VERT);
272
273 TableNextColumn();
274 if (RadioButton(ICON_MD_PEST_CONTROL, placement_type_ == kSprite)) {
276 }
277 if (ImGui::IsItemHovered()) {
278 ImGui::SetTooltip("Sprites");
279 }
280
281 TableNextColumn();
282 if (RadioButton(ICON_MD_GRASS, placement_type_ == kItem)) {
284 }
285 if (ImGui::IsItemHovered()) {
286 ImGui::SetTooltip("Items");
287 }
288
289 TableNextColumn();
290 if (RadioButton(ICON_MD_SENSOR_DOOR, placement_type_ == kDoor)) {
292 }
293 if (ImGui::IsItemHovered()) {
294 ImGui::SetTooltip("Doors");
295 }
296
297 TableNextColumn();
298 if (RadioButton(ICON_MD_SQUARE, placement_type_ == kBlock)) {
300 }
301 if (ImGui::IsItemHovered()) {
302 ImGui::SetTooltip("Blocks");
303 }
304
305 TableNextColumn();
306 if (Button(ICON_MD_PALETTE)) {
308 }
309
310 ImGui::EndTable();
311 }
312}
313
315 if (rom()->is_loaded()) {
317 gui::InputHex("Palette ID", &current_palette_id_);
318
319 if (ImGuiID child_id = ImGui::GetID((void *)(intptr_t)9);
320 BeginChild(child_id, ImGui::GetContentRegionAvail(), true,
321 ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
322 int i = 0;
323 for (const auto each_room_name : zelda3::kRoomNames) {
325 current_room_id_ == i, "Dungeon Room Names", util::HexByte(i),
326 each_room_name.data());
327 if (ImGui::IsItemClicked()) {
328 // TODO: Jump to tab if room is already open
330 if (!active_rooms_.contains(i)) {
331 active_rooms_.push_back(i);
332 }
333 }
334 i += 1;
335 }
336 }
337 EndChild();
338 }
339}
340
341using ImGui::Separator;
342
344 if (rom()->is_loaded()) {
345 auto current_entrance = entrances_[current_entrance_id_];
346 gui::InputHexWord("Entrance ID", &current_entrance.entrance_id_);
347 gui::InputHexWord("Room ID", &current_entrance.room_, 50.f, true);
348 SameLine();
349
350 gui::InputHexByte("Dungeon ID", &current_entrance.dungeon_id_, 50.f, true);
351 gui::InputHexByte("Blockset", &current_entrance.blockset_, 50.f, true);
352 SameLine();
353
354 gui::InputHexByte("Music", &current_entrance.music_, 50.f, true);
355 SameLine();
356 gui::InputHexByte("Floor", &current_entrance.floor_);
357 Separator();
358
359 gui::InputHexWord("Player X ", &current_entrance.x_position_);
360 SameLine();
361 gui::InputHexWord("Player Y ", &current_entrance.y_position_);
362
363 gui::InputHexWord("Camera X", &current_entrance.camera_trigger_x_);
364 SameLine();
365 gui::InputHexWord("Camera Y", &current_entrance.camera_trigger_y_);
366
367 gui::InputHexWord("Scroll X ", &current_entrance.camera_x_);
368 SameLine();
369 gui::InputHexWord("Scroll Y ", &current_entrance.camera_y_);
370
371 gui::InputHexWord("Exit", &current_entrance.exit_, 50.f, true);
372
373 Separator();
374 Text("Camera Boundaries");
375 Separator();
376 Text("\t\t\t\t\tNorth East South West");
377 gui::InputHexByte("Quadrant", &current_entrance.camera_boundary_qn_, 50.f,
378 true);
379 SameLine();
380 gui::InputHexByte("", &current_entrance.camera_boundary_qe_, 50.f, true);
381 SameLine();
382 gui::InputHexByte("", &current_entrance.camera_boundary_qs_, 50.f, true);
383 SameLine();
384 gui::InputHexByte("", &current_entrance.camera_boundary_qw_, 50.f, true);
385
386 gui::InputHexByte("Full room", &current_entrance.camera_boundary_fn_, 50.f,
387 true);
388 SameLine();
389 gui::InputHexByte("", &current_entrance.camera_boundary_fe_, 50.f, true);
390 SameLine();
391 gui::InputHexByte("", &current_entrance.camera_boundary_fs_, 50.f, true);
392 SameLine();
393 gui::InputHexByte("", &current_entrance.camera_boundary_fw_, 50.f, true);
394
395 if (BeginChild("EntranceSelector", ImVec2(0, 0), true,
396 ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
397 for (int i = 0; i < 0x85 + 7; i++) {
399 current_entrance_id_ == i, "Dungeon Entrance Names",
401
402 if (ImGui::IsItemClicked()) {
404 if (!active_rooms_.contains(i)) {
405 active_rooms_.push_back(entrances_[i].room_);
406 }
407 }
408 }
409 }
410 EndChild();
411 }
412}
413
415 static int next_tab_id = 0;
416
417 if (BeginTabBar("MyTabBar", kDungeonTabBarFlags)) {
418 if (ImGui::TabItemButton(ICON_MD_ADD, kDungeonTabFlags)) {
419 if (std::find(active_rooms_.begin(), active_rooms_.end(),
421 // Room is already open
422 next_tab_id++;
423 }
424 active_rooms_.push_back(next_tab_id++); // Add new tab
425 }
426
427 // Submit our regular tabs
428 for (int n = 0; n < active_rooms_.Size;) {
429 bool open = true;
430
431 if (active_rooms_[n] > sizeof(zelda3::kRoomNames) / 4) {
432 active_rooms_.erase(active_rooms_.Data + n);
433 continue;
434 }
435
436 if (BeginTabItem(zelda3::kRoomNames[active_rooms_[n]].data(), &open,
437 ImGuiTabItemFlags_None)) {
439 EndTabItem();
440 }
441
442 if (!open)
443 active_rooms_.erase(active_rooms_.Data + n);
444 else
445 n++;
446 }
447
448 EndTabBar();
449 }
450 Separator();
451}
452
454 ImGui::BeginGroup();
455
456 gui::InputHexByte("Layout", &rooms_[room_id].layout);
457 SameLine();
458
459 gui::InputHexByte("Blockset", &rooms_[room_id].blockset);
460 SameLine();
461
462 gui::InputHexByte("Spriteset", &rooms_[room_id].spriteset);
463 SameLine();
464
465 gui::InputHexByte("Palette", &rooms_[room_id].palette);
466
467 gui::InputHexByte("Floor1", &rooms_[room_id].floor1);
468 SameLine();
469
470 gui::InputHexByte("Floor2", &rooms_[room_id].floor2);
471 SameLine();
472
473 gui::InputHexWord("Message ID", &rooms_[room_id].message_id_);
474 SameLine();
475
476 if (Button("Load Room")) {
477 rooms_[room_id].LoadRoomGraphics(rooms_[room_id].blockset);
478 rooms_[room_id].RenderRoomGraphics();
479 }
480
481 static bool show_objects = false;
482 ImGui::Checkbox("Show Objects", &show_objects);
483
484 ImGui::EndGroup();
485
486 canvas_.DrawBackground();
487 canvas_.DrawContextMenu();
488 if (is_loaded_) {
489 canvas_.DrawBitmap(gfx::Arena::Get().bg1().bitmap(), 0, true);
490 canvas_.DrawBitmap(gfx::Arena::Get().bg2().bitmap(), 0, true);
491
492 if (show_objects) {
493 for (const auto &object : rooms_[room_id].tile_objects_) {
494 canvas_.DrawOutline(object.x_, object.y_, object.width_ * 16,
495 object.height_ * 16);
496 }
497 }
498 }
499 canvas_.DrawGrid();
500 canvas_.DrawOverlay();
501}
502
504 const auto height = 0x40;
505 room_gfx_canvas_.DrawBackground();
506 room_gfx_canvas_.DrawContextMenu();
507 room_gfx_canvas_.DrawTileSelector(32);
508 if (is_loaded_) {
509 auto blocks = rooms_[current_room_id_].blocks();
510 int current_block = 0;
511 for (int block : blocks) {
512 int offset = height * (current_block + 1);
513 int top_left_y = room_gfx_canvas_.zero_point().y + 2;
514 if (current_block >= 1) {
515 top_left_y = room_gfx_canvas_.zero_point().y + height * current_block;
516 }
517 room_gfx_canvas_.draw_list()->AddImage(
518 (ImTextureID)(intptr_t)gfx::Arena::Get()
519 .gfx_sheets()[block]
520 .texture(),
521 ImVec2(room_gfx_canvas_.zero_point().x + 2, top_left_y),
522 ImVec2(room_gfx_canvas_.zero_point().x + 0x100,
523 room_gfx_canvas_.zero_point().y + offset));
524 current_block += 1;
525 }
526 }
527 room_gfx_canvas_.DrawGrid(32.0f);
528 room_gfx_canvas_.DrawOverlay();
529}
530
532 if (BeginTabBar("##TabBar", ImGuiTabBarFlags_FittingPolicyScroll)) {
533 if (BeginTabItem("Room Graphics")) {
534 if (ImGuiID child_id = ImGui::GetID((void *)(intptr_t)3);
535 BeginChild(child_id, ImGui::GetContentRegionAvail(), true,
536 ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
538 }
539 EndChild();
540 EndTabItem();
541 }
542
543 if (BeginTabItem("Object Renderer")) {
545 EndTabItem();
546 }
547 EndTabBar();
548 }
549}
550
552 if (BeginTable("DungeonObjectEditorTable", 2, kDungeonObjectTableFlags,
553 ImVec2(0, 0))) {
554 TableSetupColumn("Dungeon Objects", ImGuiTableColumnFlags_WidthStretch,
555 ImGui::GetContentRegionAvail().x);
556 TableSetupColumn("Canvas");
557
558 TableNextColumn();
559 BeginChild("DungeonObjectButtons", ImVec2(250, 0), true);
560
561 int selected_object = 0;
562 int i = 0;
563 for (const auto object_name : zelda3::Type1RoomObjectNames) {
564 if (ImGui::Selectable(object_name.data(), selected_object == i)) {
565 selected_object = i;
566 // object_renderer_.LoadObject(i,
567 // rooms_[current_room_id_].mutable_blocks());
568 // object_loaded_ = true;
569 }
570 i += 1;
571 }
572
573 EndChild();
574
575 // Right side of the table - Canvas
576 TableNextColumn();
577 BeginChild("DungeonObjectCanvas", ImVec2(276, 0x10 * 0x40 + 1), true);
578
579 object_canvas_.DrawBackground(ImVec2(256 + 1, 0x10 * 0x40 + 1));
580 object_canvas_.DrawContextMenu();
581 object_canvas_.DrawTileSelector(32);
582 object_canvas_.DrawGrid(32.0f);
583 object_canvas_.DrawOverlay();
584
585 EndChild();
586 ImGui::EndTable();
587 }
588
589 if (object_loaded_) {
590 ImGui::Begin("Memory Viewer", &object_loaded_, 0);
591 static MemoryEditor mem_edit;
592 // mem_edit.DrawContents(object_renderer_.mutable_memory()->data(),
593 // object_renderer_.mutable_memory()->size());
594 ImGui::End();
595 }
596}
597
599 for (const auto &room : rooms_) {
600 if (blockset_usage_.find(room.blockset) == blockset_usage_.end()) {
601 blockset_usage_[room.blockset] = 1;
602 } else {
603 blockset_usage_[room.blockset] += 1;
604 }
605
606 if (spriteset_usage_.find(room.spriteset) == spriteset_usage_.end()) {
607 spriteset_usage_[room.spriteset] = 1;
608 } else {
609 spriteset_usage_[room.spriteset] += 1;
610 }
611
612 if (palette_usage_.find(room.palette) == palette_usage_.end()) {
613 palette_usage_[room.palette] = 1;
614 } else {
615 palette_usage_[room.palette] += 1;
616 }
617 }
618}
619
621 const absl::flat_hash_map<uint16_t, int> &usage_map, uint16_t &selected_set,
622 int spriteset_offset) {
623 // Sort the usage map by set number
624 std::vector<std::pair<uint16_t, int>> sorted_usage(usage_map.begin(),
625 usage_map.end());
626 std::sort(sorted_usage.begin(), sorted_usage.end(),
627 [](const auto &a, const auto &b) { return a.first < b.first; });
628
629 for (const auto &[set, count] : sorted_usage) {
630 std::string display_str;
631 if (spriteset_offset != 0x00) {
632 display_str = absl::StrFormat("%#02x, %#02x: %d", set,
633 (set + spriteset_offset), count);
634 } else {
635 display_str =
636 absl::StrFormat("%#02x: %d", (set + spriteset_offset), count);
637 }
638 if (ImGui::Selectable(display_str.c_str(), selected_set == set)) {
639 selected_set = set; // Update the selected set when clicked
640 }
641 }
642}
643
644namespace {
645// Calculate the unused sets in a usage map
646// Range for blocksets 0-0x24
647// Range for spritesets 0-0x8F
648// Range for palettes 0-0x47
649template <typename T>
650void RenderUnusedSets(const absl::flat_hash_map<T, int> &usage_map, int max_set,
651 int spriteset_offset = 0x00) {
652 std::vector<int> unused_sets;
653 for (int i = 0; i < max_set; i++) {
654 if (usage_map.find(i) == usage_map.end()) {
655 unused_sets.push_back(i);
656 }
657 }
658 for (const auto &set : unused_sets) {
659 if (spriteset_offset != 0x00) {
660 Text("%#02x, %#02x", set, (set + spriteset_offset));
661 } else {
662 Text("%#02x", set);
663 }
664 }
665}
666} // namespace
667
669 if (Button("Refresh")) {
670 selected_blockset_ = 0xFFFF;
671 selected_spriteset_ = 0xFFFF;
672 selected_palette_ = 0xFFFF;
673 spriteset_usage_.clear();
674 blockset_usage_.clear();
675 palette_usage_.clear();
677 }
678
679 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
680 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
681 if (BeginTable("DungeonUsageStatsTable", 8,
682 kDungeonTableFlags | ImGuiTableFlags_SizingFixedFit,
683 ImGui::GetContentRegionAvail())) {
684 TableSetupColumn("Blockset Usage");
685 TableSetupColumn("Unused Blockset");
686 TableSetupColumn("Palette Usage");
687 TableSetupColumn("Unused Palette");
688 TableSetupColumn("Spriteset Usage");
689 TableSetupColumn("Unused Spriteset");
690 TableSetupColumn("Usage Grid");
691 TableSetupColumn("Group Preview");
692 TableHeadersRow();
693 ImGui::PopStyleVar(2);
694
695 TableNextColumn();
696 BeginChild("BlocksetUsageScroll", ImVec2(0, 0), true,
697 ImGuiWindowFlags_HorizontalScrollbar);
699 EndChild();
700
701 TableNextColumn();
702 BeginChild("UnusedBlocksetScroll", ImVec2(0, 0), true,
703 ImGuiWindowFlags_HorizontalScrollbar);
704 RenderUnusedSets(blockset_usage_, 0x25);
705 EndChild();
706
707 TableNextColumn();
708 BeginChild("PaletteUsageScroll", ImVec2(0, 0), true,
709 ImGuiWindowFlags_HorizontalScrollbar);
711 EndChild();
712
713 TableNextColumn();
714 BeginChild("UnusedPaletteScroll", ImVec2(0, 0), true,
715 ImGuiWindowFlags_HorizontalScrollbar);
716 RenderUnusedSets(palette_usage_, 0x48);
717 EndChild();
718
719 TableNextColumn();
720
721 BeginChild("SpritesetUsageScroll", ImVec2(0, 0), true,
722 ImGuiWindowFlags_HorizontalScrollbar);
724 EndChild();
725
726 TableNextColumn();
727 BeginChild("UnusedSpritesetScroll", ImVec2(0, 0), true,
728 ImGuiWindowFlags_HorizontalScrollbar);
729 RenderUnusedSets(spriteset_usage_, 0x90, 0x40);
730 EndChild();
731
732 TableNextColumn();
733 BeginChild("UsageGrid", ImVec2(0, 0), true,
734 ImGuiWindowFlags_HorizontalScrollbar);
735 Text("%s", absl::StrFormat("Total size of all rooms: %d hex format: %#06x",
737 .c_str());
739 EndChild();
740
741 TableNextColumn();
742 if (selected_blockset_ < 0x25) {
743 gfx_group_editor_.SetSelectedBlockset(selected_blockset_);
744 gfx_group_editor_.DrawBlocksetViewer(true);
745 } else if (selected_spriteset_ < 0x90) {
746 gfx_group_editor_.SetSelectedSpriteset(selected_spriteset_ + 0x40);
747 gfx_group_editor_.DrawSpritesetViewer(true);
748 }
749 }
750 ImGui::EndTable();
751}
752
754 int totalSquares = 296;
755 int squaresWide = 16;
756 int squaresTall = (totalSquares + squaresWide - 1) /
757 squaresWide; // Ceiling of totalSquares/squaresWide
758
759 for (int row = 0; row < squaresTall; ++row) {
760 ImGui::NewLine();
761
762 for (int col = 0; col < squaresWide; ++col) {
763 // Check if we have reached 295 squares
764 if (row * squaresWide + col >= totalSquares) {
765 break;
766 }
767 // Determine if this square should be highlighted
768 const auto &room = rooms_[row * squaresWide + col];
769
770 // Create a button or selectable for each square
771 ImGui::BeginGroup();
772 ImVec4 color = room_palette_[room.palette];
773 color.x = color.x / 255;
774 color.y = color.y / 255;
775 color.z = color.z / 255;
776 color.w = 1.0f;
777 if (room_sizes_[row * squaresWide + col] > 0xFFFF) {
778 color = ImVec4(1.0f, 0.0f, 0.0f, 1.0f); // Or any highlight color
779 }
780 if (room_sizes_[row * squaresWide + col] == 0) {
781 color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); // Or any highlight color
782 }
783 ImGui::PushStyleColor(ImGuiCol_Button, color);
784 // Make the button text darker
785 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.0f, 0.0f, 0.0f, 1.0f));
786
787 bool highlight = room.blockset == selected_blockset_ ||
788 room.spriteset == selected_spriteset_ ||
789 room.palette == selected_palette_;
790
791 // Set highlight color if needed
792 if (highlight) {
793 ImGui::PushStyleColor(
794 ImGuiCol_Button,
795 ImVec4(1.0f, 0.5f, 0.0f, 1.0f)); // Or any highlight color
796 }
797 if (Button(absl::StrFormat("%#x", room_sizes_[row * squaresWide + col])
798 .c_str(),
799 ImVec2(55, 30))) {
800 // Switch over to the room editor tab
801 // and add a room tab by the ID of the square
802 // that was clicked
803 }
804 if (ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
805 ImGui::OpenPopup(
806 absl::StrFormat("RoomContextMenu%d", row * squaresWide + col)
807 .c_str());
808 }
809 ImGui::PopStyleColor(2);
810 ImGui::EndGroup();
811
812 // Reset style if it was highlighted
813 if (highlight) {
814 ImGui::PopStyleColor();
815 }
816
817 // Check if the square is hovered
818 if (ImGui::IsItemHovered()) {
819 // Display a tooltip with all the room properties
820 ImGui::BeginTooltip();
821 Text("Room ID: %d", row * squaresWide + col);
822 Text("Blockset: %#02x", room.blockset);
823 Text("Spriteset: %#02x", room.spriteset);
824 Text("Palette: %#02x", room.palette);
825 Text("Floor1: %#02x", room.floor1);
826 Text("Floor2: %#02x", room.floor2);
827 Text("Message ID: %#04x", room.message_id_);
828 Text("Size: %#016llx", room_sizes_[row * squaresWide + col]);
829 Text("Size Pointer: %#016llx",
830 room_size_pointers_[row * squaresWide + col]);
831 ImGui::EndTooltip();
832 }
833
834 // Keep squares in the same line
835 SameLine();
836 }
837 }
838}
839
840} // namespace editor
841} // namespace yaze
auto palette_group() const
Definition rom.h:187
ResourceLabelManager * resource_label()
Definition rom.h:194
std::array< std::array< uint8_t, 4 >, kNumPalettesets > paletteset_ids
Definition rom.h:202
absl::StatusOr< uint16_t > ReadWord(int offset)
Definition rom.cc:522
void UpdateBitmap(gfx::Bitmap *bitmap)
Definition renderer.h:49
absl::flat_hash_map< uint16_t, int > spriteset_usage_
absl::Status Update() override
std::unordered_map< int, ImVec4 > room_palette_
absl::flat_hash_map< uint16_t, int > blockset_usage_
absl::flat_hash_map< uint16_t, int > palette_usage_
void RenderSetUsage(const absl::flat_hash_map< uint16_t, int > &usage_map, uint16_t &selected_set, int spriteset_offset=0x00)
gfx::SnesPalette current_palette_
gfx::PaletteGroup current_palette_group_
void DrawDungeonCanvas(int room_id)
std::unordered_map< int, int > room_size_addresses_
absl::Status Redo() override
std::vector< int64_t > room_sizes_
std::array< zelda3::RoomEntrance, 0x8C > entrances_
std::vector< int64_t > room_size_pointers_
absl::Status Load() override
absl::Status Undo() override
std::array< zelda3::Room, 0x128 > rooms_
static Renderer & Get()
Definition renderer.h:26
std::array< gfx::Bitmap, 223 > & gfx_sheets()
Definition arena.h:29
static Arena & Get()
Definition arena.cc:10
Dungeon Room Entrance or Spawn Point.
#define ICON_MD_MORE_VERT
Definition icons.h:1241
#define ICON_MD_SQUARE
Definition icons.h:1836
#define ICON_MD_REDO
Definition icons.h:1568
#define ICON_MD_FILTER_2
Definition icons.h:750
#define ICON_MD_GRASS
Definition icons.h:889
#define ICON_MD_FILTER_NONE
Definition icons.h:769
#define ICON_MD_SENSOR_DOOR
Definition icons.h:1685
#define ICON_MD_FILTER_3
Definition icons.h:751
#define ICON_MD_ADD
Definition icons.h:84
#define ICON_MD_PEST_CONTROL
Definition icons.h:1427
#define ICON_MD_FILTER_1
Definition icons.h:749
#define ICON_MD_PALETTE
Definition icons.h:1368
#define ICON_MD_UNDO
Definition icons.h:2034
#define PRINT_IF_ERROR(expression)
Definition macro.h:25
#define RETURN_IF_ERROR(expression)
Definition macro.h:51
#define END_TAB_ITEM()
Definition macro.h:5
#define ASSIGN_OR_RETURN(type_variable_name, expression)
Definition macro.h:59
#define TAB_ITEM(w)
Definition macro.h:4
void RenderUnusedSets(const absl::flat_hash_map< T, int > &usage_map, int max_set, int spriteset_offset=0x00)
Editors are the view controllers for the application.
constexpr ImGuiTableFlags kDungeonTableFlags
constexpr ImGuiTabItemFlags kDungeonTabFlags
constexpr ImGuiTabBarFlags kDungeonTabBarFlags
constexpr ImGuiTableFlags kDungeonObjectTableFlags
absl::StatusOr< PaletteGroup > CreatePaletteGroupFromLargePalette(SnesPalette &palette, int num_colors)
Take a SNESPalette, divide it into palettes of 8 colors.
bool InputHexWord(const char *label, uint16_t *data, float input_width, bool no_step)
Definition input.cc:162
bool InputHex(const char *label, uint64_t *data)
Definition input.cc:143
void SelectablePalettePipeline(uint64_t &palette_id, bool &refresh_graphics, gfx::SnesPalette &palette)
Definition color.cc:127
bool InputHexByte(const char *label, uint8_t *data, float input_width, bool no_step)
Definition input.cc:176
std::string HexByte(uint8_t byte, HexStringParams params)
Definition hex.cc:30
constexpr std::string_view kEntranceNames[]
Definition common.h:48
RoomSize CalculateRoomSize(Rom *rom, int room_id)
Definition room.cc:20
constexpr std::string_view kRoomNames[]
Definition room.h:292
Room LoadRoomFromRom(Rom *rom, int room_id)
Definition room.cc:80
Main namespace for the application.
Definition controller.cc:18
void SelectableLabelWithNameEdit(bool selected, const std::string &type, const std::string &key, const std::string &defaultValue)
Definition project.cc:146