yaze 0.2.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
overworld_editor.cc
Go to the documentation of this file.
1#include "overworld_editor.h"
2
3#include <cmath>
4#include <future>
5#include <unordered_map>
6#include <vector>
7
8#include "absl/status/status.h"
9#include "absl/strings/str_format.h"
10#include "app/core/features.h"
15#include "app/gfx/bitmap.h"
17#include "app/gui/canvas.h"
18#include "app/gui/icons.h"
19#include "app/gui/input.h"
20#include "app/gui/style.h"
21#include "app/gui/zeml.h"
22#include "app/rom.h"
23#include "app/zelda3/common.h"
25#include "imgui/imgui.h"
26#include "imgui_memory_editor.h"
27#include "util/hex.h"
28#include "util/log.h"
29#include "util/macro.h"
30
31namespace yaze {
32namespace editor {
33
34using core::Renderer;
35using ImGui::BeginChild;
36using ImGui::BeginTabBar;
37using ImGui::BeginTabItem;
38using ImGui::BeginTable;
39using ImGui::BeginTooltip;
40using ImGui::Button;
41using ImGui::Checkbox;
42using ImGui::EndChild;
43using ImGui::EndTabBar;
44using ImGui::EndTabItem;
45using ImGui::EndTable;
46using ImGui::EndTooltip;
47using ImGui::IsItemHovered;
48using ImGui::NewLine;
49using ImGui::PopStyleColor;
50using ImGui::PushStyleColor;
51using ImGui::SameLine;
52using ImGui::Selectable;
53using ImGui::Separator;
54using ImGui::TableHeadersRow;
55using ImGui::TableNextColumn;
56using ImGui::TableNextRow;
57using ImGui::TableSetupColumn;
58using ImGui::Text;
59
60constexpr int kTile16Size = 0x10;
61
63 // Load zeml string from layouts/overworld.zeml
64 std::string layout = gui::zeml::LoadFile("overworld.zeml");
65 // Parse the zeml string into a Node object
67
68 gui::zeml::Bind(&*layout_node_.GetNode("OverworldCanvas"),
69 [this]() { DrawOverworldCanvas(); });
70 gui::zeml::Bind(&*layout_node_.GetNode("OverworldTileSelector"),
71 [this]() { status_ = DrawTileSelector(); });
72 gui::zeml::Bind(&*layout_node_.GetNode("OwUsageStats"), [this]() {
73 if (rom_.is_loaded()) {
74 status_ = UpdateUsageStats();
75 }
76 });
77 gui::zeml::Bind(&*layout_node_.GetNode("owToolset"),
78 [this]() { DrawToolset(); });
79 gui::zeml::Bind(&*layout_node_.GetNode("OwTile16Editor"), [this]() {
80 if (rom_.is_loaded()) {
81 status_ = tile16_editor_.Update();
82 }
83 });
84 gui::zeml::Bind(&*layout_node_.GetNode("OwGfxGroupEditor"), [this]() {
85 if (rom_.is_loaded()) {
86 status_ = gfx_group_editor_.Update();
87 }
88 });
89}
90
91absl::Status OverworldEditor::Load() {
95 *overworld_.mutable_all_tiles_types()));
97 all_gfx_loaded_ = true;
98 return absl::OkStatus();
99}
100
102 status_ = absl::OkStatus();
103
106 }
107
108 // Draw the overworld editor layout from the ZEML file
110 return status_;
111}
112
114 static bool use_work_area = true;
115 static ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration |
116 ImGuiWindowFlags_NoMove |
117 ImGuiWindowFlags_NoSavedSettings;
118 const ImGuiViewport *viewport = ImGui::GetMainViewport();
119 ImGui::SetNextWindowPos(use_work_area ? viewport->WorkPos : viewport->Pos);
120 ImGui::SetNextWindowSize(use_work_area ? viewport->WorkSize : viewport->Size);
121 if (ImGui::Begin("Fullscreen Overworld Editor", &overworld_canvas_fullscreen_,
122 flags)) {
123 // Draws the toolset for editing the Overworld.
124 DrawToolset();
126 }
127 ImGui::End();
128}
129
131 static bool show_gfx_group = false;
132 static bool show_properties = false;
133
134 if (toolset_table_.column_contents.empty()) {
135 gui::AddTableColumn(toolset_table_, "##Undo", [&]() {
136 if (Button(ICON_MD_UNDO)) status_ = Undo();
137 });
138 gui::AddTableColumn(toolset_table_, "##Redo", [&]() {
139 if (Button(ICON_MD_REDO)) status_ = Redo();
140 });
142 gui::AddTableColumn(toolset_table_, "##ZoomOut", [&]() {
143 if (Button(ICON_MD_ZOOM_OUT)) ow_map_canvas_.ZoomOut();
144 });
145 gui::AddTableColumn(toolset_table_, "##ZoomIn", [&]() {
146 if (Button(ICON_MD_ZOOM_IN)) ow_map_canvas_.ZoomIn();
147 });
148 gui::AddTableColumn(toolset_table_, "##Fullscreen", [&]() {
149 if (Button(ICON_MD_OPEN_IN_FULL))
151 HOVER_HINT("Fullscreen Canvas")
152 });
154 gui::AddTableColumn(toolset_table_, "##Pan", [&]() {
157 ow_map_canvas_.set_draggable(true);
158 }
159 HOVER_HINT("Pan (Right click and drag)");
160 });
161 gui::AddTableColumn(toolset_table_, "##DrawTile", [&]() {
164 }
165 HOVER_HINT("Draw Tile");
166 });
167 gui::AddTableColumn(toolset_table_, "##Entrances", [&]() {
168 if (Selectable(ICON_MD_DOOR_FRONT,
171 HOVER_HINT("Entrances");
172 });
173 gui::AddTableColumn(toolset_table_, "##Exits", [&]() {
176 HOVER_HINT("Exits");
177 });
178 gui::AddTableColumn(toolset_table_, "##Items", [&]() {
181 HOVER_HINT("Items");
182 });
183 gui::AddTableColumn(toolset_table_, "##Sprites", [&]() {
184 if (Selectable(ICON_MD_PEST_CONTROL_RODENT,
187 HOVER_HINT("Sprites");
188 });
189 gui::AddTableColumn(toolset_table_, "##Transports", [&]() {
190 if (Selectable(ICON_MD_ADD_LOCATION,
193 HOVER_HINT("Transports");
194 });
195 gui::AddTableColumn(toolset_table_, "##Music", [&]() {
198 HOVER_HINT("Music");
199 });
200 gui::AddTableColumn(toolset_table_, "##Tile16Editor", [&]() {
202 HOVER_HINT("Tile16 Editor");
203 });
204 gui::AddTableColumn(toolset_table_, "##GfxGroupEditor", [&]() {
205 if (Button(ICON_MD_TABLE_CHART)) show_gfx_group = !show_gfx_group;
206 HOVER_HINT("Gfx Group Editor");
207 });
209 gui::AddTableColumn(toolset_table_, "##Properties", [&]() {
210 if (Button(ICON_MD_CONTENT_COPY)) {
211 std::vector<uint8_t> png_data;
212 png_data = maps_bmp_[current_map_].GetPngData();
213 if (png_data.size() > 0) {
215 } else {
216 status_ = absl::InternalError(
217 "Failed to convert overworld map surface to PNG");
218 }
219 }
220 HOVER_HINT("Copy Map to Clipboard");
221 });
222 gui::AddTableColumn(toolset_table_, "##Palette", [&]() {
224 });
226 gui::AddTableColumn(toolset_table_, "##Properties",
227 [&]() { Checkbox("Properties", &show_properties); });
228
229 } else {
231 }
232
234 ImGui::Begin("Tile16 Editor", &show_tile16_editor_,
235 ImGuiWindowFlags_MenuBar);
236 status_ = tile16_editor_.Update();
237 ImGui::End();
238 }
239
240 if (show_gfx_group) {
241 gui::BeginWindowWithDisplaySettings("Gfx Group Editor", &show_gfx_group);
242 status_ = gfx_group_editor_.Update();
244 }
245
246 if (show_properties) {
247 ImGui::Begin("Properties", &show_properties);
249 ImGui::End();
250 }
251
252 // TODO: Customizable shortcuts for the Overworld Editor
253 if (!ImGui::IsAnyItemActive()) {
254 if (ImGui::IsKeyDown(ImGuiKey_1)) {
256 } else if (ImGui::IsKeyDown(ImGuiKey_2)) {
258 } else if (ImGui::IsKeyDown(ImGuiKey_3)) {
260 } else if (ImGui::IsKeyDown(ImGuiKey_4)) {
262 } else if (ImGui::IsKeyDown(ImGuiKey_5)) {
264 } else if (ImGui::IsKeyDown(ImGuiKey_6)) {
266 } else if (ImGui::IsKeyDown(ImGuiKey_7)) {
268 } else if (ImGui::IsKeyDown(ImGuiKey_8)) {
270 }
271 }
272}
273
274constexpr std::array<const char *, 8> kMapSettingsColumnNames = {
275 "##WorldId", "##GfxId", "##PalId", "##SprGfxId",
276 "##5thCol", "##6thCol", "##7thCol", "##8thCol"};
277
279 if (BeginTable(kOWMapTable.data(), 8, kOWMapFlags, ImVec2(0, 0), -1)) {
280 for (const auto &name : kMapSettingsColumnNames)
281 ImGui::TableSetupColumn(name);
282
283 TableNextColumn();
284 ImGui::SetNextItemWidth(120.f);
285 ImGui::Combo("##world", &current_world_, kWorldList.data(), 3);
286
287 TableNextColumn();
288 ImGui::BeginGroup();
289 if (gui::InputHexByte("Gfx",
290 overworld_.mutable_overworld_map(current_map_)
291 ->mutable_area_graphics(),
295 }
296 ImGui::EndGroup();
297
298 TableNextColumn();
299 ImGui::BeginGroup();
300 if (gui::InputHexByte("Palette",
301 overworld_.mutable_overworld_map(current_map_)
302 ->mutable_area_palette(),
307 }
308 ImGui::EndGroup();
309
310 TableNextColumn();
311 ImGui::BeginGroup();
312 gui::InputHexByte("Spr Gfx",
313 overworld_.mutable_overworld_map(current_map_)
314 ->mutable_sprite_graphics(game_state_),
316 ImGui::EndGroup();
317
318 TableNextColumn();
319 ImGui::BeginGroup();
320 gui::InputHexByte("Spr Palette",
321 overworld_.mutable_overworld_map(current_map_)
322 ->mutable_sprite_palette(game_state_),
324 ImGui::EndGroup();
325
326 TableNextColumn();
327 ImGui::BeginGroup();
329 "Msg Id",
330 overworld_.mutable_overworld_map(current_map_)->mutable_message_id(),
331 kInputFieldSize + 20);
332 ImGui::EndGroup();
333
334 TableNextColumn();
335 ImGui::SetNextItemWidth(100.f);
336 ImGui::Combo("##World", &game_state_, kGamePartComboString.data(), 3);
337
338 TableNextColumn();
339 ImGui::Checkbox(
340 "##mosaic",
341 overworld_.mutable_overworld_map(current_map_)->mutable_mosaic());
342 HOVER_HINT("Enable Mosaic effect for the current map");
343
344 ImGui::EndTable();
345 }
346}
347
349 if (BeginTable(kOWMapTable.data(), 15, kOWMapFlags, ImVec2(0, 0), -1)) {
350 for (const auto &name : kMapSettingsColumnNames)
351 ImGui::TableSetupColumn(name);
352
353 TableNextColumn();
354 ImGui::SetNextItemWidth(120.f);
355 ImGui::Combo("##world", &current_world_, kWorldList.data(), 3);
356
357 static const std::array<std::string, 8> kCustomMapSettingsColumnNames = {
358 "TileGfx0", "TileGfx1", "TileGfx2", "TileGfx3",
359 "TileGfx4", "TileGfx5", "TileGfx6", "TileGfx7"};
360 for (int i = 0; i < 8; ++i) {
361 TableNextColumn();
362 ImGui::BeginGroup();
363 if (gui::InputHexByte(kCustomMapSettingsColumnNames[i].data(),
364 overworld_.mutable_overworld_map(current_map_)
365 ->mutable_custom_tileset(i),
369 }
370 ImGui::EndGroup();
371 }
372
373 TableNextColumn();
374 ImGui::BeginGroup();
375 if (gui::InputHexByte("Palette",
376 overworld_.mutable_overworld_map(current_map_)
377 ->mutable_area_palette(),
382 }
383 ImGui::EndGroup();
384
385 TableNextColumn();
386 ImGui::BeginGroup();
387 gui::InputHexByte("Spr Gfx",
388 overworld_.mutable_overworld_map(current_map_)
389 ->mutable_sprite_graphics(game_state_),
391 ImGui::EndGroup();
392
393 TableNextColumn();
394 ImGui::BeginGroup();
395 gui::InputHexByte("Spr Palette",
396 overworld_.mutable_overworld_map(current_map_)
397 ->mutable_sprite_palette(game_state_),
399 ImGui::EndGroup();
400
401 TableNextColumn();
402 ImGui::BeginGroup();
404 "Msg Id",
405 overworld_.mutable_overworld_map(current_map_)->mutable_message_id(),
406 kInputFieldSize + 20);
407 ImGui::EndGroup();
408
409 TableNextColumn();
410 ImGui::SetNextItemWidth(100.f);
411 ImGui::Combo("##World", &game_state_, kGamePartComboString.data(), 3);
412
413 TableNextColumn();
414 ImGui::Checkbox(
415 "##mosaic",
416 overworld_.mutable_overworld_map(current_map_)->mutable_mosaic());
417 HOVER_HINT("Enable Mosaic effect for the current map");
418
419 ImGui::EndTable();
420 }
421}
422
424 int xx = 0;
425 int yy = 0;
426 for (int i = 0; i < 0x40; i++) {
427 int world_index = i + (current_world_ * 0x40);
428 int map_x = (xx * kOverworldMapSize * ow_map_canvas_.global_scale());
429 int map_y = (yy * kOverworldMapSize * ow_map_canvas_.global_scale());
430 ow_map_canvas_.DrawBitmap(maps_bmp_[world_index], map_x, map_y,
431 ow_map_canvas_.global_scale());
432 xx++;
433 if (xx >= 8) {
434 yy++;
435 xx = 0;
436 }
437 }
438}
439
441 // Determine which overworld map the user is currently editing.
442 auto mouse_position = ow_map_canvas_.drawn_tile_position();
443 int map_x = mouse_position.x / kOverworldMapSize;
444 int map_y = mouse_position.y / kOverworldMapSize;
445 current_map_ = map_x + map_y * 8;
446 if (current_world_ == 1) {
447 current_map_ += 0x40;
448 } else if (current_world_ == 2) {
449 current_map_ += 0x80;
450 }
451
452 // Render the updated map bitmap.
453 RenderUpdatedMapBitmap(mouse_position,
455
456 // Calculate the correct superX and superY values
457 int superY = current_map_ / 8;
458 int superX = current_map_ % 8;
459 int mouse_x = mouse_position.x;
460 int mouse_y = mouse_position.y;
461 // Calculate the correct tile16_x and tile16_y positions
462 int tile16_x = (mouse_x % kOverworldMapSize) / (kOverworldMapSize / 32);
463 int tile16_y = (mouse_y % kOverworldMapSize) / (kOverworldMapSize / 32);
464
465 // Update the overworld_.map_tiles() based on tile16 ID and current world
466 auto &selected_world =
467 (current_world_ == 0) ? overworld_.mutable_map_tiles()->light_world
468 : (current_world_ == 1) ? overworld_.mutable_map_tiles()->dark_world
469 : overworld_.mutable_map_tiles()->special_world;
470
471 int index_x = superX * 32 + tile16_x;
472 int index_y = superY * 32 + tile16_y;
473
474 selected_world[index_x][index_y] = current_tile16_;
475}
476
478 const ImVec2 &click_position, const std::vector<uint8_t> &tile_data) {
479 // Calculate the tile index for x and y based on the click_position
480 int tile_index_x =
481 (static_cast<int>(click_position.x) % kOverworldMapSize) / kTile16Size;
482 int tile_index_y =
483 (static_cast<int>(click_position.y) % kOverworldMapSize) / kTile16Size;
484
485 // Calculate the pixel start position based on tile index and tile size
486 ImVec2 start_position;
487 start_position.x = tile_index_x * kTile16Size;
488 start_position.y = tile_index_y * kTile16Size;
489
490 // Update the bitmap's pixel data based on the start_position and tile_data
491 gfx::Bitmap &current_bitmap = maps_bmp_[current_map_];
492 for (int y = 0; y < kTile16Size; ++y) {
493 for (int x = 0; x < kTile16Size; ++x) {
494 int pixel_index =
495 (start_position.y + y) * kOverworldMapSize + (start_position.x + x);
496 current_bitmap.WriteToPixel(pixel_index, tile_data[y * kTile16Size + x]);
497 }
498 }
499
500 current_bitmap.set_modified(true);
501}
502
505
506 // User has selected a tile they want to draw from the blockset.
507 if (!blockset_canvas_.points().empty() &&
508 !ow_map_canvas_.select_rect_active()) {
509 // Left click is pressed
511 kTile16Size)) {
513 }
514 }
515
516 if (ow_map_canvas_.select_rect_active()) {
517 if (ImGui::IsMouseClicked(ImGuiMouseButton_Left) ||
518 ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
519 auto &selected_world =
520 (current_world_ == 0) ? overworld_.mutable_map_tiles()->light_world
521 : (current_world_ == 1)
522 ? overworld_.mutable_map_tiles()->dark_world
523 : overworld_.mutable_map_tiles()->special_world;
524 // new_start_pos and new_end_pos
525 auto start = ow_map_canvas_.selected_points()[0];
526 auto end = ow_map_canvas_.selected_points()[1];
527
528 // Calculate the bounds of the rectangle in terms of 16x16 tile indices
529 int start_x = std::floor(start.x / kTile16Size) * kTile16Size;
530 int start_y = std::floor(start.y / kTile16Size) * kTile16Size;
531 int end_x = std::floor(end.x / kTile16Size) * kTile16Size;
532 int end_y = std::floor(end.y / kTile16Size) * kTile16Size;
533
534 if (start_x > end_x) std::swap(start_x, end_x);
535 if (start_y > end_y) std::swap(start_y, end_y);
536
537 constexpr int local_map_size = 512; // Size of each local map
538 // Number of tiles per local map (since each tile is 16x16)
539 constexpr int tiles_per_local_map = local_map_size / kTile16Size;
540
541 for (int y = start_y, i = 0; y <= end_y; y += kTile16Size) {
542 for (int x = start_x; x <= end_x; x += kTile16Size, ++i) {
543 // Determine which local map (512x512) the tile is in
544 int local_map_x = x / local_map_size;
545 int local_map_y = y / local_map_size;
546
547 // Calculate the tile's position within its local map
548 int tile16_x = (x % local_map_size) / kTile16Size;
549 int tile16_y = (y % local_map_size) / kTile16Size;
550
551 // Calculate the index within the overall map structure
552 int index_x = local_map_x * tiles_per_local_map + tile16_x;
553 int index_y = local_map_y * tiles_per_local_map + tile16_y;
554 int tile16_id = overworld_.GetTileFromPosition(
555 ow_map_canvas_.selected_tiles()[i]);
556 selected_world[index_x][index_y] = tile16_id;
557 }
558 }
559
561 }
562 }
563}
564
566 ow_map_canvas_.DrawSelectRect(current_map_);
567
568 // Single tile case
569 if (ow_map_canvas_.selected_tile_pos().x != -1) {
571 overworld_.GetTileFromPosition(ow_map_canvas_.selected_tile_pos());
572 ow_map_canvas_.set_selected_tile_pos(ImVec2(-1, -1));
573 }
574
575 static std::vector<int> tile16_ids;
576 if (ow_map_canvas_.select_rect_active()) {
577 // Get the tile16 IDs from the selected tile ID positions
578 if (tile16_ids.size() != 0) {
579 tile16_ids.clear();
580 }
581
582 if (ow_map_canvas_.selected_tiles().size() > 0) {
583 for (auto &each : ow_map_canvas_.selected_tiles()) {
584 tile16_ids.push_back(overworld_.GetTileFromPosition(each));
585 }
586 }
587 }
588 // Create a composite image of all the tile16s selected
589 ow_map_canvas_.DrawBitmapGroup(tile16_ids, tile16_individual_, 0x10);
590}
591
593 // 4096x4096, 512x512 maps and some are larges maps 1024x1024
594 const auto mouse_position = ImGui::GetIO().MousePos;
595 const int large_map_size = 1024;
596 const auto canvas_zero_point = ow_map_canvas_.zero_point();
597
598 // Calculate which small map the mouse is currently over
599 int map_x = (mouse_position.x - canvas_zero_point.x) / kOverworldMapSize;
600 int map_y = (mouse_position.y - canvas_zero_point.y) / kOverworldMapSize;
601
602 // Calculate the index of the map in the `maps_bmp_` vector
603 current_map_ = map_x + map_y * 8;
604 const int current_highlighted_map = current_map_;
605 if (current_world_ == 1) {
606 current_map_ += 0x40;
607 } else if (current_world_ == 2) {
608 current_map_ += 0x80;
609 }
610
611 current_parent_ = overworld_.overworld_map(current_map_)->parent();
612
613 if (overworld_.overworld_map(current_map_)->is_large_map() ||
614 overworld_.overworld_map(current_map_)->large_index() != 0) {
615 const int highlight_parent =
616 overworld_.overworld_map(current_highlighted_map)->parent();
617 const int parent_map_x = highlight_parent % 8;
618 const int parent_map_y = highlight_parent / 8;
619 ow_map_canvas_.DrawOutline(parent_map_x * kOverworldMapSize,
620 parent_map_y * kOverworldMapSize, large_map_size,
621 large_map_size);
622 } else {
623 const int current_map_x = current_highlighted_map % 8;
624 const int current_map_y = current_highlighted_map / 8;
625 ow_map_canvas_.DrawOutline(current_map_x * kOverworldMapSize,
626 current_map_y * kOverworldMapSize,
628 }
629
630 if (maps_bmp_[current_map_].modified() ||
631 ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
635 maps_bmp_[current_map_].set_modified(false);
636 }
637
638 return absl::OkStatus();
639}
640
642 if (ImGui::IsMouseDragging(ImGuiMouseButton_Middle)) {
645 ow_map_canvas_.set_draggable(true);
647 }
648 if (ImGui::IsMouseReleased(ImGuiMouseButton_Middle) &&
651 ow_map_canvas_.set_draggable(false);
653 }
654}
655
657 if (all_gfx_loaded_) {
658 if (core::FeatureFlags::get().overworld.kLoadCustomOverworld) {
660 } else {
662 }
663 Separator();
664 }
665
668 ow_map_canvas_.DrawBackground();
670
673 ow_map_canvas_.DrawContextMenu();
674 } else {
675 ow_map_canvas_.set_draggable(false);
676 }
677
678 if (overworld_.is_loaded()) {
680 DrawOverworldExits(ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling());
682 ow_map_canvas_.scrolling());
687 }
688 if (IsItemHovered()) status_ = CheckForCurrentMap();
689 }
690
691 ow_map_canvas_.DrawGrid();
692 ow_map_canvas_.DrawOverlay();
693 EndChild();
694
695 // Handle mouse wheel activity
696 if (ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows) &&
697 ImGui::IsMouseDragging(ImGuiMouseButton_Middle)) {
698 ImGui::SetScrollX(ImGui::GetScrollX() + ImGui::GetIO().MouseWheelH * 16.0f);
699 ImGui::SetScrollY(ImGui::GetScrollY() + ImGui::GetIO().MouseWheel * 16.0f);
700 }
701}
702
705 ImGui::BeginGroup();
706 gui::BeginChildWithScrollbar("##Tile16SelectorScrollRegion");
707 blockset_canvas_.DrawBackground();
709 {
710 blockset_canvas_.DrawContextMenu();
711 blockset_canvas_.DrawBitmap(tile16_blockset_bmp_, /*border_offset=*/2,
713
714 if (blockset_canvas_.DrawTileSelector(32.0f)) {
715 // Open the tile16 editor to the tile
716 auto tile_pos = blockset_canvas_.points().front();
717 int grid_x = static_cast<int>(tile_pos.x / 32);
718 int grid_y = static_cast<int>(tile_pos.y / 32);
719 int id = grid_x + grid_y * 8;
720 RETURN_IF_ERROR(tile16_editor_.SetCurrentTile(id));
721 show_tile16_editor_ = true;
722 }
723
724 if (ImGui::IsItemClicked() && !blockset_canvas_.points().empty()) {
725 int x = blockset_canvas_.points().front().x / 32;
726 int y = blockset_canvas_.points().front().y / 32;
727 current_tile16_ = x + (y * 8);
728 }
729
730 blockset_canvas_.DrawGrid();
731 blockset_canvas_.DrawOverlay();
732 }
733 EndChild();
734 ImGui::EndGroup();
735 return absl::OkStatus();
736}
737
739 graphics_bin_canvas_.DrawBackground();
740 graphics_bin_canvas_.DrawContextMenu();
741 if (all_gfx_loaded_) {
742 int key = 0;
743 for (auto &value : GraphicsSheetManager::GetInstance().gfx_sheets()) {
744 int offset = 0x40 * (key + 1);
745 int top_left_y = graphics_bin_canvas_.zero_point().y + 2;
746 if (key >= 1) {
747 top_left_y = graphics_bin_canvas_.zero_point().y + 0x40 * key;
748 }
749 auto texture = value.texture();
750 graphics_bin_canvas_.draw_list()->AddImage(
751 (ImTextureID)(intptr_t)texture,
752 ImVec2(graphics_bin_canvas_.zero_point().x + 2, top_left_y),
753 ImVec2(graphics_bin_canvas_.zero_point().x + 0x100,
754 graphics_bin_canvas_.zero_point().y + offset));
755 key++;
756 }
757 }
758 graphics_bin_canvas_.DrawGrid();
759 graphics_bin_canvas_.DrawOverlay();
760}
761
763 if (overworld_.is_loaded() &&
765 overworld_.set_current_map(current_map_);
766 palette_ = overworld_.current_area_palette();
767 gfx::Bitmap bmp;
768 Renderer::GetInstance().CreateAndRenderBitmap(0x80, kOverworldMapSize, 0x08,
769 overworld_.current_graphics(),
770 bmp, palette_);
772 }
773
775 ImGui::BeginGroup();
776 gui::BeginChildWithScrollbar("##AreaGraphicsScrollRegion");
777 current_gfx_canvas_.DrawBackground();
779 {
780 current_gfx_canvas_.DrawContextMenu();
782 /*border_offset=*/2, overworld_.is_loaded());
783 current_gfx_canvas_.DrawTileSelector(32.0f);
784 current_gfx_canvas_.DrawGrid();
785 current_gfx_canvas_.DrawOverlay();
786 }
787 EndChild();
788 ImGui::EndGroup();
789 return absl::OkStatus();
790}
791
793 if (BeginTabBar(kTileSelectorTab.data(),
794 ImGuiTabBarFlags_FittingPolicyScroll)) {
795 if (BeginTabItem("Tile16")) {
797 EndTabItem();
798 }
799 if (BeginTabItem("Tile8")) {
801 gui::BeginChildWithScrollbar("##Tile8SelectorScrollRegion");
803 EndChild();
805 EndTabItem();
806 }
807 if (BeginTabItem("Area Graphics")) {
809 EndTabItem();
810 }
811 EndTabBar();
812 }
813 return absl::OkStatus();
814}
815
816void OverworldEditor::DrawOverworldEntrances(ImVec2 canvas_p0, ImVec2 scrolling,
817 bool holes) {
818 int i = 0;
819 for (auto &each : overworld_.entrances()) {
820 if (each.map_id_ < 0x40 + (current_world_ * 0x40) &&
821 each.map_id_ >= (current_world_ * 0x40) && !each.deleted) {
822 auto color = ImVec4(255, 255, 0, 100);
823 if (each.is_hole_) {
824 color = ImVec4(255, 255, 255, 200);
825 }
826 ow_map_canvas_.DrawRect(each.x_, each.y_, 16, 16, color);
827 std::string str = util::HexByte(each.entrance_id_);
828
830 HandleEntityDragging(&each, canvas_p0, scrolling, is_dragging_entity_,
832
833 if (IsMouseHoveringOverEntity(each, canvas_p0, scrolling) &&
834 ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
835 jump_to_tab_ = each.entrance_id_;
836 }
837
838 if (IsMouseHoveringOverEntity(each, canvas_p0, scrolling) &&
839 ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
841 current_entrance_ = each;
842 }
843 }
844
845 ow_map_canvas_.DrawText(str, each.x_, each.y_);
846 }
847 i++;
848 }
849
851 // Get the deleted entrance ID and insert it at the mouse position
852 auto deleted_entrance_id = overworld_.deleted_entrances().back();
853 overworld_.deleted_entrances().pop_back();
854 auto &entrance = overworld_.entrances()[deleted_entrance_id];
855 entrance.map_id_ = current_map_;
856 entrance.entrance_id_ = deleted_entrance_id;
857 entrance.x_ = ow_map_canvas_.hover_mouse_pos().x;
858 entrance.y_ = ow_map_canvas_.hover_mouse_pos().y;
859 entrance.deleted = false;
860 }
861
863 const auto is_hovering =
864 IsMouseHoveringOverEntity(current_entrance_, canvas_p0, scrolling);
865
866 if (!is_hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
867 ImGui::OpenPopup("Entrance Inserter");
868 } else {
870 overworld_.entrances()[current_entrance_id_])) {
872 }
873
874 if (overworld_.entrances()[current_entrance_id_].deleted) {
875 overworld_.mutable_deleted_entrances()->emplace_back(
877 }
878 }
879 }
880}
881
882void OverworldEditor::DrawOverworldExits(ImVec2 canvas_p0, ImVec2 scrolling) {
883 int i = 0;
884 for (auto &each : *overworld_.mutable_exits()) {
885 if (each.map_id_ < 0x40 + (current_world_ * 0x40) &&
886 each.map_id_ >= (current_world_ * 0x40) && !each.deleted_) {
887 ow_map_canvas_.DrawRect(each.x_, each.y_, 16, 16,
888 ImVec4(255, 255, 255, 150));
890 each.entity_id_ = i;
891 HandleEntityDragging(&each, ow_map_canvas_.zero_point(),
894
895 if (IsMouseHoveringOverEntity(each, canvas_p0, scrolling) &&
896 ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
897 jump_to_tab_ = each.room_id_;
898 }
899
900 if (IsMouseHoveringOverEntity(each, canvas_p0, scrolling) &&
901 ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
903 current_exit_ = each;
904 current_entity_ = &each;
905 current_entity_->entity_id_ = i;
906 ImGui::OpenPopup("Exit editor");
907 }
908 }
909
910 std::string str = util::HexByte(i);
911 ow_map_canvas_.DrawText(str, each.x_, each.y_);
912 }
913 i++;
914 }
915
918 const auto hovering = IsMouseHoveringOverEntity(
919 overworld_.mutable_exits()->at(current_exit_id_),
920 ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling());
921
922 if (!hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
923 ImGui::OpenPopup("Exit Inserter");
924 } else {
926 overworld_.mutable_exits()->at(current_exit_id_))) {
927 overworld_.mutable_exits()->at(current_exit_id_) = current_exit_;
928 }
929 }
930 }
931}
932
934 int i = 0;
935 for (auto &item : *overworld_.mutable_all_items()) {
936 // Get the item's bitmap and real X and Y positions
937 if (item.room_map_id_ < 0x40 + (current_world_ * 0x40) &&
938 item.room_map_id_ >= (current_world_ * 0x40) && !item.deleted) {
939 ow_map_canvas_.DrawRect(item.x_, item.y_, 16, 16, ImVec4(255, 0, 0, 150));
940
942 // Check if this item is being clicked and dragged
943 HandleEntityDragging(&item, ow_map_canvas_.zero_point(),
946
947 const auto hovering = IsMouseHoveringOverEntity(
948 item, ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling());
949 if (hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
951 current_item_ = item;
952 current_entity_ = &item;
953 }
954 }
955 std::string item_name = "";
956 if (item.id_ < zelda3::kSecretItemNames.size()) {
957 item_name = zelda3::kSecretItemNames[item.id_];
958 } else {
959 item_name = absl::StrFormat("0x%02X", item.id_);
960 }
961 ow_map_canvas_.DrawText(item_name, item.x_, item.y_);
962 }
963 i++;
964 }
965
968 const auto hovering = IsMouseHoveringOverEntity(
969 overworld_.mutable_all_items()->at(current_item_id_),
970 ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling());
971
972 if (!hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
973 ImGui::OpenPopup("Item Inserter");
974 } else {
976 overworld_.mutable_all_items()->at(current_item_id_))) {
977 overworld_.mutable_all_items()->at(current_item_id_) = current_item_;
978 }
979 }
980 }
981}
982
984 int i = 0;
985 for (auto &sprite : *overworld_.mutable_sprites(game_state_)) {
986 if (!sprite.deleted()) {
987 int map_x = sprite.map_x();
988 int map_y = sprite.map_y();
989 ow_map_canvas_.DrawRect(map_x, map_y, kTile16Size, kTile16Size,
990 /*magenta=*/ImVec4(255, 0, 255, 150));
992 HandleEntityDragging(&sprite, ow_map_canvas_.zero_point(),
995 if (IsMouseHoveringOverEntity(sprite, ow_map_canvas_.zero_point(),
996 ow_map_canvas_.scrolling()) &&
997 ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
999 current_sprite_ = sprite;
1000 }
1001 }
1002 if (core::FeatureFlags::get().overworld.kDrawOverworldSprites) {
1003 if (sprite_previews_[sprite.id()].is_active()) {
1004 ow_map_canvas_.DrawBitmap(sprite_previews_[sprite.id()], map_x, map_y,
1005 2.0f);
1006 }
1007 }
1008
1009 ow_map_canvas_.DrawText(absl::StrFormat("%s", sprite.name()), map_x,
1010 map_y);
1011 }
1012 i++;
1013 }
1014
1017 const auto hovering = IsMouseHoveringOverEntity(
1018 overworld_.mutable_sprites(game_state_)->at(current_sprite_id_),
1019 ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling());
1020
1021 if (!hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
1022 ImGui::OpenPopup("Sprite Inserter");
1023 } else {
1024 if (DrawSpriteEditorPopup(overworld_.mutable_sprites(game_state_)
1025 ->at(current_sprite_id_))) {
1026 overworld_.mutable_sprites(game_state_)->at(current_sprite_id_) =
1028 }
1029 }
1030 }
1031}
1032
1034 if (core::FeatureFlags::get().overworld.kSaveOverworldMaps) {
1035 RETURN_IF_ERROR(overworld_.CreateTile32Tilemap());
1036 RETURN_IF_ERROR(overworld_.SaveMap32Tiles());
1037 RETURN_IF_ERROR(overworld_.SaveMap16Tiles());
1038 RETURN_IF_ERROR(overworld_.SaveOverworldMaps());
1039 }
1040 if (core::FeatureFlags::get().overworld.kSaveOverworldEntrances) {
1041 RETURN_IF_ERROR(overworld_.SaveEntrances());
1042 }
1043 if (core::FeatureFlags::get().overworld.kSaveOverworldExits) {
1044 RETURN_IF_ERROR(overworld_.SaveExits());
1045 }
1046 if (core::FeatureFlags::get().overworld.kSaveOverworldItems) {
1047 RETURN_IF_ERROR(overworld_.SaveItems());
1048 }
1049 if (core::FeatureFlags::get().overworld.kSaveOverworldProperties) {
1050 RETURN_IF_ERROR(overworld_.SaveMapProperties());
1051 }
1052 return absl::OkStatus();
1053}
1054
1056 util::logf("Loading overworld.");
1057 // Load the Link to the Past overworld.
1059 palette_ = overworld_.current_area_palette();
1060
1061 util::logf("Loading overworld graphics.");
1062 // Create the area graphics image
1063 Renderer::GetInstance().CreateAndRenderBitmap(0x80, kOverworldMapSize, 0x40,
1064 overworld_.current_graphics(),
1066
1067 util::logf("Loading overworld tileset.");
1068 // Create the tile16 blockset image
1069 Renderer::GetInstance().CreateAndRenderBitmap(
1070 0x80, 0x2000, 0x08, overworld_.tile16_blockset_data(),
1072 map_blockset_loaded_ = true;
1073
1074 // Copy the tile16 data into individual tiles.
1075 auto tile16_data = overworld_.tile16_blockset_data();
1076
1077 util::logf("Loading overworld tile16 graphics.");
1078 // Loop through the tiles and copy their pixel data into separate vectors
1079 for (unsigned int i = 0; i < zelda3::kNumTile16Individual; i++) {
1082
1083 // Copy the pixel data for the current tile into the vector
1084 for (int ty = 0; ty < kTile16Size; ty++) {
1085 for (int tx = 0; tx < kTile16Size; tx++) {
1086 int position = tx + (ty * kTile16Size);
1087 uint8_t value =
1088 tile16_data[(i % 8 * kTile16Size) + (i / 8 * kTile16Size * 0x80) +
1089 (ty * 0x80) + tx];
1090 tile16_individual_[i].mutable_data()[position] = value;
1091 }
1092 }
1093
1095 Renderer::GetInstance().RenderBitmap(&tile16_individual_[i]);
1096 }
1097
1098 util::logf("Loading overworld maps.");
1099 // Render the overworld maps loaded from the ROM.
1100 for (int i = 0; i < zelda3::kNumOverworldMaps; ++i) {
1101 overworld_.set_current_map(i);
1102 auto palette = overworld_.current_area_palette();
1103 try {
1104 Renderer::GetInstance().CreateAndRenderBitmap(
1106 overworld_.current_map_bitmap_data(), maps_bmp_[i], palette);
1107 } catch (const std::bad_alloc &e) {
1108 std::cout << "Error: " << e.what() << std::endl;
1109 continue;
1110 }
1111 }
1112
1113 if (core::FeatureFlags::get().overworld.kDrawOverworldSprites) {
1115 }
1116
1117 return absl::OkStatus();
1118}
1119
1121 // Render the sprites for each Overworld map
1122 const int depth = 0x10;
1123 for (int i = 0; i < 3; i++)
1124 for (auto const &sprite : *overworld_.mutable_sprites(i)) {
1125 int width = sprite.width();
1126 int height = sprite.height();
1127 if (width == 0 || height == 0) {
1128 continue;
1129 }
1130 if (sprite_previews_.size() < sprite.id()) {
1131 sprite_previews_.resize(sprite.id() + 1);
1132 }
1133 sprite_previews_[sprite.id()].Create(width, height, depth,
1134 *sprite.preview_graphics());
1135 RETURN_IF_ERROR(sprite_previews_[sprite.id()].SetPalette(palette_));
1136 Renderer::GetInstance().RenderBitmap(&(sprite_previews_[sprite.id()]));
1137 }
1138 return absl::OkStatus();
1139}
1140
1142 overworld_.mutable_overworld_map(map_index)->LoadAreaGraphics();
1143 status_ = overworld_.mutable_overworld_map(map_index)->BuildTileset();
1145 status_ = overworld_.mutable_overworld_map(map_index)->BuildTiles16Gfx(
1146 *overworld_.mutable_tiles16(), overworld_.tiles16().size());
1148 status_ = overworld_.mutable_overworld_map(map_index)->BuildBitmap(
1149 overworld_.GetMapTiles(current_world_));
1150 maps_bmp_[map_index].set_data(
1151 overworld_.mutable_overworld_map(map_index)->bitmap_data());
1152 maps_bmp_[map_index].set_modified(true);
1154}
1155
1157 std::vector<std::future<void>> futures;
1158 int indices[4];
1159
1160 auto refresh_map_async = [this](int map_index) {
1161 RefreshChildMap(map_index);
1162 };
1163
1164 int source_map_id = current_map_;
1165 bool is_large = overworld_.overworld_map(current_map_)->is_large_map();
1166 if (is_large) {
1167 source_map_id = current_parent_;
1168 // We need to update the map and its siblings if it's a large map
1169 for (int i = 1; i < 4; i++) {
1170 int sibling_index = overworld_.overworld_map(source_map_id)->parent() + i;
1171 if (i >= 2) sibling_index += 6;
1172 futures.push_back(
1173 std::async(std::launch::async, refresh_map_async, sibling_index));
1174 indices[i] = sibling_index;
1175 }
1176 }
1177 indices[0] = source_map_id;
1178 futures.push_back(
1179 std::async(std::launch::async, refresh_map_async, source_map_id));
1180
1181 for (auto &each : futures) {
1182 each.wait();
1183 each.get();
1184 }
1185 int n = is_large ? 4 : 1;
1186 // We do texture updating on the main thread
1187 for (int i = 0; i < n; ++i) {
1188 Renderer::GetInstance().UpdateBitmap(&maps_bmp_[indices[i]]);
1189 }
1190}
1191
1194 overworld_.mutable_overworld_map(current_map_)->LoadPalette());
1195 const auto current_map_palette = overworld_.current_area_palette();
1196
1197 if (overworld_.overworld_map(current_map_)->is_large_map()) {
1198 // We need to update the map and its siblings if it's a large map
1199 for (int i = 1; i < 4; i++) {
1200 int sibling_index = overworld_.overworld_map(current_map_)->parent() + i;
1201 if (i >= 2) sibling_index += 6;
1203 overworld_.mutable_overworld_map(sibling_index)->LoadPalette());
1204 RETURN_IF_ERROR(maps_bmp_[sibling_index].SetPalette(current_map_palette));
1205 }
1206 }
1207
1208 RETURN_IF_ERROR(maps_bmp_[current_map_].SetPalette(current_map_palette));
1209 return absl::OkStatus();
1210}
1211
1213 auto &current_ow_map = *overworld_.mutable_overworld_map(current_map_);
1214 if (current_ow_map.is_large_map()) {
1215 // We need to copy the properties from the parent map to the children
1216 for (int i = 1; i < 4; i++) {
1217 int sibling_index = current_ow_map.parent() + i;
1218 if (i >= 2) {
1219 sibling_index += 6;
1220 }
1221 auto &map = *overworld_.mutable_overworld_map(sibling_index);
1222 map.set_area_graphics(current_ow_map.area_graphics());
1223 map.set_area_palette(current_ow_map.area_palette());
1224 map.set_sprite_graphics(game_state_,
1225 current_ow_map.sprite_graphics(game_state_));
1226 map.set_sprite_palette(game_state_,
1227 current_ow_map.sprite_palette(game_state_));
1228 map.set_message_id(current_ow_map.message_id());
1229 }
1230 }
1231}
1232
1234 if (current_blockset_ ==
1235 overworld_.overworld_map(current_map_)->area_graphics()) {
1236 return absl::OkStatus();
1237 }
1238 current_blockset_ = overworld_.overworld_map(current_map_)->area_graphics();
1239
1240 overworld_.set_current_map(current_map_);
1241 palette_ = overworld_.current_area_palette();
1242 // Create the tile16 blockset image
1245
1246 // Copy the tile16 data into individual tiles.
1247 const auto tile16_data = overworld_.tile16_blockset_data();
1248
1249 // Loop through the tiles and copy their pixel data into separate vectors
1250 std::vector<std::future<absl::Status>> futures;
1251 for (unsigned int i = 0; i < zelda3::kNumTile16Individual; i++) {
1252 futures.push_back(std::async(
1253 std::launch::async,
1254 [&](int index) -> absl::Status {
1255 std::vector<uint8_t> tile_data(16 * 16, 0x00);
1256 for (int ty = 0; ty < 16; ty++) {
1257 for (int tx = 0; tx < 16; tx++) {
1258 int position = tx + (ty * 0x10);
1259 uint8_t value =
1260 tile16_data[(index % 8 * 16) + (index / 8 * 16 * 0x80) +
1261 (ty * 0x80) + tx];
1262 tile_data[position] = value;
1263 }
1264 }
1265 tile16_individual_[index].set_data(tile_data);
1266 RETURN_IF_ERROR(tile16_individual_[index].SetPalette(palette_));
1267 return absl::OkStatus();
1268 },
1269 i));
1270 }
1271
1272 for (auto &future : futures) {
1273 future.wait();
1274 RETURN_IF_ERROR(future.get());
1275 }
1276
1277 // Render the bitmaps of each tile.
1278 for (unsigned int id = 0; id < zelda3::kNumTile16Individual; id++) {
1279 Renderer::GetInstance().UpdateBitmap(&tile16_individual_[id]);
1280 }
1281
1282 return absl::OkStatus();
1283}
1284
1286 static bool init_properties = false;
1287
1288 if (!init_properties) {
1289 for (int i = 0; i < 0x40; i++) {
1290 std::string area_graphics_str = absl::StrFormat(
1291 "%02hX", overworld_.overworld_map(i)->area_graphics());
1293 ->push_back(area_graphics_str);
1294
1295 area_graphics_str = absl::StrFormat(
1296 "%02hX", overworld_.overworld_map(i + 0x40)->area_graphics());
1298 ->push_back(area_graphics_str);
1299
1300 std::string area_palette_str =
1301 absl::StrFormat("%02hX", overworld_.overworld_map(i)->area_palette());
1303 ->push_back(area_palette_str);
1304
1305 area_palette_str = absl::StrFormat(
1306 "%02hX", overworld_.overworld_map(i + 0x40)->area_palette());
1308 ->push_back(area_palette_str);
1309 std::string sprite_gfx_str = absl::StrFormat(
1310 "%02hX", overworld_.overworld_map(i)->sprite_graphics(1));
1312 ->push_back(sprite_gfx_str);
1313
1314 sprite_gfx_str = absl::StrFormat(
1315 "%02hX", overworld_.overworld_map(i)->sprite_graphics(2));
1317 ->push_back(sprite_gfx_str);
1318
1319 sprite_gfx_str = absl::StrFormat(
1320 "%02hX", overworld_.overworld_map(i + 0x40)->sprite_graphics(1));
1322 ->push_back(sprite_gfx_str);
1323
1324 sprite_gfx_str = absl::StrFormat(
1325 "%02hX", overworld_.overworld_map(i + 0x40)->sprite_graphics(2));
1327 ->push_back(sprite_gfx_str);
1328
1329 std::string sprite_palette_str = absl::StrFormat(
1330 "%02hX", overworld_.overworld_map(i)->sprite_palette(1));
1332 ->push_back(sprite_palette_str);
1333
1334 sprite_palette_str = absl::StrFormat(
1335 "%02hX", overworld_.overworld_map(i)->sprite_palette(2));
1337 ->push_back(sprite_palette_str);
1338
1339 sprite_palette_str = absl::StrFormat(
1340 "%02hX", overworld_.overworld_map(i + 0x40)->sprite_palette(1));
1342 ->push_back(sprite_palette_str);
1343
1344 sprite_palette_str = absl::StrFormat(
1345 "%02hX", overworld_.overworld_map(i + 0x40)->sprite_palette(2));
1347 ->push_back(sprite_palette_str);
1348 }
1349 init_properties = true;
1350 }
1351
1352 Text("Area Gfx LW/DW");
1353 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 8, 2.0f, 32,
1355 SameLine();
1356 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 8, 2.0f, 32,
1358 ImGui::Separator();
1359
1360 Text("Sprite Gfx LW/DW");
1361 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 8, 2.0f, 32,
1363 SameLine();
1364 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 8, 2.0f, 32,
1366 SameLine();
1367 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 8, 2.0f, 32,
1369 SameLine();
1370 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 8, 2.0f, 32,
1372 ImGui::Separator();
1373
1374 Text("Area Pal LW/DW");
1375 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 8, 2.0f, 32,
1377 SameLine();
1378 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 8, 2.0f, 32,
1380
1381 static bool show_gfx_group = false;
1382 Checkbox("Show Gfx Group Editor", &show_gfx_group);
1383 if (show_gfx_group) {
1384 gui::BeginWindowWithDisplaySettings("Gfx Group Editor", &show_gfx_group);
1385 status_ = gfx_group_editor_.Update();
1387 }
1388}
1389
1391 if (BeginTable("UsageStatsTable", 3, kOWEditFlags, ImVec2(0, 0))) {
1392 TableSetupColumn("Entrances");
1393 TableSetupColumn("Grid", ImGuiTableColumnFlags_WidthStretch,
1394 ImGui::GetContentRegionAvail().x);
1395 TableSetupColumn("Usage", ImGuiTableColumnFlags_WidthFixed, 256);
1396 TableHeadersRow();
1397 TableNextRow();
1398
1399 TableNextColumn();
1400 if (BeginChild("UnusedSpritesetScroll", ImVec2(0, 0), true,
1401 ImGuiWindowFlags_HorizontalScrollbar)) {
1402 for (int i = 0; i < 0x81; i++) {
1403 auto entrance_name = rom_.resource_label()->CreateOrGetLabel(
1404 "Dungeon Entrance Names", util::HexByte(i),
1405 zelda3::kEntranceNames[i].data());
1406 std::string str = absl::StrFormat("%#x - %s", i, entrance_name);
1407 if (Selectable(str.c_str(), selected_entrance_ == i,
1408 overworld_.entrances().at(i).deleted
1409 ? ImGuiSelectableFlags_Disabled
1410 : 0)) {
1412 selected_usage_map_ = overworld_.entrances().at(i).map_id_;
1413 properties_canvas_.set_highlight_tile_id(selected_usage_map_);
1414 }
1415 if (IsItemHovered()) {
1416 BeginTooltip();
1417 Text("Entrance ID: %d", i);
1418 Text("Map ID: %d", overworld_.entrances().at(i).map_id_);
1419 Text("Entrance ID: %d", overworld_.entrances().at(i).entrance_id_);
1420 Text("X: %d", overworld_.entrances().at(i).x_);
1421 Text("Y: %d", overworld_.entrances().at(i).y_);
1422 Text("Deleted? %s",
1423 overworld_.entrances().at(i).deleted ? "Yes" : "No");
1424 EndTooltip();
1425 }
1426 }
1427 EndChild();
1428 }
1429
1430 TableNextColumn();
1431 DrawUsageGrid();
1432
1433 TableNextColumn();
1435
1436 EndTable();
1437 }
1438 return absl::OkStatus();
1439}
1440
1442 // Create a grid of 8x8 squares
1443 int total_squares = 128;
1444 int squares_wide = 8;
1445 int squares_tall = (total_squares + squares_wide - 1) /
1446 squares_wide; // Ceiling of total_squares/squares_wide
1447
1448 // Loop through each row
1449 for (int row = 0; row < squares_tall; ++row) {
1450 NewLine();
1451
1452 for (int col = 0; col < squares_wide; ++col) {
1453 if (row * squares_wide + col >= total_squares) {
1454 break;
1455 }
1456 // Determine if this square should be highlighted
1457 bool highlight = selected_usage_map_ == (row * squares_wide + col);
1458
1459 // Set highlight color if needed
1460 if (highlight) {
1461 PushStyleColor(ImGuiCol_Button,
1462 ImVec4(1.0f, 0.5f, 0.0f,
1463 1.0f)); // Or any highlight color
1464 }
1465
1466 // Create a button or selectable for each square
1467 if (Button("##square", ImVec2(20, 20))) {
1468 // Switch over to the room editor tab
1469 // and add a room tab by the ID of the square
1470 // that was clicked
1471 }
1472
1473 // Reset style if it was highlighted
1474 if (highlight) {
1475 PopStyleColor();
1476 }
1477
1478 // Check if the square is hovered
1479 if (IsItemHovered()) {
1480 // Display a tooltip with all the room properties
1481 }
1482
1483 // Keep squares in the same line
1484 SameLine();
1485 }
1486 }
1487}
1488
1490 Text("Current Map: %d", current_map_);
1491 Text("Current Tile16: %d", current_tile16_);
1492 int relative_x = (int)ow_map_canvas_.drawn_tile_position().x % 512;
1493 int relative_y = (int)ow_map_canvas_.drawn_tile_position().y % 512;
1494 Text("Current Tile16 Drawn Position (Relative): %d, %d", relative_x,
1495 relative_y);
1496
1497 // Print the size of the overworld map_tiles per world
1498 Text("Light World Map Tiles: %d",
1499 (int)overworld_.mutable_map_tiles()->light_world.size());
1500 Text("Dark World Map Tiles: %d",
1501 (int)overworld_.mutable_map_tiles()->dark_world.size());
1502 Text("Special World Map Tiles: %d",
1503 (int)overworld_.mutable_map_tiles()->special_world.size());
1504
1505 static bool view_lw_map_tiles = false;
1506 static MemoryEditor mem_edit;
1507 // Let's create buttons which let me view containers in the memory editor
1508 if (Button("View Light World Map Tiles")) {
1509 view_lw_map_tiles = !view_lw_map_tiles;
1510 }
1511
1512 if (view_lw_map_tiles) {
1513 mem_edit.DrawContents(
1514 overworld_.mutable_map_tiles()->light_world[current_map_].data(),
1515 overworld_.mutable_map_tiles()->light_world[current_map_].size());
1516 }
1517}
1518
1519} // namespace editor
1520} // namespace yaze
static GraphicsSheetManager & GetInstance()
Definition rom.h:270
static Flags & get()
Definition features.h:69
zelda3::OverworldItem current_item_
zelda3::OverworldEntranceTileTypes entrance_tiletypes_
zelda3::OverworldEntrance current_entrance_
absl::Status CheckForCurrentMap()
Check for changes to the overworld map. Calls RefreshOverworldMap and RefreshTile16Blockset on the cu...
void DrawOverworldExits(ImVec2 zero, ImVec2 scrolling)
absl::Status Undo() override
zelda3::GameEntity * dragged_entity_
std::array< gfx::Bitmap, zelda3::kNumOverworldMaps > maps_bmp_
void CheckForOverworldEdits()
Check for changes to the overworld map.
zelda3::OverworldExit current_exit_
void RenderUpdatedMapBitmap(const ImVec2 &click_position, const std::vector< uint8_t > &tile_data)
absl::Status Redo() override
void CheckForSelectRectangle()
Draw and create the tile16 IDs that are currently selected.
absl::Status Load() override
std::vector< gfx::Bitmap > sprite_previews_
absl::Status LoadGraphics()
Load the Bitmap objects for each OverworldMap.
std::array< gfx::Bitmap, zelda3::kNumTile16Individual > tile16_individual_
void DrawOverworldEntrances(ImVec2 canvas_p, ImVec2 scrolling, bool holes=false)
zelda3::GameEntity * current_entity_
static Renderer & GetInstance()
Definition renderer.h:26
Represents a bitmap image.
Definition bitmap.h:67
void WriteToPixel(int position, uint8_t value)
Definition bitmap.h:137
void set_modified(bool modified)
Definition bitmap.h:174
#define ICON_MD_GRID_VIEW
Definition icons.h:895
#define ICON_MD_MORE_VERT
Definition icons.h:1241
#define ICON_MD_DRAW
Definition icons.h:623
#define ICON_MD_ZOOM_OUT
Definition icons.h:2191
#define ICON_MD_OPEN_IN_FULL
Definition icons.h:1351
#define ICON_MD_TABLE_CHART
Definition icons.h:1928
#define ICON_MD_REDO
Definition icons.h:1568
#define ICON_MD_GRASS
Definition icons.h:889
#define ICON_MD_DOOR_BACK
Definition icons.h:610
#define ICON_MD_MUSIC_NOTE
Definition icons.h:1262
#define ICON_MD_DOOR_FRONT
Definition icons.h:611
#define ICON_MD_ADD_LOCATION
Definition icons.h:98
#define ICON_MD_ZOOM_IN
Definition icons.h:2189
#define ICON_MD_PEST_CONTROL_RODENT
Definition icons.h:1428
#define ICON_MD_CONTENT_COPY
Definition icons.h:463
#define ICON_MD_UNDO
Definition icons.h:2034
#define ICON_MD_PAN_TOOL_ALT
Definition icons.h:1370
#define PRINT_IF_ERROR(expression)
Definition macro.h:25
#define RETURN_IF_ERROR(expression)
Definition macro.h:51
#define ASSIGN_OR_RETURN(type_variable_name, expression)
Definition macro.h:59
#define HOVER_HINT(string)
Definition macro.h:22
void CopyImageToClipboard(const std::vector< uint8_t > &data)
Definition clipboard.cc:9
Editors are the view controllers for the application.
void DrawExitInserterPopup()
Definition entity.cc:162
constexpr absl::string_view kOWMapTable
constexpr ImGuiTableFlags kOWMapFlags
constexpr unsigned int kOverworldMapSize
bool DrawSpriteEditorPopup(zelda3::Sprite &sprite)
Definition entity.cc:452
void DrawItemInsertPopup()
Definition entity.cc:312
bool DrawEntranceInserterPopup()
Definition entity.cc:105
void DrawSpriteInserterPopup()
Definition entity.cc:428
constexpr absl::string_view kWorldList
constexpr absl::string_view kGamePartComboString
void HandleEntityDragging(zelda3::GameEntity *entity, ImVec2 canvas_p0, ImVec2 scrolling, bool &is_dragging_entity, zelda3::GameEntity *&dragged_entity, zelda3::GameEntity *&current_entity, bool free_movement)
Definition entity.cc:56
bool DrawOverworldEntrancePopup(zelda3::OverworldEntrance &entrance)
Definition entity.cc:130
bool IsMouseHoveringOverEntity(const zelda3::GameEntity &entity, ImVec2 canvas_p0, ImVec2 scrolling)
Definition entity.cc:21
absl::Status DisplayPalette(gfx::SnesPalette &palette, bool loaded)
bool DrawItemEditorPopup(zelda3::OverworldItem &item)
Definition entity.cc:342
constexpr int kTile16Size
constexpr ImGuiTableFlags kOWEditFlags
constexpr float kInputFieldSize
Definition entity.cc:19
constexpr absl::string_view kTileSelectorTab
constexpr std::array< const char *, 8 > kMapSettingsColumnNames
bool DrawExitEditorPopup(zelda3::OverworldExit &exit)
Definition entity.cc:180
void Render(Node &node)
Render a zeml tree.
Definition zeml.cc:380
std::string LoadFile(const std::string &filename)
Definition zeml.cc:589
void Bind(Node *node, std::function< void()> callback)
Bind a callback to a node.
Definition zeml.cc:566
Node Parse(const std::string &yazon_input, const std::map< std::string, void * > &data_bindings)
Parse a zeml string.
Definition zeml.cc:359
void BeginPadding(int i)
Definition style.cc:372
void BeginChildBothScrollbars(int id)
Definition style.cc:389
bool InputHexWord(const char *label, uint16_t *data, float input_width, bool no_step)
Definition input.cc:153
void AddTableColumn(Table &table, const std::string &label, GuiElement element)
Definition input.cc:337
void EndNoPadding()
Definition style.cc:382
void DrawTable(Table &params)
Definition input.cc:343
void EndPadding()
Definition style.cc:376
void BeginNoPadding()
Definition style.cc:378
void EndWindowWithDisplaySettings()
Definition style.cc:367
void BeginWindowWithDisplaySettings(const char *id, bool *active, const ImVec2 &size, ImGuiWindowFlags flags)
Definition style.cc:347
bool InputHexByte(const char *label, uint8_t *data, float input_width, bool no_step)
Definition input.cc:167
void BeginChildWithScrollbar(const char *str_id)
Definition style.cc:384
std::string HexByte(uint8_t byte, HexStringParams params)
Definition hex.cc:33
absl::StatusOr< OverworldEntranceTileTypes > LoadEntranceTileTypes(Rom &rom)
constexpr std::string_view kEntranceNames[]
Definition common.h:48
constexpr int kNumTile16Individual
Definition overworld.h:97
constexpr int kNumOverworldMaps
Definition overworld.h:96
const std::vector< std::string > kSecretItemNames
Main namespace for the application.
Definition controller.cc:18