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