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