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