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