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