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 status_ = absl::OkStatus();
64 if (rom_.is_loaded() && !all_gfx_loaded_) {
67 *overworld_.mutable_all_tiles_types()));
69 all_gfx_loaded_ = true;
70 }
71
74 }
75
76 // Draw the overworld editor layout from the ZEML file
78 return status_;
79}
80
82 static bool use_work_area = true;
83 static ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration |
84 ImGuiWindowFlags_NoMove |
85 ImGuiWindowFlags_NoSavedSettings;
86 const ImGuiViewport *viewport = ImGui::GetMainViewport();
87 ImGui::SetNextWindowPos(use_work_area ? viewport->WorkPos : viewport->Pos);
88 ImGui::SetNextWindowSize(use_work_area ? viewport->WorkSize : viewport->Size);
89 if (ImGui::Begin("Fullscreen Overworld Editor", &overworld_canvas_fullscreen_,
90 flags)) {
91 // Draws the toolset for editing the Overworld.
94 }
95 ImGui::End();
96}
97
99 static bool show_gfx_group = false;
100 static bool show_properties = false;
101
102 if (toolset_table_.column_contents.empty()) {
103 gui::AddTableColumn(toolset_table_, "##Undo", [&]() {
104 if (Button(ICON_MD_UNDO)) status_ = Undo();
105 });
106 gui::AddTableColumn(toolset_table_, "##Redo", [&]() {
107 if (Button(ICON_MD_REDO)) status_ = Redo();
108 });
110 gui::AddTableColumn(toolset_table_, "##ZoomOut", [&]() {
111 if (Button(ICON_MD_ZOOM_OUT)) ow_map_canvas_.ZoomOut();
112 });
113 gui::AddTableColumn(toolset_table_, "##ZoomIn", [&]() {
114 if (Button(ICON_MD_ZOOM_IN)) ow_map_canvas_.ZoomIn();
115 });
116 gui::AddTableColumn(toolset_table_, "##Fullscreen", [&]() {
117 if (Button(ICON_MD_OPEN_IN_FULL))
119 HOVER_HINT("Fullscreen Canvas")
120 });
122 gui::AddTableColumn(toolset_table_, "##Pan", [&]() {
125 ow_map_canvas_.set_draggable(true);
126 }
127 HOVER_HINT("Pan (Right click and drag)");
128 });
129 gui::AddTableColumn(toolset_table_, "##DrawTile", [&]() {
132 }
133 HOVER_HINT("Draw Tile");
134 });
135 gui::AddTableColumn(toolset_table_, "##Entrances", [&]() {
136 if (Selectable(ICON_MD_DOOR_FRONT,
139 HOVER_HINT("Entrances");
140 });
141 gui::AddTableColumn(toolset_table_, "##Exits", [&]() {
144 HOVER_HINT("Exits");
145 });
146 gui::AddTableColumn(toolset_table_, "##Items", [&]() {
149 HOVER_HINT("Items");
150 });
151 gui::AddTableColumn(toolset_table_, "##Sprites", [&]() {
152 if (Selectable(ICON_MD_PEST_CONTROL_RODENT,
155 HOVER_HINT("Sprites");
156 });
157 gui::AddTableColumn(toolset_table_, "##Transports", [&]() {
158 if (Selectable(ICON_MD_ADD_LOCATION,
161 HOVER_HINT("Transports");
162 });
163 gui::AddTableColumn(toolset_table_, "##Music", [&]() {
166 HOVER_HINT("Music");
167 });
168 gui::AddTableColumn(toolset_table_, "##Tile16Editor", [&]() {
170 HOVER_HINT("Tile16 Editor");
171 });
172 gui::AddTableColumn(toolset_table_, "##GfxGroupEditor", [&]() {
173 if (Button(ICON_MD_TABLE_CHART)) show_gfx_group = !show_gfx_group;
174 HOVER_HINT("Gfx Group Editor");
175 });
177 gui::AddTableColumn(toolset_table_, "##Properties", [&]() {
178 if (Button(ICON_MD_CONTENT_COPY)) {
179 std::vector<uint8_t> png_data;
180 png_data = maps_bmp_[current_map_].GetPngData();
181 if (png_data.size() > 0) {
183 } else {
184 status_ = absl::InternalError(
185 "Failed to convert overworld map surface to PNG");
186 }
187 }
188 HOVER_HINT("Copy Map to Clipboard");
189 });
190 gui::AddTableColumn(toolset_table_, "##Palette", [&]() {
192 });
194 gui::AddTableColumn(toolset_table_, "##Properties",
195 [&]() { Checkbox("Properties", &show_properties); });
196
197 } else {
199 }
200
202 ImGui::Begin("Tile16 Editor", &show_tile16_editor_,
203 ImGuiWindowFlags_MenuBar);
204 status_ = tile16_editor_.Update();
205 ImGui::End();
206 }
207
208 if (show_gfx_group) {
209 gui::BeginWindowWithDisplaySettings("Gfx Group Editor", &show_gfx_group);
210 status_ = gfx_group_editor_.Update();
212 }
213
214 if (show_properties) {
215 ImGui::Begin("Properties", &show_properties);
217 ImGui::End();
218 }
219
220 // TODO: Customizable shortcuts for the Overworld Editor
221 if (!ImGui::IsAnyItemActive()) {
222 if (ImGui::IsKeyDown(ImGuiKey_1)) {
224 } else if (ImGui::IsKeyDown(ImGuiKey_2)) {
226 } else if (ImGui::IsKeyDown(ImGuiKey_3)) {
228 } else if (ImGui::IsKeyDown(ImGuiKey_4)) {
230 } else if (ImGui::IsKeyDown(ImGuiKey_5)) {
232 } else if (ImGui::IsKeyDown(ImGuiKey_6)) {
234 } else if (ImGui::IsKeyDown(ImGuiKey_7)) {
236 } else if (ImGui::IsKeyDown(ImGuiKey_8)) {
238 }
239 }
240}
241
242constexpr std::array<const char *, 8> kMapSettingsColumnNames = {
243 "##WorldId", "##GfxId", "##PalId", "##SprGfxId",
244 "##5thCol", "##6thCol", "##7thCol", "##8thCol"};
245
247 if (BeginTable(kOWMapTable.data(), 8, kOWMapFlags, ImVec2(0, 0), -1)) {
248 for (const auto &name : kMapSettingsColumnNames)
249 ImGui::TableSetupColumn(name);
250
251 TableNextColumn();
252 ImGui::SetNextItemWidth(120.f);
253 ImGui::Combo("##world", &current_world_, kWorldList.data(), 3);
254
255 TableNextColumn();
256 ImGui::BeginGroup();
257 if (gui::InputHexByte("Gfx",
258 overworld_.mutable_overworld_map(current_map_)
259 ->mutable_area_graphics(),
263 }
264 ImGui::EndGroup();
265
266 TableNextColumn();
267 ImGui::BeginGroup();
268 if (gui::InputHexByte("Palette",
269 overworld_.mutable_overworld_map(current_map_)
270 ->mutable_area_palette(),
275 }
276 ImGui::EndGroup();
277
278 TableNextColumn();
279 ImGui::BeginGroup();
280 gui::InputHexByte("Spr Gfx",
281 overworld_.mutable_overworld_map(current_map_)
282 ->mutable_sprite_graphics(game_state_),
284 ImGui::EndGroup();
285
286 TableNextColumn();
287 ImGui::BeginGroup();
288 gui::InputHexByte("Spr Palette",
289 overworld_.mutable_overworld_map(current_map_)
290 ->mutable_sprite_palette(game_state_),
292 ImGui::EndGroup();
293
294 TableNextColumn();
295 ImGui::BeginGroup();
297 "Msg Id",
298 overworld_.mutable_overworld_map(current_map_)->mutable_message_id(),
299 kInputFieldSize + 20);
300 ImGui::EndGroup();
301
302 TableNextColumn();
303 ImGui::SetNextItemWidth(100.f);
304 ImGui::Combo("##World", &game_state_, kGamePartComboString.data(), 3);
305
306 TableNextColumn();
307 ImGui::Checkbox(
308 "##mosaic",
309 overworld_.mutable_overworld_map(current_map_)->mutable_mosaic());
310 HOVER_HINT("Enable Mosaic effect for the current map");
311
312 ImGui::EndTable();
313 }
314}
315
317 if (BeginTable(kOWMapTable.data(), 15, kOWMapFlags, ImVec2(0, 0), -1)) {
318 for (const auto &name : kMapSettingsColumnNames)
319 ImGui::TableSetupColumn(name);
320
321 TableNextColumn();
322 ImGui::SetNextItemWidth(120.f);
323 ImGui::Combo("##world", &current_world_, kWorldList.data(), 3);
324
325 static const std::array<std::string, 8> kCustomMapSettingsColumnNames = {
326 "TileGfx0", "TileGfx1", "TileGfx2", "TileGfx3",
327 "TileGfx4", "TileGfx5", "TileGfx6", "TileGfx7"};
328 for (int i = 0; i < 8; ++i) {
329 TableNextColumn();
330 ImGui::BeginGroup();
331 if (gui::InputHexByte(kCustomMapSettingsColumnNames[i].data(),
332 overworld_.mutable_overworld_map(current_map_)
333 ->mutable_custom_tileset(i),
337 }
338 ImGui::EndGroup();
339 }
340
341 TableNextColumn();
342 ImGui::BeginGroup();
343 if (gui::InputHexByte("Palette",
344 overworld_.mutable_overworld_map(current_map_)
345 ->mutable_area_palette(),
350 }
351 ImGui::EndGroup();
352
353 TableNextColumn();
354 ImGui::BeginGroup();
355 gui::InputHexByte("Spr Gfx",
356 overworld_.mutable_overworld_map(current_map_)
357 ->mutable_sprite_graphics(game_state_),
359 ImGui::EndGroup();
360
361 TableNextColumn();
362 ImGui::BeginGroup();
363 gui::InputHexByte("Spr Palette",
364 overworld_.mutable_overworld_map(current_map_)
365 ->mutable_sprite_palette(game_state_),
367 ImGui::EndGroup();
368
369 TableNextColumn();
370 ImGui::BeginGroup();
372 "Msg Id",
373 overworld_.mutable_overworld_map(current_map_)->mutable_message_id(),
374 kInputFieldSize + 20);
375 ImGui::EndGroup();
376
377 TableNextColumn();
378 ImGui::SetNextItemWidth(100.f);
379 ImGui::Combo("##World", &game_state_, kGamePartComboString.data(), 3);
380
381 TableNextColumn();
382 ImGui::Checkbox(
383 "##mosaic",
384 overworld_.mutable_overworld_map(current_map_)->mutable_mosaic());
385 HOVER_HINT("Enable Mosaic effect for the current map");
386
387 ImGui::EndTable();
388 }
389}
390
392 int xx = 0;
393 int yy = 0;
394 for (int i = 0; i < 0x40; i++) {
395 int world_index = i + (current_world_ * 0x40);
396 int map_x = (xx * kOverworldMapSize * ow_map_canvas_.global_scale());
397 int map_y = (yy * kOverworldMapSize * ow_map_canvas_.global_scale());
398 ow_map_canvas_.DrawBitmap(maps_bmp_[world_index], map_x, map_y,
399 ow_map_canvas_.global_scale());
400 xx++;
401 if (xx >= 8) {
402 yy++;
403 xx = 0;
404 }
405 }
406}
407
409 // Determine which overworld map the user is currently editing.
410 auto mouse_position = ow_map_canvas_.drawn_tile_position();
411 int map_x = mouse_position.x / kOverworldMapSize;
412 int map_y = mouse_position.y / kOverworldMapSize;
413 current_map_ = map_x + map_y * 8;
414 if (current_world_ == 1) {
415 current_map_ += 0x40;
416 } else if (current_world_ == 2) {
417 current_map_ += 0x80;
418 }
419
420 // Render the updated map bitmap.
421 RenderUpdatedMapBitmap(mouse_position,
423
424 // Calculate the correct superX and superY values
425 int superY = current_map_ / 8;
426 int superX = current_map_ % 8;
427 int mouse_x = mouse_position.x;
428 int mouse_y = mouse_position.y;
429 // Calculate the correct tile16_x and tile16_y positions
430 int tile16_x = (mouse_x % kOverworldMapSize) / (kOverworldMapSize / 32);
431 int tile16_y = (mouse_y % kOverworldMapSize) / (kOverworldMapSize / 32);
432
433 // Update the overworld_.map_tiles() based on tile16 ID and current world
434 auto &selected_world =
435 (current_world_ == 0) ? overworld_.mutable_map_tiles()->light_world
436 : (current_world_ == 1) ? overworld_.mutable_map_tiles()->dark_world
437 : overworld_.mutable_map_tiles()->special_world;
438
439 int index_x = superX * 32 + tile16_x;
440 int index_y = superY * 32 + tile16_y;
441
442 selected_world[index_x][index_y] = current_tile16_;
443}
444
446 const ImVec2 &click_position, const std::vector<uint8_t> &tile_data) {
447 // Calculate the tile index for x and y based on the click_position
448 int tile_index_x =
449 (static_cast<int>(click_position.x) % kOverworldMapSize) / kTile16Size;
450 int tile_index_y =
451 (static_cast<int>(click_position.y) % kOverworldMapSize) / kTile16Size;
452
453 // Calculate the pixel start position based on tile index and tile size
454 ImVec2 start_position;
455 start_position.x = tile_index_x * kTile16Size;
456 start_position.y = tile_index_y * kTile16Size;
457
458 // Update the bitmap's pixel data based on the start_position and tile_data
459 gfx::Bitmap &current_bitmap = maps_bmp_[current_map_];
460 for (int y = 0; y < kTile16Size; ++y) {
461 for (int x = 0; x < kTile16Size; ++x) {
462 int pixel_index =
463 (start_position.y + y) * kOverworldMapSize + (start_position.x + x);
464 current_bitmap.WriteToPixel(pixel_index, tile_data[y * kTile16Size + x]);
465 }
466 }
467
468 current_bitmap.set_modified(true);
469}
470
473
474 // User has selected a tile they want to draw from the blockset.
475 if (!blockset_canvas_.points().empty() &&
476 !ow_map_canvas_.select_rect_active()) {
477 // Left click is pressed
479 kTile16Size)) {
481 }
482 }
483
484 if (ow_map_canvas_.select_rect_active()) {
485 if (ImGui::IsMouseClicked(ImGuiMouseButton_Left) ||
486 ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
487 auto &selected_world =
488 (current_world_ == 0) ? overworld_.mutable_map_tiles()->light_world
489 : (current_world_ == 1)
490 ? overworld_.mutable_map_tiles()->dark_world
491 : overworld_.mutable_map_tiles()->special_world;
492 // new_start_pos and new_end_pos
493 auto start = ow_map_canvas_.selected_points()[0];
494 auto end = ow_map_canvas_.selected_points()[1];
495
496 // Calculate the bounds of the rectangle in terms of 16x16 tile indices
497 int start_x = std::floor(start.x / kTile16Size) * kTile16Size;
498 int start_y = std::floor(start.y / kTile16Size) * kTile16Size;
499 int end_x = std::floor(end.x / kTile16Size) * kTile16Size;
500 int end_y = std::floor(end.y / kTile16Size) * kTile16Size;
501
502 if (start_x > end_x) std::swap(start_x, end_x);
503 if (start_y > end_y) std::swap(start_y, end_y);
504
505 constexpr int local_map_size = 512; // Size of each local map
506 // Number of tiles per local map (since each tile is 16x16)
507 constexpr int tiles_per_local_map = local_map_size / kTile16Size;
508
509 for (int y = start_y, i = 0; y <= end_y; y += kTile16Size) {
510 for (int x = start_x; x <= end_x; x += kTile16Size, ++i) {
511 // Determine which local map (512x512) the tile is in
512 int local_map_x = x / local_map_size;
513 int local_map_y = y / local_map_size;
514
515 // Calculate the tile's position within its local map
516 int tile16_x = (x % local_map_size) / kTile16Size;
517 int tile16_y = (y % local_map_size) / kTile16Size;
518
519 // Calculate the index within the overall map structure
520 int index_x = local_map_x * tiles_per_local_map + tile16_x;
521 int index_y = local_map_y * tiles_per_local_map + tile16_y;
522 int tile16_id = overworld_.GetTileFromPosition(
523 ow_map_canvas_.selected_tiles()[i]);
524 selected_world[index_x][index_y] = tile16_id;
525 }
526 }
527
529 }
530 }
531}
532
534 ow_map_canvas_.DrawSelectRect(current_map_);
535
536 // Single tile case
537 if (ow_map_canvas_.selected_tile_pos().x != -1) {
539 overworld_.GetTileFromPosition(ow_map_canvas_.selected_tile_pos());
540 ow_map_canvas_.set_selected_tile_pos(ImVec2(-1, -1));
541 }
542
543 static std::vector<int> tile16_ids;
544 if (ow_map_canvas_.select_rect_active()) {
545 // Get the tile16 IDs from the selected tile ID positions
546 if (tile16_ids.size() != 0) {
547 tile16_ids.clear();
548 }
549
550 if (ow_map_canvas_.selected_tiles().size() > 0) {
551 for (auto &each : ow_map_canvas_.selected_tiles()) {
552 tile16_ids.push_back(overworld_.GetTileFromPosition(each));
553 }
554 }
555 }
556 // Create a composite image of all the tile16s selected
557 ow_map_canvas_.DrawBitmapGroup(tile16_ids, tile16_individual_, 0x10);
558}
559
561 // 4096x4096, 512x512 maps and some are larges maps 1024x1024
562 const auto mouse_position = ImGui::GetIO().MousePos;
563 const int large_map_size = 1024;
564 const auto canvas_zero_point = ow_map_canvas_.zero_point();
565
566 // Calculate which small map the mouse is currently over
567 int map_x = (mouse_position.x - canvas_zero_point.x) / kOverworldMapSize;
568 int map_y = (mouse_position.y - canvas_zero_point.y) / kOverworldMapSize;
569
570 // Calculate the index of the map in the `maps_bmp_` vector
571 current_map_ = map_x + map_y * 8;
572 const int current_highlighted_map = current_map_;
573 if (current_world_ == 1) {
574 current_map_ += 0x40;
575 } else if (current_world_ == 2) {
576 current_map_ += 0x80;
577 }
578
579 current_parent_ = overworld_.overworld_map(current_map_)->parent();
580
581 if (overworld_.overworld_map(current_map_)->is_large_map() ||
582 overworld_.overworld_map(current_map_)->large_index() != 0) {
583 const int highlight_parent =
584 overworld_.overworld_map(current_highlighted_map)->parent();
585 const int parent_map_x = highlight_parent % 8;
586 const int parent_map_y = highlight_parent / 8;
587 ow_map_canvas_.DrawOutline(parent_map_x * kOverworldMapSize,
588 parent_map_y * kOverworldMapSize, large_map_size,
589 large_map_size);
590 } else {
591 const int current_map_x = current_highlighted_map % 8;
592 const int current_map_y = current_highlighted_map / 8;
593 ow_map_canvas_.DrawOutline(current_map_x * kOverworldMapSize,
594 current_map_y * kOverworldMapSize,
596 }
597
598 if (maps_bmp_[current_map_].modified() ||
599 ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
603 maps_bmp_[current_map_].set_modified(false);
604 }
605
606 return absl::OkStatus();
607}
608
610 if (ImGui::IsMouseDragging(ImGuiMouseButton_Middle)) {
613 ow_map_canvas_.set_draggable(true);
615 }
616 if (ImGui::IsMouseReleased(ImGuiMouseButton_Middle) &&
619 ow_map_canvas_.set_draggable(false);
621 }
622}
623
625 if (all_gfx_loaded_) {
626 if (core::FeatureFlags::get().overworld.kLoadCustomOverworld) {
628 } else {
630 }
631 Separator();
632 }
633
636 ow_map_canvas_.DrawBackground();
638
641 ow_map_canvas_.DrawContextMenu();
642 } else {
643 ow_map_canvas_.set_draggable(false);
644 }
645
646 if (overworld_.is_loaded()) {
648 DrawOverworldExits(ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling());
650 ow_map_canvas_.scrolling());
655 }
656 if (IsItemHovered()) status_ = CheckForCurrentMap();
657 }
658
659 ow_map_canvas_.DrawGrid();
660 ow_map_canvas_.DrawOverlay();
661 EndChild();
662
663 // Handle mouse wheel activity
664 if (ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows) &&
665 ImGui::IsMouseDragging(ImGuiMouseButton_Middle)) {
666 ImGui::SetScrollX(ImGui::GetScrollX() + ImGui::GetIO().MouseWheelH * 16.0f);
667 ImGui::SetScrollY(ImGui::GetScrollY() + ImGui::GetIO().MouseWheel * 16.0f);
668 }
669}
670
673 ImGui::BeginGroup();
674 gui::BeginChildWithScrollbar("##Tile16SelectorScrollRegion");
675 blockset_canvas_.DrawBackground();
677 {
678 blockset_canvas_.DrawContextMenu();
679 blockset_canvas_.DrawBitmap(tile16_blockset_bmp_, /*border_offset=*/2,
681
682 if (blockset_canvas_.DrawTileSelector(32.0f)) {
683 // Open the tile16 editor to the tile
684 auto tile_pos = blockset_canvas_.points().front();
685 int grid_x = static_cast<int>(tile_pos.x / 32);
686 int grid_y = static_cast<int>(tile_pos.y / 32);
687 int id = grid_x + grid_y * 8;
688 RETURN_IF_ERROR(tile16_editor_.SetCurrentTile(id));
689 show_tile16_editor_ = true;
690 }
691
692 if (ImGui::IsItemClicked() && !blockset_canvas_.points().empty()) {
693 int x = blockset_canvas_.points().front().x / 32;
694 int y = blockset_canvas_.points().front().y / 32;
695 current_tile16_ = x + (y * 8);
696 }
697
698 blockset_canvas_.DrawGrid();
699 blockset_canvas_.DrawOverlay();
700 }
701 EndChild();
702 ImGui::EndGroup();
703 return absl::OkStatus();
704}
705
707 graphics_bin_canvas_.DrawBackground();
708 graphics_bin_canvas_.DrawContextMenu();
709 if (all_gfx_loaded_) {
710 int key = 0;
711 for (auto &value : GraphicsSheetManager::GetInstance().gfx_sheets()) {
712 int offset = 0x40 * (key + 1);
713 int top_left_y = graphics_bin_canvas_.zero_point().y + 2;
714 if (key >= 1) {
715 top_left_y = graphics_bin_canvas_.zero_point().y + 0x40 * key;
716 }
717 auto texture = value.texture();
718 graphics_bin_canvas_.draw_list()->AddImage(
719 (ImTextureID)(intptr_t)texture,
720 ImVec2(graphics_bin_canvas_.zero_point().x + 2, top_left_y),
721 ImVec2(graphics_bin_canvas_.zero_point().x + 0x100,
722 graphics_bin_canvas_.zero_point().y + offset));
723 key++;
724 }
725 }
726 graphics_bin_canvas_.DrawGrid();
727 graphics_bin_canvas_.DrawOverlay();
728}
729
731 if (overworld_.is_loaded() &&
733 overworld_.set_current_map(current_map_);
734 palette_ = overworld_.current_area_palette();
735 gfx::Bitmap bmp;
736 RETURN_IF_ERROR(Renderer::GetInstance().CreateAndRenderBitmap(
737 0x80, kOverworldMapSize, 0x08, overworld_.current_graphics(), bmp,
738 palette_));
740 }
741
743 ImGui::BeginGroup();
744 gui::BeginChildWithScrollbar("##AreaGraphicsScrollRegion");
745 current_gfx_canvas_.DrawBackground();
747 {
748 current_gfx_canvas_.DrawContextMenu();
750 /*border_offset=*/2, overworld_.is_loaded());
751 current_gfx_canvas_.DrawTileSelector(32.0f);
752 current_gfx_canvas_.DrawGrid();
753 current_gfx_canvas_.DrawOverlay();
754 }
755 EndChild();
756 ImGui::EndGroup();
757 return absl::OkStatus();
758}
759
761 if (BeginTabBar(kTileSelectorTab.data(),
762 ImGuiTabBarFlags_FittingPolicyScroll)) {
763 if (BeginTabItem("Tile16")) {
765 EndTabItem();
766 }
767 if (BeginTabItem("Tile8")) {
769 gui::BeginChildWithScrollbar("##Tile8SelectorScrollRegion");
771 EndChild();
773 EndTabItem();
774 }
775 if (BeginTabItem("Area Graphics")) {
777 EndTabItem();
778 }
779 EndTabBar();
780 }
781 return absl::OkStatus();
782}
783
784void OverworldEditor::DrawOverworldEntrances(ImVec2 canvas_p0, ImVec2 scrolling,
785 bool holes) {
786 int i = 0;
787 for (auto &each : overworld_.entrances()) {
788 if (each.map_id_ < 0x40 + (current_world_ * 0x40) &&
789 each.map_id_ >= (current_world_ * 0x40) && !each.deleted) {
790 auto color = ImVec4(255, 255, 0, 100);
791 if (each.is_hole_) {
792 color = ImVec4(255, 255, 255, 200);
793 }
794 ow_map_canvas_.DrawRect(each.x_, each.y_, 16, 16, color);
795 std::string str = util::HexByte(each.entrance_id_);
796
798 HandleEntityDragging(&each, canvas_p0, scrolling, is_dragging_entity_,
800
801 if (IsMouseHoveringOverEntity(each, canvas_p0, scrolling) &&
802 ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
803 jump_to_tab_ = each.entrance_id_;
804 }
805
806 if (IsMouseHoveringOverEntity(each, canvas_p0, scrolling) &&
807 ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
809 current_entrance_ = each;
810 }
811 }
812
813 ow_map_canvas_.DrawText(str, each.x_, each.y_);
814 }
815 i++;
816 }
817
819 // Get the deleted entrance ID and insert it at the mouse position
820 auto deleted_entrance_id = overworld_.deleted_entrances().back();
821 overworld_.deleted_entrances().pop_back();
822 auto &entrance = overworld_.entrances()[deleted_entrance_id];
823 entrance.map_id_ = current_map_;
824 entrance.entrance_id_ = deleted_entrance_id;
825 entrance.x_ = ow_map_canvas_.hover_mouse_pos().x;
826 entrance.y_ = ow_map_canvas_.hover_mouse_pos().y;
827 entrance.deleted = false;
828 }
829
831 const auto is_hovering =
832 IsMouseHoveringOverEntity(current_entrance_, canvas_p0, scrolling);
833
834 if (!is_hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
835 ImGui::OpenPopup("Entrance Inserter");
836 } else {
838 overworld_.entrances()[current_entrance_id_])) {
840 }
841
842 if (overworld_.entrances()[current_entrance_id_].deleted) {
843 overworld_.mutable_deleted_entrances()->emplace_back(
845 }
846 }
847 }
848}
849
850void OverworldEditor::DrawOverworldExits(ImVec2 canvas_p0, ImVec2 scrolling) {
851 int i = 0;
852 for (auto &each : *overworld_.mutable_exits()) {
853 if (each.map_id_ < 0x40 + (current_world_ * 0x40) &&
854 each.map_id_ >= (current_world_ * 0x40) && !each.deleted_) {
855 ow_map_canvas_.DrawRect(each.x_, each.y_, 16, 16,
856 ImVec4(255, 255, 255, 150));
858 each.entity_id_ = i;
859 HandleEntityDragging(&each, ow_map_canvas_.zero_point(),
862
863 if (IsMouseHoveringOverEntity(each, canvas_p0, scrolling) &&
864 ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
865 jump_to_tab_ = each.room_id_;
866 }
867
868 if (IsMouseHoveringOverEntity(each, canvas_p0, scrolling) &&
869 ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
871 current_exit_ = each;
872 current_entity_ = &each;
873 current_entity_->entity_id_ = i;
874 ImGui::OpenPopup("Exit editor");
875 }
876 }
877
878 std::string str = util::HexByte(i);
879 ow_map_canvas_.DrawText(str, each.x_, each.y_);
880 }
881 i++;
882 }
883
886 const auto hovering = IsMouseHoveringOverEntity(
887 overworld_.mutable_exits()->at(current_exit_id_),
888 ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling());
889
890 if (!hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
891 ImGui::OpenPopup("Exit Inserter");
892 } else {
894 overworld_.mutable_exits()->at(current_exit_id_))) {
895 overworld_.mutable_exits()->at(current_exit_id_) = current_exit_;
896 }
897 }
898 }
899}
900
902 int i = 0;
903 for (auto &item : *overworld_.mutable_all_items()) {
904 // Get the item's bitmap and real X and Y positions
905 if (item.room_map_id_ < 0x40 + (current_world_ * 0x40) &&
906 item.room_map_id_ >= (current_world_ * 0x40) && !item.deleted) {
907 ow_map_canvas_.DrawRect(item.x_, item.y_, 16, 16, ImVec4(255, 0, 0, 150));
908
910 // Check if this item is being clicked and dragged
911 HandleEntityDragging(&item, ow_map_canvas_.zero_point(),
914
915 const auto hovering = IsMouseHoveringOverEntity(
916 item, ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling());
917 if (hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
919 current_item_ = item;
920 current_entity_ = &item;
921 }
922 }
923 std::string item_name = "";
924 if (item.id_ < zelda3::kSecretItemNames.size()) {
925 item_name = zelda3::kSecretItemNames[item.id_];
926 } else {
927 item_name = absl::StrFormat("0x%02X", item.id_);
928 }
929 ow_map_canvas_.DrawText(item_name, item.x_, item.y_);
930 }
931 i++;
932 }
933
936 const auto hovering = IsMouseHoveringOverEntity(
937 overworld_.mutable_all_items()->at(current_item_id_),
938 ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling());
939
940 if (!hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
941 ImGui::OpenPopup("Item Inserter");
942 } else {
944 overworld_.mutable_all_items()->at(current_item_id_))) {
945 overworld_.mutable_all_items()->at(current_item_id_) = current_item_;
946 }
947 }
948 }
949}
950
952 int i = 0;
953 for (auto &sprite : *overworld_.mutable_sprites(game_state_)) {
954 if (!sprite.deleted()) {
955 int map_x = sprite.map_x();
956 int map_y = sprite.map_y();
957 ow_map_canvas_.DrawRect(map_x, map_y, kTile16Size, kTile16Size,
958 /*magenta=*/ImVec4(255, 0, 255, 150));
960 HandleEntityDragging(&sprite, ow_map_canvas_.zero_point(),
963 if (IsMouseHoveringOverEntity(sprite, ow_map_canvas_.zero_point(),
964 ow_map_canvas_.scrolling()) &&
965 ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
967 current_sprite_ = sprite;
968 }
969 }
970 if (core::FeatureFlags::get().overworld.kDrawOverworldSprites) {
971 if (sprite_previews_[sprite.id()].is_active()) {
972 ow_map_canvas_.DrawBitmap(sprite_previews_[sprite.id()], map_x, map_y,
973 2.0f);
974 }
975 }
976
977 ow_map_canvas_.DrawText(absl::StrFormat("%s", sprite.name()), map_x,
978 map_y);
979 }
980 i++;
981 }
982
985 const auto hovering = IsMouseHoveringOverEntity(
986 overworld_.mutable_sprites(game_state_)->at(current_sprite_id_),
987 ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling());
988
989 if (!hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
990 ImGui::OpenPopup("Sprite Inserter");
991 } else {
992 if (DrawSpriteEditorPopup(overworld_.mutable_sprites(game_state_)
993 ->at(current_sprite_id_))) {
994 overworld_.mutable_sprites(game_state_)->at(current_sprite_id_) =
996 }
997 }
998 }
999}
1000
1002 if (core::FeatureFlags::get().overworld.kSaveOverworldMaps) {
1003 RETURN_IF_ERROR(overworld_.CreateTile32Tilemap());
1004 RETURN_IF_ERROR(overworld_.SaveMap32Tiles());
1005 RETURN_IF_ERROR(overworld_.SaveMap16Tiles());
1006 RETURN_IF_ERROR(overworld_.SaveOverworldMaps());
1007 }
1008 if (core::FeatureFlags::get().overworld.kSaveOverworldEntrances) {
1009 RETURN_IF_ERROR(overworld_.SaveEntrances());
1010 }
1011 if (core::FeatureFlags::get().overworld.kSaveOverworldExits) {
1012 RETURN_IF_ERROR(overworld_.SaveExits());
1013 }
1014 if (core::FeatureFlags::get().overworld.kSaveOverworldItems) {
1015 RETURN_IF_ERROR(overworld_.SaveItems());
1016 }
1017 if (core::FeatureFlags::get().overworld.kSaveOverworldProperties) {
1018 RETURN_IF_ERROR(overworld_.SaveMapProperties());
1019 }
1020 return absl::OkStatus();
1021}
1022
1024 util::logf("Loading overworld.");
1025 // Load the Link to the Past overworld.
1027 palette_ = overworld_.current_area_palette();
1028
1029 util::logf("Loading overworld graphics.");
1030 // Create the area graphics image
1031 RETURN_IF_ERROR(Renderer::GetInstance().CreateAndRenderBitmap(
1032 0x80, kOverworldMapSize, 0x40, overworld_.current_graphics(),
1034
1035 util::logf("Loading overworld tileset.");
1036 // Create the tile16 blockset image
1037 RETURN_IF_ERROR(Renderer::GetInstance().CreateAndRenderBitmap(
1038 0x80, 0x2000, 0x08, overworld_.tile16_blockset_data(),
1040 map_blockset_loaded_ = true;
1041
1042 // Copy the tile16 data into individual tiles.
1043 auto tile16_data = overworld_.tile16_blockset_data();
1044
1045 util::logf("Loading overworld tile16 graphics.");
1046 // Loop through the tiles and copy their pixel data into separate vectors
1047 for (unsigned int i = 0; i < zelda3::kNumTile16Individual; i++) {
1050
1051 // Copy the pixel data for the current tile into the vector
1052 for (int ty = 0; ty < kTile16Size; ty++) {
1053 for (int tx = 0; tx < kTile16Size; tx++) {
1054 int position = tx + (ty * kTile16Size);
1055 uint8_t value =
1056 tile16_data[(i % 8 * kTile16Size) + (i / 8 * kTile16Size * 0x80) +
1057 (ty * 0x80) + tx];
1058 tile16_individual_[i].mutable_data()[position] = value;
1059 }
1060 }
1061
1064 }
1065
1066 util::logf("Loading overworld maps.");
1067 // Render the overworld maps loaded from the ROM.
1068 for (int i = 0; i < zelda3::kNumOverworldMaps; ++i) {
1069 overworld_.set_current_map(i);
1070 auto palette = overworld_.current_area_palette();
1071 try {
1072 RETURN_IF_ERROR(Renderer::GetInstance().CreateAndRenderBitmap(
1074 overworld_.current_map_bitmap_data(), maps_bmp_[i], palette));
1075 } catch (const std::bad_alloc &e) {
1076 std::cout << "Error: " << e.what() << std::endl;
1077 continue;
1078 }
1079 }
1080
1081 if (core::FeatureFlags::get().overworld.kDrawOverworldSprites) {
1083 }
1084
1085 return absl::OkStatus();
1086}
1087
1089 // Render the sprites for each Overworld map
1090 const int depth = 0x10;
1091 for (int i = 0; i < 3; i++)
1092 for (auto const &sprite : *overworld_.mutable_sprites(i)) {
1093 int width = sprite.width();
1094 int height = sprite.height();
1095 if (width == 0 || height == 0) {
1096 continue;
1097 }
1098 if (sprite_previews_.size() < sprite.id()) {
1099 sprite_previews_.resize(sprite.id() + 1);
1100 }
1101 sprite_previews_[sprite.id()].Create(width, height, depth,
1102 *sprite.preview_graphics());
1103 RETURN_IF_ERROR(sprite_previews_[sprite.id()].ApplyPalette(palette_));
1105 }
1106 return absl::OkStatus();
1107}
1108
1110 overworld_.mutable_overworld_map(map_index)->LoadAreaGraphics();
1111 status_ = overworld_.mutable_overworld_map(map_index)->BuildTileset();
1113 status_ = overworld_.mutable_overworld_map(map_index)->BuildTiles16Gfx(
1114 *overworld_.mutable_tiles16(), overworld_.tiles16().size());
1116 status_ = overworld_.mutable_overworld_map(map_index)->BuildBitmap(
1117 overworld_.GetMapTiles(current_world_));
1118 maps_bmp_[map_index].set_data(
1119 overworld_.mutable_overworld_map(map_index)->bitmap_data());
1120 maps_bmp_[map_index].set_modified(true);
1122}
1123
1125 std::vector<std::future<void>> futures;
1126 int indices[4];
1127
1128 auto refresh_map_async = [this](int map_index) {
1129 RefreshChildMap(map_index);
1130 };
1131
1132 int source_map_id = current_map_;
1133 bool is_large = overworld_.overworld_map(current_map_)->is_large_map();
1134 if (is_large) {
1135 source_map_id = current_parent_;
1136 // We need to update the map and its siblings if it's a large map
1137 for (int i = 1; i < 4; i++) {
1138 int sibling_index = overworld_.overworld_map(source_map_id)->parent() + i;
1139 if (i >= 2) sibling_index += 6;
1140 futures.push_back(
1141 std::async(std::launch::async, refresh_map_async, sibling_index));
1142 indices[i] = sibling_index;
1143 }
1144 }
1145 indices[0] = source_map_id;
1146 futures.push_back(
1147 std::async(std::launch::async, refresh_map_async, source_map_id));
1148
1149 for (auto &each : futures) {
1150 each.wait();
1151 each.get();
1152 }
1153 int n = is_large ? 4 : 1;
1154 // We do texture updating on the main thread
1155 for (int i = 0; i < n; ++i) {
1157 }
1158}
1159
1162 overworld_.mutable_overworld_map(current_map_)->LoadPalette());
1163 const auto current_map_palette = overworld_.current_area_palette();
1164
1165 if (overworld_.overworld_map(current_map_)->is_large_map()) {
1166 // We need to update the map and its siblings if it's a large map
1167 for (int i = 1; i < 4; i++) {
1168 int sibling_index = overworld_.overworld_map(current_map_)->parent() + i;
1169 if (i >= 2) sibling_index += 6;
1171 overworld_.mutable_overworld_map(sibling_index)->LoadPalette());
1173 maps_bmp_[sibling_index].ApplyPalette(current_map_palette));
1174 }
1175 }
1176
1177 RETURN_IF_ERROR(maps_bmp_[current_map_].ApplyPalette(current_map_palette));
1178 return absl::OkStatus();
1179}
1180
1182 auto &current_ow_map = *overworld_.mutable_overworld_map(current_map_);
1183 if (current_ow_map.is_large_map()) {
1184 // We need to copy the properties from the parent map to the children
1185 for (int i = 1; i < 4; i++) {
1186 int sibling_index = current_ow_map.parent() + i;
1187 if (i >= 2) {
1188 sibling_index += 6;
1189 }
1190 auto &map = *overworld_.mutable_overworld_map(sibling_index);
1191 map.set_area_graphics(current_ow_map.area_graphics());
1192 map.set_area_palette(current_ow_map.area_palette());
1193 map.set_sprite_graphics(game_state_,
1194 current_ow_map.sprite_graphics(game_state_));
1195 map.set_sprite_palette(game_state_,
1196 current_ow_map.sprite_palette(game_state_));
1197 map.set_message_id(current_ow_map.message_id());
1198 }
1199 }
1200}
1201
1203 if (current_blockset_ ==
1204 overworld_.overworld_map(current_map_)->area_graphics()) {
1205 return absl::OkStatus();
1206 }
1207 current_blockset_ = overworld_.overworld_map(current_map_)->area_graphics();
1208
1209 overworld_.set_current_map(current_map_);
1210 palette_ = overworld_.current_area_palette();
1211 // Create the tile16 blockset image
1214
1215 // Copy the tile16 data into individual tiles.
1216 const auto tile16_data = overworld_.tile16_blockset_data();
1217
1218 // Loop through the tiles and copy their pixel data into separate vectors
1219 std::vector<std::future<absl::Status>> futures;
1220 for (unsigned int i = 0; i < zelda3::kNumTile16Individual; i++) {
1221 futures.push_back(std::async(
1222 std::launch::async,
1223 [&](int index) -> absl::Status {
1224 std::vector<uint8_t> tile_data(16 * 16, 0x00);
1225 for (int ty = 0; ty < 16; ty++) {
1226 for (int tx = 0; tx < 16; tx++) {
1227 int position = tx + (ty * 0x10);
1228 uint8_t value =
1229 tile16_data[(index % 8 * 16) + (index / 8 * 16 * 0x80) +
1230 (ty * 0x80) + tx];
1231 tile_data[position] = value;
1232 }
1233 }
1234 tile16_individual_[index].set_data(tile_data);
1235 RETURN_IF_ERROR(tile16_individual_[index].ApplyPalette(palette_));
1236 return absl::OkStatus();
1237 },
1238 i));
1239 }
1240
1241 for (auto &future : futures) {
1242 future.wait();
1243 RETURN_IF_ERROR(future.get());
1244 }
1245
1246 // Render the bitmaps of each tile.
1247 for (unsigned int id = 0; id < zelda3::kNumTile16Individual; id++) {
1249 }
1250
1251 return absl::OkStatus();
1252}
1253
1255 static bool init_properties = false;
1256
1257 if (!init_properties) {
1258 for (int i = 0; i < 0x40; i++) {
1259 std::string area_graphics_str = absl::StrFormat(
1260 "%02hX", overworld_.overworld_map(i)->area_graphics());
1262 ->push_back(area_graphics_str);
1263
1264 area_graphics_str = absl::StrFormat(
1265 "%02hX", overworld_.overworld_map(i + 0x40)->area_graphics());
1267 ->push_back(area_graphics_str);
1268
1269 std::string area_palette_str =
1270 absl::StrFormat("%02hX", overworld_.overworld_map(i)->area_palette());
1272 ->push_back(area_palette_str);
1273
1274 area_palette_str = absl::StrFormat(
1275 "%02hX", overworld_.overworld_map(i + 0x40)->area_palette());
1277 ->push_back(area_palette_str);
1278 std::string sprite_gfx_str = absl::StrFormat(
1279 "%02hX", overworld_.overworld_map(i)->sprite_graphics(1));
1281 ->push_back(sprite_gfx_str);
1282
1283 sprite_gfx_str = absl::StrFormat(
1284 "%02hX", overworld_.overworld_map(i)->sprite_graphics(2));
1286 ->push_back(sprite_gfx_str);
1287
1288 sprite_gfx_str = absl::StrFormat(
1289 "%02hX", overworld_.overworld_map(i + 0x40)->sprite_graphics(1));
1291 ->push_back(sprite_gfx_str);
1292
1293 sprite_gfx_str = absl::StrFormat(
1294 "%02hX", overworld_.overworld_map(i + 0x40)->sprite_graphics(2));
1296 ->push_back(sprite_gfx_str);
1297
1298 std::string sprite_palette_str = absl::StrFormat(
1299 "%02hX", overworld_.overworld_map(i)->sprite_palette(1));
1301 ->push_back(sprite_palette_str);
1302
1303 sprite_palette_str = absl::StrFormat(
1304 "%02hX", overworld_.overworld_map(i)->sprite_palette(2));
1306 ->push_back(sprite_palette_str);
1307
1308 sprite_palette_str = absl::StrFormat(
1309 "%02hX", overworld_.overworld_map(i + 0x40)->sprite_palette(1));
1311 ->push_back(sprite_palette_str);
1312
1313 sprite_palette_str = absl::StrFormat(
1314 "%02hX", overworld_.overworld_map(i + 0x40)->sprite_palette(2));
1316 ->push_back(sprite_palette_str);
1317 }
1318 init_properties = true;
1319 }
1320
1321 Text("Area Gfx LW/DW");
1322 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 8, 2.0f, 32,
1324 SameLine();
1325 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 8, 2.0f, 32,
1327 ImGui::Separator();
1328
1329 Text("Sprite Gfx LW/DW");
1330 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 8, 2.0f, 32,
1332 SameLine();
1333 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 8, 2.0f, 32,
1335 SameLine();
1336 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 8, 2.0f, 32,
1338 SameLine();
1339 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 8, 2.0f, 32,
1341 ImGui::Separator();
1342
1343 Text("Area Pal LW/DW");
1344 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 8, 2.0f, 32,
1346 SameLine();
1347 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 8, 2.0f, 32,
1349
1350 static bool show_gfx_group = false;
1351 Checkbox("Show Gfx Group Editor", &show_gfx_group);
1352 if (show_gfx_group) {
1353 gui::BeginWindowWithDisplaySettings("Gfx Group Editor", &show_gfx_group);
1354 status_ = gfx_group_editor_.Update();
1356 }
1357}
1358
1360 if (BeginTable("UsageStatsTable", 3, kOWEditFlags, ImVec2(0, 0))) {
1361 TableSetupColumn("Entrances");
1362 TableSetupColumn("Grid", ImGuiTableColumnFlags_WidthStretch,
1363 ImGui::GetContentRegionAvail().x);
1364 TableSetupColumn("Usage", ImGuiTableColumnFlags_WidthFixed, 256);
1365 TableHeadersRow();
1366 TableNextRow();
1367
1368 TableNextColumn();
1369 if (BeginChild("UnusedSpritesetScroll", ImVec2(0, 0), true,
1370 ImGuiWindowFlags_HorizontalScrollbar)) {
1371 for (int i = 0; i < 0x81; i++) {
1372 auto entrance_name = rom_.resource_label()->CreateOrGetLabel(
1373 "Dungeon Entrance Names", util::HexByte(i),
1374 zelda3::kEntranceNames[i].data());
1375 std::string str = absl::StrFormat("%#x - %s", i, entrance_name);
1376 if (Selectable(str.c_str(), selected_entrance_ == i,
1377 overworld_.entrances().at(i).deleted
1378 ? ImGuiSelectableFlags_Disabled
1379 : 0)) {
1381 selected_usage_map_ = overworld_.entrances().at(i).map_id_;
1382 properties_canvas_.set_highlight_tile_id(selected_usage_map_);
1383 }
1384 if (IsItemHovered()) {
1385 BeginTooltip();
1386 Text("Entrance ID: %d", i);
1387 Text("Map ID: %d", overworld_.entrances().at(i).map_id_);
1388 Text("Entrance ID: %d", overworld_.entrances().at(i).entrance_id_);
1389 Text("X: %d", overworld_.entrances().at(i).x_);
1390 Text("Y: %d", overworld_.entrances().at(i).y_);
1391 Text("Deleted? %s",
1392 overworld_.entrances().at(i).deleted ? "Yes" : "No");
1393 EndTooltip();
1394 }
1395 }
1396 EndChild();
1397 }
1398
1399 TableNextColumn();
1400 DrawUsageGrid();
1401
1402 TableNextColumn();
1404
1405 EndTable();
1406 }
1407 return absl::OkStatus();
1408}
1409
1411 // Create a grid of 8x8 squares
1412 int total_squares = 128;
1413 int squares_wide = 8;
1414 int squares_tall = (total_squares + squares_wide - 1) /
1415 squares_wide; // Ceiling of total_squares/squares_wide
1416
1417 // Loop through each row
1418 for (int row = 0; row < squares_tall; ++row) {
1419 NewLine();
1420
1421 for (int col = 0; col < squares_wide; ++col) {
1422 if (row * squares_wide + col >= total_squares) {
1423 break;
1424 }
1425 // Determine if this square should be highlighted
1426 bool highlight = selected_usage_map_ == (row * squares_wide + col);
1427
1428 // Set highlight color if needed
1429 if (highlight) {
1430 PushStyleColor(ImGuiCol_Button,
1431 ImVec4(1.0f, 0.5f, 0.0f,
1432 1.0f)); // Or any highlight color
1433 }
1434
1435 // Create a button or selectable for each square
1436 if (Button("##square", ImVec2(20, 20))) {
1437 // Switch over to the room editor tab
1438 // and add a room tab by the ID of the square
1439 // that was clicked
1440 }
1441
1442 // Reset style if it was highlighted
1443 if (highlight) {
1444 PopStyleColor();
1445 }
1446
1447 // Check if the square is hovered
1448 if (IsItemHovered()) {
1449 // Display a tooltip with all the room properties
1450 }
1451
1452 // Keep squares in the same line
1453 SameLine();
1454 }
1455 }
1456}
1457
1459 Text("Current Map: %d", current_map_);
1460 Text("Current Tile16: %d", current_tile16_);
1461 int relative_x = (int)ow_map_canvas_.drawn_tile_position().x % 512;
1462 int relative_y = (int)ow_map_canvas_.drawn_tile_position().y % 512;
1463 Text("Current Tile16 Drawn Position (Relative): %d, %d", relative_x,
1464 relative_y);
1465
1466 // Print the size of the overworld map_tiles per world
1467 Text("Light World Map Tiles: %d",
1468 (int)overworld_.mutable_map_tiles()->light_world.size());
1469 Text("Dark World Map Tiles: %d",
1470 (int)overworld_.mutable_map_tiles()->dark_world.size());
1471 Text("Special World Map Tiles: %d",
1472 (int)overworld_.mutable_map_tiles()->special_world.size());
1473
1474 static bool view_lw_map_tiles = false;
1475 static MemoryEditor mem_edit;
1476 // Let's create buttons which let me view containers in the memory editor
1477 if (Button("View Light World Map Tiles")) {
1478 view_lw_map_tiles = !view_lw_map_tiles;
1479 }
1480
1481 if (view_lw_map_tiles) {
1482 mem_edit.DrawContents(
1483 overworld_.mutable_map_tiles()->light_world[current_map_].data(),
1484 overworld_.mutable_map_tiles()->light_world[current_map_].size());
1485 }
1486}
1487
1489 // Load zeml string from layouts/overworld.zeml
1490 std::string layout = gui::zeml::LoadFile("overworld.zeml");
1491 // Parse the zeml string into a Node object
1493
1494 gui::zeml::Bind(&*layout_node_.GetNode("OverworldCanvas"),
1495 [this]() { DrawOverworldCanvas(); });
1496 gui::zeml::Bind(&*layout_node_.GetNode("OverworldTileSelector"),
1497 [this]() { status_ = DrawTileSelector(); });
1498 gui::zeml::Bind(&*layout_node_.GetNode("OwUsageStats"), [this]() {
1499 if (rom_.is_loaded()) {
1500 status_ = UpdateUsageStats();
1501 }
1502 });
1503 gui::zeml::Bind(&*layout_node_.GetNode("owToolset"),
1504 [this]() { DrawToolset(); });
1505 gui::zeml::Bind(&*layout_node_.GetNode("OwTile16Editor"), [this]() {
1506 if (rom_.is_loaded()) {
1507 status_ = tile16_editor_.Update();
1508 }
1509 });
1510 gui::zeml::Bind(&*layout_node_.GetNode("OwGfxGroupEditor"), [this]() {
1511 if (rom_.is_loaded()) {
1512 status_ = gfx_group_editor_.Update();
1513 }
1514 });
1515}
1516
1517} // namespace editor
1518} // namespace yaze
static GraphicsSheetManager & GetInstance()
Definition rom.h:271
static Flags & get()
Definition features.h:69
void UpdateBitmap(gfx::Bitmap *bitmap)
Used to update a bitmap on the screen.
Definition renderer.h:55
void RenderBitmap(gfx::Bitmap *bitmap)
Used to render a bitmap to the screen.
Definition renderer.h:48
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.
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:66
void WriteToPixel(int position, uint8_t value)
Definition bitmap.h:136
void set_modified(bool modified)
Definition bitmap.h:173
#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:36
#define RETURN_IF_ERROR(expression)
Definition macro.h:62
#define ASSIGN_OR_RETURN(type_variable_name, expression)
Definition macro.h:70
#define HOVER_HINT(string)
Definition macro.h:33
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:358
void BeginChildBothScrollbars(int id)
Definition style.cc:375
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:258
void EndNoPadding()
Definition style.cc:368
void DrawTable(Table &params)
Definition input.cc:263
void EndPadding()
Definition style.cc:362
void BeginNoPadding()
Definition style.cc:364
void EndWindowWithDisplaySettings()
Definition style.cc:353
void BeginWindowWithDisplaySettings(const char *id, bool *active, const ImVec2 &size, ImGuiWindowFlags flags)
Definition style.cc:333
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:370
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