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