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"
12#include "app/core/window.h"
15#include "app/gfx/arena.h"
16#include "app/gfx/bitmap.h"
18#include "app/gfx/tilemap.h"
19#include "app/gui/canvas.h"
20#include "app/gui/icons.h"
21#include "app/gui/input.h"
22#include "app/gui/style.h"
23#include "app/gui/zeml.h"
24#include "app/rom.h"
25#include "app/zelda3/common.h"
27#include "imgui/imgui.h"
28#include "imgui_memory_editor.h"
29#include "util/hex.h"
30#include "util/log.h"
31#include "util/macro.h"
32
33namespace yaze::editor {
34
35using core::Renderer;
36using namespace ImGui;
37
38constexpr int kTile16Size = 0x10;
39constexpr float kInputFieldSize = 30.f;
40
43
44 gui::zeml::Bind(std::to_address(layout_node_.GetNode("OverworldCanvas")),
45 [this]() { DrawOverworldCanvas(); });
47 std::to_address(layout_node_.GetNode("OverworldTileSelector")),
48 [this]() { status_ = DrawTileSelector(); });
49 gui::zeml::Bind(std::to_address(layout_node_.GetNode("OwUsageStats")),
50 [this]() {
51 if (rom_->is_loaded()) {
52 status_ = UpdateUsageStats();
53 }
54 });
55 gui::zeml::Bind(std::to_address(layout_node_.GetNode("owToolset")),
56 [this]() { DrawToolset(); });
57 gui::zeml::Bind(std::to_address(layout_node_.GetNode("OwTile16Editor")),
58 [this]() {
59 if (rom_->is_loaded()) {
60 status_ = tile16_editor_.Update();
61 }
62 });
63 gui::zeml::Bind(std::to_address(layout_node_.GetNode("OwGfxGroupEditor")),
64 [this]() {
65 if (rom_->is_loaded()) {
66 status_ = gfx_group_editor_.Update();
67 }
68 });
69
70 gui::AddTableColumn(toolset_table_, "##Undo", [&]() {
71 if (Button(ICON_MD_UNDO)) status_ = Undo();
72 });
73 gui::AddTableColumn(toolset_table_, "##Redo", [&]() {
74 if (Button(ICON_MD_REDO)) status_ = Redo();
75 });
76 gui::AddTableColumn(toolset_table_, "##Sep1", ICON_MD_MORE_VERT);
77 gui::AddTableColumn(toolset_table_, "##ZoomOut", [&]() {
78 if (Button(ICON_MD_ZOOM_OUT)) ow_map_canvas_.ZoomOut();
79 });
80 gui::AddTableColumn(toolset_table_, "##ZoomIn", [&]() {
81 if (Button(ICON_MD_ZOOM_IN)) ow_map_canvas_.ZoomIn();
82 });
83 gui::AddTableColumn(toolset_table_, "##Fullscreen", [&]() {
85 overworld_canvas_fullscreen_ = !overworld_canvas_fullscreen_;
86 HOVER_HINT("Fullscreen Canvas");
87 });
88 gui::AddTableColumn(toolset_table_, "##Sep2", ICON_MD_MORE_VERT);
89 gui::AddTableColumn(toolset_table_, "##Pan", [&]() {
90 if (Selectable(ICON_MD_PAN_TOOL_ALT, current_mode == EditingMode::PAN)) {
91 current_mode = EditingMode::PAN;
92 ow_map_canvas_.set_draggable(true);
93 }
94 HOVER_HINT("Pan (Right click and drag)");
95 });
96 gui::AddTableColumn(toolset_table_, "##DrawTile", [&]() {
97 if (Selectable(ICON_MD_DRAW, current_mode == EditingMode::DRAW_TILE)) {
98 current_mode = EditingMode::DRAW_TILE;
99 }
100 HOVER_HINT("Draw Tile");
101 });
102 gui::AddTableColumn(toolset_table_, "##Entrances", [&]() {
103 if (Selectable(ICON_MD_DOOR_FRONT, current_mode == EditingMode::ENTRANCES))
104 current_mode = EditingMode::ENTRANCES;
105 HOVER_HINT("Entrances");
106 });
107 gui::AddTableColumn(toolset_table_, "##Exits", [&]() {
108 if (Selectable(ICON_MD_DOOR_BACK, current_mode == EditingMode::EXITS))
109 current_mode = EditingMode::EXITS;
110 HOVER_HINT("Exits");
111 });
112 gui::AddTableColumn(toolset_table_, "##Items", [&]() {
113 if (Selectable(ICON_MD_GRASS, current_mode == EditingMode::ITEMS))
114 current_mode = EditingMode::ITEMS;
115 HOVER_HINT("Items");
116 });
117 gui::AddTableColumn(toolset_table_, "##Sprites", [&]() {
119 current_mode == EditingMode::SPRITES))
120 current_mode = EditingMode::SPRITES;
121 HOVER_HINT("Sprites");
122 });
123 gui::AddTableColumn(toolset_table_, "##Transports", [&]() {
125 current_mode == EditingMode::TRANSPORTS))
126 current_mode = EditingMode::TRANSPORTS;
127 HOVER_HINT("Transports");
128 });
129 gui::AddTableColumn(toolset_table_, "##Music", [&]() {
130 if (Selectable(ICON_MD_MUSIC_NOTE, current_mode == EditingMode::MUSIC))
131 current_mode = EditingMode::MUSIC;
132 HOVER_HINT("Music");
133 });
134 gui::AddTableColumn(toolset_table_, "##Tile16Editor", [&]() {
135 if (Button(ICON_MD_GRID_VIEW)) show_tile16_editor_ = !show_tile16_editor_;
136 HOVER_HINT("Tile16 Editor");
137 });
138 gui::AddTableColumn(toolset_table_, "##GfxGroupEditor", [&]() {
140 show_gfx_group_editor_ = !show_gfx_group_editor_;
141 HOVER_HINT("Gfx Group Editor");
142 });
143 gui::AddTableColumn(toolset_table_, "##sep3", ICON_MD_MORE_VERT);
144 gui::AddTableColumn(toolset_table_, "##Properties", [&]() {
146 std::vector<uint8_t> png_data;
147 png_data = maps_bmp_[current_map_].GetPngData();
148 if (png_data.size() > 0) {
150 } else {
151 status_ = absl::InternalError(
152 "Failed to convert overworld map surface to PNG");
153 }
154 }
155 HOVER_HINT("Copy Map to Clipboard");
156 });
157 gui::AddTableColumn(toolset_table_, "##Palette", [&]() {
158 status_ = DisplayPalette(palette_, overworld_.is_loaded());
159 });
160 gui::AddTableColumn(toolset_table_, "##Sep4", ICON_MD_MORE_VERT);
161 gui::AddTableColumn(toolset_table_, "##Properties", [&]() {
162 Checkbox("Properties", &show_properties_editor_);
163 });
164}
165
166absl::Status OverworldEditor::Load() {
170 *overworld_.mutable_all_tiles_types()));
172 all_gfx_loaded_ = true;
173 return absl::OkStatus();
174}
175
177 status_ = absl::OkStatus();
180 return status_;
181}
182
184 static bool use_work_area = true;
185 static ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration |
186 ImGuiWindowFlags_NoMove |
187 ImGuiWindowFlags_NoSavedSettings;
188 const ImGuiViewport *viewport = ImGui::GetMainViewport();
189 ImGui::SetNextWindowPos(use_work_area ? viewport->WorkPos : viewport->Pos);
190 ImGui::SetNextWindowSize(use_work_area ? viewport->WorkSize : viewport->Size);
191 if (ImGui::Begin("Fullscreen Overworld Editor", &overworld_canvas_fullscreen_,
192 flags)) {
193 // Draws the toolset for editing the Overworld.
194 DrawToolset();
196 }
197 ImGui::End();
198}
199
202
204 ImGui::Begin("Tile16 Editor", &show_tile16_editor_,
205 ImGuiWindowFlags_MenuBar);
206 status_ = tile16_editor_.Update();
207 ImGui::End();
208 }
209
211 gui::BeginWindowWithDisplaySettings("Gfx Group Editor",
213 status_ = gfx_group_editor_.Update();
215 }
216
218 ImGui::Begin("Properties", &show_properties_editor_);
220 ImGui::End();
221 }
222
223 // TODO: Customizable shortcuts for the Overworld Editor
224 if (!ImGui::IsAnyItemActive()) {
225 using enum EditingMode;
226 if (ImGui::IsKeyDown(ImGuiKey_1)) {
228 } else if (ImGui::IsKeyDown(ImGuiKey_2)) {
230 } else if (ImGui::IsKeyDown(ImGuiKey_3)) {
232 } else if (ImGui::IsKeyDown(ImGuiKey_4)) {
234 } else if (ImGui::IsKeyDown(ImGuiKey_5)) {
236 } else if (ImGui::IsKeyDown(ImGuiKey_6)) {
238 } else if (ImGui::IsKeyDown(ImGuiKey_7)) {
240 } else if (ImGui::IsKeyDown(ImGuiKey_8)) {
242 }
243 }
244}
245
246constexpr std::array<const char *, 8> kMapSettingsColumnNames = {
247 "##WorldId", "##GfxId", "##PalId", "##SprGfxId",
248 "##5thCol", "##6thCol", "##7thCol", "##8thCol"};
249
251 if (BeginTable(kOWMapTable.data(), 8, kOWMapFlags, ImVec2(0, 0), -1)) {
252 for (const auto &name : kMapSettingsColumnNames)
253 ImGui::TableSetupColumn(name);
254
255 TableNextColumn();
256 ImGui::SetNextItemWidth(120.f);
257 ImGui::Combo("##world", &current_world_, kWorldList.data(), 3);
258
259 TableNextColumn();
260 ImGui::BeginGroup();
261 if (gui::InputHexByte("Gfx",
262 overworld_.mutable_overworld_map(current_map_)
263 ->mutable_area_graphics(),
267 }
268 ImGui::EndGroup();
269
270 TableNextColumn();
271 ImGui::BeginGroup();
272 if (gui::InputHexByte("Palette",
273 overworld_.mutable_overworld_map(current_map_)
274 ->mutable_area_palette(),
279 }
280 ImGui::EndGroup();
281
282 TableNextColumn();
283 ImGui::BeginGroup();
284 gui::InputHexByte("Spr Gfx",
285 overworld_.mutable_overworld_map(current_map_)
286 ->mutable_sprite_graphics(game_state_),
288 ImGui::EndGroup();
289
290 TableNextColumn();
291 ImGui::BeginGroup();
292 gui::InputHexByte("Spr Palette",
293 overworld_.mutable_overworld_map(current_map_)
294 ->mutable_sprite_palette(game_state_),
296 ImGui::EndGroup();
297
298 TableNextColumn();
299 ImGui::BeginGroup();
301 "Msg Id",
302 overworld_.mutable_overworld_map(current_map_)->mutable_message_id(),
303 kInputFieldSize + 20);
304 ImGui::EndGroup();
305
306 TableNextColumn();
307 ImGui::SetNextItemWidth(100.f);
308 ImGui::Combo("##World", &game_state_, kGamePartComboString.data(), 3);
309
310 TableNextColumn();
311 ImGui::Checkbox(
312 "##mosaic",
313 overworld_.mutable_overworld_map(current_map_)->mutable_mosaic());
314 HOVER_HINT("Enable Mosaic effect for the current map");
315
316 ImGui::EndTable();
317 }
318}
319
321 if (BeginTable(kOWMapTable.data(), 8, kOWMapFlags, ImVec2(0, 0), -1)) {
322 for (const auto &name : kMapSettingsColumnNames)
323 ImGui::TableSetupColumn(name);
324
325 TableNextColumn();
326 ImGui::SetNextItemWidth(120.f);
327 ImGui::Combo("##world", &current_world_, kWorldList.data(), 3);
328
329 TableNextColumn();
330
331 if (ImGui::Button("Tile Graphics", ImVec2(120, 0))) {
332 ImGui::OpenPopup("TileGraphicsPopup");
333 }
334 if (ImGui::BeginPopup("TileGraphicsPopup")) {
335 static const std::array<std::string, 8> kCustomMapSettingsColumnNames = {
336 "TileGfx0", "TileGfx1", "TileGfx2", "TileGfx3",
337 "TileGfx4", "TileGfx5", "TileGfx6", "TileGfx7"};
338 for (int i = 0; i < 8; ++i) {
339 ImGui::BeginGroup();
340 if (gui::InputHexByte(kCustomMapSettingsColumnNames[i].data(),
341 overworld_.mutable_overworld_map(current_map_)
342 ->mutable_custom_tileset(i),
346 }
347 ImGui::EndGroup();
348 }
349 ImGui::EndPopup();
350 }
351
352 TableNextColumn();
353 ImGui::BeginGroup();
354 if (gui::InputHexByte("Palette",
355 overworld_.mutable_overworld_map(current_map_)
356 ->mutable_area_palette(),
361 }
362 ImGui::EndGroup();
363
364 TableNextColumn();
365 ImGui::BeginGroup();
366 gui::InputHexByte("Spr Gfx",
367 overworld_.mutable_overworld_map(current_map_)
368 ->mutable_sprite_graphics(game_state_),
370 ImGui::EndGroup();
371
372 TableNextColumn();
373 ImGui::BeginGroup();
374 gui::InputHexByte("Spr Palette",
375 overworld_.mutable_overworld_map(current_map_)
376 ->mutable_sprite_palette(game_state_),
378 ImGui::EndGroup();
379
380 TableNextColumn();
381 ImGui::BeginGroup();
383 "Msg Id",
384 overworld_.mutable_overworld_map(current_map_)->mutable_message_id(),
385 kInputFieldSize + 20);
386 ImGui::EndGroup();
387
388 TableNextColumn();
389 ImGui::SetNextItemWidth(100.f);
390 ImGui::Combo("##World", &game_state_, kGamePartComboString.data(), 3);
391
392 TableNextColumn();
393 ImGui::Checkbox(
394 "##mosaic",
395 overworld_.mutable_overworld_map(current_map_)->mutable_mosaic());
396 HOVER_HINT("Enable Mosaic effect for the current map");
397
398 ImGui::EndTable();
399 }
400}
401
403 int xx = 0;
404 int yy = 0;
405 for (int i = 0; i < 0x40; i++) {
406 int world_index = i + (current_world_ * 0x40);
407 int scale = static_cast<int>(ow_map_canvas_.global_scale());
408 int map_x = (xx * kOverworldMapSize * scale);
409 int map_y = (yy * kOverworldMapSize * scale);
410 ow_map_canvas_.DrawBitmap(maps_bmp_[world_index], map_x, map_y,
411 ow_map_canvas_.global_scale());
412 xx++;
413 if (xx >= 8) {
414 yy++;
415 xx = 0;
416 }
417 }
418}
419
421 // Determine which overworld map the user is currently editing.
422 auto mouse_position = ow_map_canvas_.drawn_tile_position();
423 int map_x = mouse_position.x / kOverworldMapSize;
424 int map_y = mouse_position.y / kOverworldMapSize;
425 current_map_ = map_x + map_y * 8;
426 if (current_world_ == 1) {
427 current_map_ += 0x40;
428 } else if (current_world_ == 2) {
429 current_map_ += 0x80;
430 }
431
432 // Render the updated map bitmap.
435
436 // Calculate the correct superX and superY values
437 int superY = current_map_ / 8;
438 int superX = current_map_ % 8;
439 int mouse_x = mouse_position.x;
440 int mouse_y = mouse_position.y;
441 // Calculate the correct tile16_x and tile16_y positions
442 int tile16_x = (mouse_x % kOverworldMapSize) / (kOverworldMapSize / 32);
443 int tile16_y = (mouse_y % kOverworldMapSize) / (kOverworldMapSize / 32);
444
445 // Update the overworld_.map_tiles() based on tile16 ID and current world
446 auto &selected_world =
447 (current_world_ == 0) ? overworld_.mutable_map_tiles()->light_world
448 : (current_world_ == 1) ? overworld_.mutable_map_tiles()->dark_world
449 : overworld_.mutable_map_tiles()->special_world;
450
451 int index_x = superX * 32 + tile16_x;
452 int index_y = superY * 32 + tile16_y;
453
454 selected_world[index_x][index_y] = current_tile16_;
455}
456
458 const ImVec2 &click_position, const std::vector<uint8_t> &tile_data) {
459 // Calculate the tile index for x and y based on the click_position
460 int tile_index_x =
461 (static_cast<int>(click_position.x) % kOverworldMapSize) / kTile16Size;
462 int tile_index_y =
463 (static_cast<int>(click_position.y) % kOverworldMapSize) / kTile16Size;
464
465 // Calculate the pixel start position based on tile index and tile size
466 ImVec2 start_position;
467 start_position.x = static_cast<float>(tile_index_x * kTile16Size);
468 start_position.y = static_cast<float>(tile_index_y * kTile16Size);
469
470 // Update the bitmap's pixel data based on the start_position and tile_data
471 gfx::Bitmap &current_bitmap = maps_bmp_[current_map_];
472 for (int y = 0; y < kTile16Size; ++y) {
473 for (int x = 0; x < kTile16Size; ++x) {
474 int pixel_index =
475 (start_position.y + y) * kOverworldMapSize + (start_position.x + x);
476 current_bitmap.WriteToPixel(pixel_index, tile_data[y * kTile16Size + x]);
477 }
478 }
479
480 current_bitmap.set_modified(true);
481}
482
485
486 // User has selected a tile they want to draw from the blockset
487 // and clicked on the canvas.
488 if (!blockset_canvas_.points().empty() &&
489 !ow_map_canvas_.select_rect_active() &&
490 ow_map_canvas_.DrawTilemapPainter(tile16_blockset_, current_tile16_)) {
492 }
493
494 if (ow_map_canvas_.select_rect_active()) {
495 if (ImGui::IsMouseClicked(ImGuiMouseButton_Left) ||
496 ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
497 auto &selected_world =
498 (current_world_ == 0) ? overworld_.mutable_map_tiles()->light_world
499 : (current_world_ == 1)
500 ? overworld_.mutable_map_tiles()->dark_world
501 : overworld_.mutable_map_tiles()->special_world;
502 // new_start_pos and new_end_pos
503 auto start = ow_map_canvas_.selected_points()[0];
504 auto end = ow_map_canvas_.selected_points()[1];
505
506 // Calculate the bounds of the rectangle in terms of 16x16 tile indices
507 int start_x = std::floor(start.x / kTile16Size) * kTile16Size;
508 int start_y = std::floor(start.y / kTile16Size) * kTile16Size;
509 int end_x = std::floor(end.x / kTile16Size) * kTile16Size;
510 int end_y = std::floor(end.y / kTile16Size) * kTile16Size;
511
512 if (start_x > end_x) std::swap(start_x, end_x);
513 if (start_y > end_y) std::swap(start_y, end_y);
514
515 constexpr int local_map_size = 512; // Size of each local map
516 // Number of tiles per local map (since each tile is 16x16)
517 constexpr int tiles_per_local_map = local_map_size / kTile16Size;
518
519 for (int y = start_y, i = 0; y <= end_y; y += kTile16Size) {
520 for (int x = start_x; x <= end_x; x += kTile16Size, ++i) {
521 // Determine which local map (512x512) the tile is in
522 int local_map_x = x / local_map_size;
523 int local_map_y = y / local_map_size;
524
525 // Calculate the tile's position within its local map
526 int tile16_x = (x % local_map_size) / kTile16Size;
527 int tile16_y = (y % local_map_size) / kTile16Size;
528
529 // Calculate the index within the overall map structure
530 int index_x = local_map_x * tiles_per_local_map + tile16_x;
531 int index_y = local_map_y * tiles_per_local_map + tile16_y;
532 int tile16_id = overworld_.GetTileFromPosition(
533 ow_map_canvas_.selected_tiles()[i]);
534 selected_world[index_x][index_y] = tile16_id;
535 }
536 }
537
539 }
540 }
541}
542
544 ow_map_canvas_.DrawSelectRect(current_map_);
545
546 // Single tile case
547 if (ow_map_canvas_.selected_tile_pos().x != -1) {
549 overworld_.GetTileFromPosition(ow_map_canvas_.selected_tile_pos());
550 ow_map_canvas_.set_selected_tile_pos(ImVec2(-1, -1));
551 }
552
553 static std::vector<int> tile16_ids;
554 if (ow_map_canvas_.select_rect_active()) {
555 // Get the tile16 IDs from the selected tile ID positions
556 if (tile16_ids.size() != 0) {
557 tile16_ids.clear();
558 }
559
560 if (ow_map_canvas_.selected_tiles().size() > 0) {
561 for (auto &each : ow_map_canvas_.selected_tiles()) {
562 tile16_ids.push_back(overworld_.GetTileFromPosition(each));
563 }
564 }
565 }
566 // Create a composite image of all the tile16s selected
567 ow_map_canvas_.DrawBitmapGroup(tile16_ids, tile16_blockset_, 0x10);
568}
569
571 // 4096x4096, 512x512 maps and some are larges maps 1024x1024
572 const auto mouse_position = ImGui::GetIO().MousePos;
573 const int large_map_size = 1024;
574 const auto canvas_zero_point = ow_map_canvas_.zero_point();
575
576 // Calculate which small map the mouse is currently over
577 int map_x = (mouse_position.x - canvas_zero_point.x) / kOverworldMapSize;
578 int map_y = (mouse_position.y - canvas_zero_point.y) / kOverworldMapSize;
579
580 // Calculate the index of the map in the `maps_bmp_` vector
581 current_map_ = map_x + map_y * 8;
582 if (current_world_ == 1) {
583 current_map_ += 0x40;
584 } else if (current_world_ == 2) {
585 current_map_ += 0x80;
586 }
587 const int current_highlighted_map = current_map_;
588
589 if (!current_map_lock_) {
590 current_parent_ = overworld_.overworld_map(current_map_)->parent();
591 }
592
593 if (overworld_.overworld_map(current_map_)->is_large_map() ||
594 overworld_.overworld_map(current_map_)->large_index() != 0) {
595 const int highlight_parent =
596 overworld_.overworld_map(current_highlighted_map)->parent();
597 const int parent_map_x = highlight_parent % 8;
598 const int parent_map_y = highlight_parent / 8;
599 ow_map_canvas_.DrawOutline(parent_map_x * kOverworldMapSize,
600 parent_map_y * kOverworldMapSize, large_map_size,
601 large_map_size);
602 } else {
603 const int current_map_x = current_highlighted_map % 8;
604 const int current_map_y = current_highlighted_map / 8;
605 ow_map_canvas_.DrawOutline(current_map_x * kOverworldMapSize,
606 current_map_y * kOverworldMapSize,
608 }
609
610 if (maps_bmp_[current_map_].modified() ||
611 ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
615 maps_bmp_[current_map_].set_modified(false);
616 }
617
618 // If double clicked, toggle the current map
619 if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Right)) {
621 }
622
623 return absl::OkStatus();
624}
625
627 if (ImGui::IsMouseDragging(ImGuiMouseButton_Middle)) {
630 ow_map_canvas_.set_draggable(true);
632 }
633 if (ImGui::IsMouseReleased(ImGuiMouseButton_Middle) &&
636 ow_map_canvas_.set_draggable(false);
638 }
639}
640
642 if (all_gfx_loaded_) {
643 if (core::FeatureFlags::get().overworld.kLoadCustomOverworld) {
645 } else {
647 }
648 Separator();
649 }
650
653 ow_map_canvas_.DrawBackground();
655
658 ow_map_canvas_.DrawContextMenu();
659 } else {
660 ow_map_canvas_.set_draggable(false);
661 }
662
663 if (overworld_.is_loaded()) {
665 DrawOverworldExits(ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling());
667 ow_map_canvas_.scrolling());
672 }
673 if (IsItemHovered()) status_ = CheckForCurrentMap();
674 }
675
676 ow_map_canvas_.DrawGrid();
677 ow_map_canvas_.DrawOverlay();
678 EndChild();
679
680 // Handle mouse wheel activity
681 if (ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows) &&
682 ImGui::IsMouseDragging(ImGuiMouseButton_Middle)) {
683 ImGui::SetScrollX(ImGui::GetScrollX() + ImGui::GetIO().MouseWheelH * 16.0f);
684 ImGui::SetScrollY(ImGui::GetScrollY() + ImGui::GetIO().MouseWheel * 16.0f);
685 }
686}
687
690 ImGui::BeginGroup();
691 gui::BeginChildWithScrollbar("##Tile16SelectorScrollRegion");
692 blockset_canvas_.DrawBackground();
694 {
695 blockset_canvas_.DrawContextMenu();
696 blockset_canvas_.DrawBitmap(tile16_blockset_.atlas, /*border_offset=*/2,
697 map_blockset_loaded_, /*scale=*/2);
698
699 if (blockset_canvas_.DrawTileSelector(32.0f)) {
700 // Open the tile16 editor to the tile
701 auto tile_pos = blockset_canvas_.points().front();
702 int grid_x = static_cast<int>(tile_pos.x / 32);
703 int grid_y = static_cast<int>(tile_pos.y / 32);
704 int id = grid_x + grid_y * 8;
705 RETURN_IF_ERROR(tile16_editor_.SetCurrentTile(id));
706 show_tile16_editor_ = true;
707 }
708
709 if (ImGui::IsItemClicked() && !blockset_canvas_.points().empty()) {
710 int x = blockset_canvas_.points().front().x / 32;
711 int y = blockset_canvas_.points().front().y / 32;
712 current_tile16_ = x + (y * 8);
713 }
714
715 blockset_canvas_.DrawGrid();
716 blockset_canvas_.DrawOverlay();
717 }
718 EndChild();
719 ImGui::EndGroup();
720 return absl::OkStatus();
721}
722
724 graphics_bin_canvas_.DrawBackground();
725 graphics_bin_canvas_.DrawContextMenu();
726 if (all_gfx_loaded_) {
727 int key = 0;
728 for (auto &value : gfx::Arena::Get().gfx_sheets()) {
729 int offset = 0x40 * (key + 1);
730 int top_left_y = graphics_bin_canvas_.zero_point().y + 2;
731 if (key >= 1) {
732 top_left_y = graphics_bin_canvas_.zero_point().y + 0x40 * key;
733 }
734 auto texture = value.texture();
735 graphics_bin_canvas_.draw_list()->AddImage(
736 (ImTextureID)(intptr_t)texture,
737 ImVec2(graphics_bin_canvas_.zero_point().x + 2, top_left_y),
738 ImVec2(graphics_bin_canvas_.zero_point().x + 0x100,
739 graphics_bin_canvas_.zero_point().y + offset));
740 key++;
741 }
742 }
743 graphics_bin_canvas_.DrawGrid();
744 graphics_bin_canvas_.DrawOverlay();
745}
746
748 if (overworld_.is_loaded() && current_graphics_set_.contains(current_map_)) {
749 overworld_.set_current_map(current_map_);
750 palette_ = overworld_.current_area_palette();
751 gfx::Bitmap bmp;
753 overworld_.current_graphics(), bmp,
754 palette_);
756 }
757
759 ImGui::BeginGroup();
760 gui::BeginChildWithScrollbar("##AreaGraphicsScrollRegion");
761 current_gfx_canvas_.DrawBackground();
763 {
764 current_gfx_canvas_.DrawContextMenu();
766 /*border_offset=*/2, overworld_.is_loaded());
767 current_gfx_canvas_.DrawTileSelector(32.0f);
768 current_gfx_canvas_.DrawGrid();
769 current_gfx_canvas_.DrawOverlay();
770 }
771 EndChild();
772 ImGui::EndGroup();
773 return absl::OkStatus();
774}
775
777 if (BeginTabBar(kTileSelectorTab.data(),
778 ImGuiTabBarFlags_FittingPolicyScroll)) {
779 if (BeginTabItem("Tile16")) {
781 EndTabItem();
782 }
783 if (BeginTabItem("Tile8")) {
785 gui::BeginChildWithScrollbar("##Tile8SelectorScrollRegion");
787 EndChild();
789 EndTabItem();
790 }
791 if (BeginTabItem("Area Graphics")) {
793 EndTabItem();
794 }
795 EndTabBar();
796 }
797 return absl::OkStatus();
798}
799
800void OverworldEditor::DrawOverworldEntrances(ImVec2 canvas_p0, ImVec2 scrolling,
801 bool holes) {
802 int i = 0;
803 for (auto &each : overworld_.entrances()) {
804 if (each.map_id_ < 0x40 + (current_world_ * 0x40) &&
805 each.map_id_ >= (current_world_ * 0x40) && !each.deleted) {
806 auto color = ImVec4(255, 255, 0, 100);
807 if (each.is_hole_) {
808 color = ImVec4(255, 255, 255, 200);
809 }
810 ow_map_canvas_.DrawRect(each.x_, each.y_, 16, 16, color);
811 std::string str = util::HexByte(each.entrance_id_);
812
814 HandleEntityDragging(&each, canvas_p0, scrolling, is_dragging_entity_,
816
817 if (IsMouseHoveringOverEntity(each, canvas_p0, scrolling) &&
818 ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
819 jump_to_tab_ = each.entrance_id_;
820 }
821
822 if (IsMouseHoveringOverEntity(each, canvas_p0, scrolling) &&
823 ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
825 current_entrance_ = each;
826 }
827 }
828
829 ow_map_canvas_.DrawText(str, each.x_, each.y_);
830 }
831 i++;
832 }
833
835 // Get the deleted entrance ID and insert it at the mouse position
836 auto deleted_entrance_id = overworld_.deleted_entrances().back();
837 overworld_.deleted_entrances().pop_back();
838 auto &entrance = overworld_.entrances()[deleted_entrance_id];
839 entrance.map_id_ = current_map_;
840 entrance.entrance_id_ = deleted_entrance_id;
841 entrance.x_ = ow_map_canvas_.hover_mouse_pos().x;
842 entrance.y_ = ow_map_canvas_.hover_mouse_pos().y;
843 entrance.deleted = false;
844 }
845
847 const auto is_hovering =
848 IsMouseHoveringOverEntity(current_entrance_, canvas_p0, scrolling);
849
850 if (!is_hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
851 ImGui::OpenPopup("Entrance Inserter");
852 } else {
854 overworld_.entrances()[current_entrance_id_])) {
856 }
857
858 if (overworld_.entrances()[current_entrance_id_].deleted) {
859 overworld_.mutable_deleted_entrances()->emplace_back(
861 }
862 }
863 }
864}
865
866void OverworldEditor::DrawOverworldExits(ImVec2 canvas_p0, ImVec2 scrolling) {
867 int i = 0;
868 for (auto &each : *overworld_.mutable_exits()) {
869 if (each.map_id_ < 0x40 + (current_world_ * 0x40) &&
870 each.map_id_ >= (current_world_ * 0x40) && !each.deleted_) {
871 ow_map_canvas_.DrawRect(each.x_, each.y_, 16, 16,
872 ImVec4(255, 255, 255, 150));
874 each.entity_id_ = i;
875 HandleEntityDragging(&each, ow_map_canvas_.zero_point(),
878
879 if (IsMouseHoveringOverEntity(each, canvas_p0, scrolling) &&
880 ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
881 jump_to_tab_ = each.room_id_;
882 }
883
884 if (IsMouseHoveringOverEntity(each, canvas_p0, scrolling) &&
885 ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
887 current_exit_ = each;
888 current_entity_ = &each;
889 current_entity_->entity_id_ = i;
890 ImGui::OpenPopup("Exit editor");
891 }
892 }
893
894 std::string str = util::HexByte(i);
895 ow_map_canvas_.DrawText(str, each.x_, each.y_);
896 }
897 i++;
898 }
899
902 const auto hovering = IsMouseHoveringOverEntity(
903 overworld_.mutable_exits()->at(current_exit_id_),
904 ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling());
905
906 if (!hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
907 ImGui::OpenPopup("Exit Inserter");
908 } else {
910 overworld_.mutable_exits()->at(current_exit_id_))) {
911 overworld_.mutable_exits()->at(current_exit_id_) = current_exit_;
912 }
913 }
914 }
915}
916
918 int i = 0;
919 for (auto &item : *overworld_.mutable_all_items()) {
920 // Get the item's bitmap and real X and Y positions
921 if (item.room_map_id_ < 0x40 + (current_world_ * 0x40) &&
922 item.room_map_id_ >= (current_world_ * 0x40) && !item.deleted) {
923 ow_map_canvas_.DrawRect(item.x_, item.y_, 16, 16, ImVec4(255, 0, 0, 150));
924
926 // Check if this item is being clicked and dragged
927 HandleEntityDragging(&item, ow_map_canvas_.zero_point(),
930
931 const auto hovering = IsMouseHoveringOverEntity(
932 item, ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling());
933 if (hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
935 current_item_ = item;
936 current_entity_ = &item;
937 }
938 }
939 std::string item_name = "";
940 if (item.id_ < zelda3::kSecretItemNames.size()) {
941 item_name = zelda3::kSecretItemNames[item.id_];
942 } else {
943 item_name = absl::StrFormat("0x%02X", item.id_);
944 }
945 ow_map_canvas_.DrawText(item_name, item.x_, item.y_);
946 }
947 i++;
948 }
949
952 const auto hovering = IsMouseHoveringOverEntity(
953 overworld_.mutable_all_items()->at(current_item_id_),
954 ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling());
955
956 if (!hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
957 ImGui::OpenPopup("Item Inserter");
958 } else {
960 overworld_.mutable_all_items()->at(current_item_id_))) {
961 overworld_.mutable_all_items()->at(current_item_id_) = current_item_;
962 }
963 }
964 }
965}
966
968 int i = 0;
969 for (auto &sprite : *overworld_.mutable_sprites(game_state_)) {
970 if (!sprite.deleted()) {
971 int map_x = sprite.map_x();
972 int map_y = sprite.map_y();
973 ow_map_canvas_.DrawRect(map_x, map_y, kTile16Size, kTile16Size,
974 /*magenta=*/ImVec4(255, 0, 255, 150));
976 HandleEntityDragging(&sprite, ow_map_canvas_.zero_point(),
979 if (IsMouseHoveringOverEntity(sprite, ow_map_canvas_.zero_point(),
980 ow_map_canvas_.scrolling()) &&
981 ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
983 current_sprite_ = sprite;
984 }
985 }
986 if (core::FeatureFlags::get().overworld.kDrawOverworldSprites) {
987 if (sprite_previews_[sprite.id()].is_active()) {
988 ow_map_canvas_.DrawBitmap(sprite_previews_[sprite.id()], map_x, map_y,
989 2.0f);
990 }
991 }
992
993 ow_map_canvas_.DrawText(absl::StrFormat("%s", sprite.name()), map_x,
994 map_y);
995 }
996 i++;
997 }
998
1001 const auto hovering = IsMouseHoveringOverEntity(
1002 overworld_.mutable_sprites(game_state_)->at(current_sprite_id_),
1003 ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling());
1004
1005 if (!hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
1006 ImGui::OpenPopup("Sprite Inserter");
1007 } else {
1008 if (DrawSpriteEditorPopup(overworld_.mutable_sprites(game_state_)
1009 ->at(current_sprite_id_))) {
1010 overworld_.mutable_sprites(game_state_)->at(current_sprite_id_) =
1012 }
1013 }
1014 }
1015}
1016
1018 if (core::FeatureFlags::get().overworld.kSaveOverworldMaps) {
1019 RETURN_IF_ERROR(overworld_.CreateTile32Tilemap());
1020 RETURN_IF_ERROR(overworld_.SaveMap32Tiles());
1021 RETURN_IF_ERROR(overworld_.SaveMap16Tiles());
1022 RETURN_IF_ERROR(overworld_.SaveOverworldMaps());
1023 }
1024 if (core::FeatureFlags::get().overworld.kSaveOverworldEntrances) {
1025 RETURN_IF_ERROR(overworld_.SaveEntrances());
1026 }
1027 if (core::FeatureFlags::get().overworld.kSaveOverworldExits) {
1028 RETURN_IF_ERROR(overworld_.SaveExits());
1029 }
1030 if (core::FeatureFlags::get().overworld.kSaveOverworldItems) {
1031 RETURN_IF_ERROR(overworld_.SaveItems());
1032 }
1033 if (core::FeatureFlags::get().overworld.kSaveOverworldProperties) {
1034 RETURN_IF_ERROR(overworld_.SaveMapProperties());
1035 }
1036 return absl::OkStatus();
1037}
1038
1040 util::logf("Loading overworld.");
1041 // Load the Link to the Past overworld.
1043 palette_ = overworld_.current_area_palette();
1044
1045 util::logf("Loading overworld graphics.");
1046 // Create the area graphics image
1048 overworld_.current_graphics(),
1050
1051 util::logf("Loading overworld tileset.");
1052 // Create the tile16 blockset image
1053 Renderer::Get().CreateAndRenderBitmap(0x80, 0x2000, 0x08,
1054 overworld_.tile16_blockset_data(),
1056 map_blockset_loaded_ = true;
1057
1058 // Copy the tile16 data into individual tiles.
1059 auto tile16_blockset_data = overworld_.tile16_blockset_data();
1060 util::logf("Loading overworld tile16 graphics.");
1061
1063 gfx::CreateTilemap(tile16_blockset_data, 0x80, 0x2000, kTile16Size,
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 {
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 sprite_previews_[sprite.id()].SetPalette(palette_);
1104 Renderer::Get().RenderBitmap(&(sprite_previews_[sprite.id()]));
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 std::array<int, 4> indices = {0, 0, 0, 0};
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) {
1156 Renderer::Get().UpdateBitmap(&maps_bmp_[indices[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());
1172 maps_bmp_[sibling_index].SetPalette(current_map_palette);
1173 }
1174 }
1175
1176 maps_bmp_[current_map_].SetPalette(current_map_palette);
1177 return absl::OkStatus();
1178}
1179
1181 const auto &current_ow_map = *overworld_.mutable_overworld_map(current_map_);
1182 if (current_ow_map.is_large_map()) {
1183 // We need to copy the properties from the parent map to the children
1184 for (int i = 1; i < 4; i++) {
1185 int sibling_index = current_ow_map.parent() + i;
1186 if (i >= 2) {
1187 sibling_index += 6;
1188 }
1189 auto &map = *overworld_.mutable_overworld_map(sibling_index);
1190 map.set_area_graphics(current_ow_map.area_graphics());
1191 map.set_area_palette(current_ow_map.area_palette());
1192 map.set_sprite_graphics(game_state_,
1193 current_ow_map.sprite_graphics(game_state_));
1194 map.set_sprite_palette(game_state_,
1195 current_ow_map.sprite_palette(game_state_));
1196 map.set_message_id(current_ow_map.message_id());
1197 }
1198 }
1199}
1200
1202 if (current_blockset_ ==
1203 overworld_.overworld_map(current_map_)->area_graphics()) {
1204 return absl::OkStatus();
1205 }
1206 current_blockset_ = overworld_.overworld_map(current_map_)->area_graphics();
1207
1208 overworld_.set_current_map(current_map_);
1209 palette_ = overworld_.current_area_palette();
1210
1211 const auto tile16_data = overworld_.tile16_blockset_data();
1212
1214 tile16_blockset_.atlas.SetPalette(palette_);
1215 return absl::OkStatus();
1216}
1217
1219 static bool init_properties = false;
1220
1221 if (!init_properties) {
1222 for (int i = 0; i < 0x40; i++) {
1223 std::string area_graphics_str = absl::StrFormat(
1224 "%02hX", overworld_.overworld_map(i)->area_graphics());
1226 ->push_back(area_graphics_str);
1227
1228 area_graphics_str = absl::StrFormat(
1229 "%02hX", overworld_.overworld_map(i + 0x40)->area_graphics());
1231 ->push_back(area_graphics_str);
1232
1233 std::string area_palette_str =
1234 absl::StrFormat("%02hX", overworld_.overworld_map(i)->area_palette());
1236 ->push_back(area_palette_str);
1237
1238 area_palette_str = absl::StrFormat(
1239 "%02hX", overworld_.overworld_map(i + 0x40)->area_palette());
1241 ->push_back(area_palette_str);
1242 std::string sprite_gfx_str = absl::StrFormat(
1243 "%02hX", overworld_.overworld_map(i)->sprite_graphics(1));
1245 ->push_back(sprite_gfx_str);
1246
1247 sprite_gfx_str = absl::StrFormat(
1248 "%02hX", overworld_.overworld_map(i)->sprite_graphics(2));
1250 ->push_back(sprite_gfx_str);
1251
1252 sprite_gfx_str = absl::StrFormat(
1253 "%02hX", overworld_.overworld_map(i + 0x40)->sprite_graphics(1));
1255 ->push_back(sprite_gfx_str);
1256
1257 sprite_gfx_str = absl::StrFormat(
1258 "%02hX", overworld_.overworld_map(i + 0x40)->sprite_graphics(2));
1260 ->push_back(sprite_gfx_str);
1261
1262 std::string sprite_palette_str = absl::StrFormat(
1263 "%02hX", overworld_.overworld_map(i)->sprite_palette(1));
1265 ->push_back(sprite_palette_str);
1266
1267 sprite_palette_str = absl::StrFormat(
1268 "%02hX", overworld_.overworld_map(i)->sprite_palette(2));
1270 ->push_back(sprite_palette_str);
1271
1272 sprite_palette_str = absl::StrFormat(
1273 "%02hX", overworld_.overworld_map(i + 0x40)->sprite_palette(1));
1275 ->push_back(sprite_palette_str);
1276
1277 sprite_palette_str = absl::StrFormat(
1278 "%02hX", overworld_.overworld_map(i + 0x40)->sprite_palette(2));
1280 ->push_back(sprite_palette_str);
1281 }
1282 init_properties = true;
1283 }
1284
1285 Text("Area Gfx LW/DW");
1286 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 32,
1288 SameLine();
1289 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 32,
1291 ImGui::Separator();
1292
1293 Text("Sprite Gfx LW/DW");
1294 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 32,
1296 SameLine();
1297 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 32,
1299 SameLine();
1300 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 32,
1302 SameLine();
1303 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 32,
1305 ImGui::Separator();
1306
1307 Text("Area Pal LW/DW");
1308 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 32,
1310 SameLine();
1311 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 32,
1313
1314 static bool show_gfx_group = false;
1315 Checkbox("Show Gfx Group Editor", &show_gfx_group);
1316 if (show_gfx_group) {
1317 gui::BeginWindowWithDisplaySettings("Gfx Group Editor", &show_gfx_group);
1318 status_ = gfx_group_editor_.Update();
1320 }
1321}
1322
1324 if (BeginTable("UsageStatsTable", 3, kOWEditFlags, ImVec2(0, 0))) {
1325 TableSetupColumn("Entrances");
1326 TableSetupColumn("Grid", ImGuiTableColumnFlags_WidthStretch,
1327 ImGui::GetContentRegionAvail().x);
1328 TableSetupColumn("Usage", ImGuiTableColumnFlags_WidthFixed, 256);
1329 TableHeadersRow();
1330 TableNextRow();
1331
1332 TableNextColumn();
1333 if (BeginChild("UnusedSpritesetScroll", ImVec2(0, 0), true,
1334 ImGuiWindowFlags_HorizontalScrollbar)) {
1335 for (int i = 0; i < 0x81; i++) {
1336 auto entrance_name = rom_->resource_label()->CreateOrGetLabel(
1337 "Dungeon Entrance Names", util::HexByte(i),
1338 zelda3::kEntranceNames[i].data());
1339 std::string str = absl::StrFormat("%#x - %s", i, entrance_name);
1340 if (Selectable(str.c_str(), selected_entrance_ == i,
1341 overworld_.entrances().at(i).deleted
1342 ? ImGuiSelectableFlags_Disabled
1343 : 0)) {
1345 selected_usage_map_ = overworld_.entrances().at(i).map_id_;
1346 properties_canvas_.set_highlight_tile_id(selected_usage_map_);
1347 }
1348 if (IsItemHovered()) {
1349 BeginTooltip();
1350 Text("Entrance ID: %d", i);
1351 Text("Map ID: %d", overworld_.entrances().at(i).map_id_);
1352 Text("Entrance ID: %d", overworld_.entrances().at(i).entrance_id_);
1353 Text("X: %d", overworld_.entrances().at(i).x_);
1354 Text("Y: %d", overworld_.entrances().at(i).y_);
1355 Text("Deleted? %s",
1356 overworld_.entrances().at(i).deleted ? "Yes" : "No");
1357 EndTooltip();
1358 }
1359 }
1360 EndChild();
1361 }
1362
1363 TableNextColumn();
1364 DrawUsageGrid();
1365
1366 TableNextColumn();
1368
1369 EndTable();
1370 }
1371 return absl::OkStatus();
1372}
1373
1375 // Create a grid of 8x8 squares
1376 int total_squares = 128;
1377 int squares_wide = 8;
1378 int squares_tall = (total_squares + squares_wide - 1) /
1379 squares_wide; // Ceiling of total_squares/squares_wide
1380
1381 // Loop through each row
1382 for (int row = 0; row < squares_tall; ++row) {
1383 NewLine();
1384
1385 for (int col = 0; col < squares_wide; ++col) {
1386 if (row * squares_wide + col >= total_squares) {
1387 break;
1388 }
1389 // Determine if this square should be highlighted
1390 bool highlight = selected_usage_map_ == (row * squares_wide + col);
1391
1392 // Set highlight color if needed
1393 if (highlight) {
1394 PushStyleColor(ImGuiCol_Button,
1395 ImVec4(1.0f, 0.5f, 0.0f,
1396 1.0f)); // Or any highlight color
1397 }
1398
1399 // Create a button or selectable for each square
1400 if (Button("##square", ImVec2(20, 20))) {
1401 // Switch over to the room editor tab
1402 // and add a room tab by the ID of the square
1403 // that was clicked
1404 }
1405
1406 // Reset style if it was highlighted
1407 if (highlight) {
1408 PopStyleColor();
1409 }
1410
1411 // Check if the square is hovered
1412 if (IsItemHovered()) {
1413 // Display a tooltip with all the room properties
1414 }
1415
1416 // Keep squares in the same line
1417 SameLine();
1418 }
1419 }
1420}
1421
1423 Text("Current Map: %d", current_map_);
1424 Text("Current Tile16: %d", current_tile16_);
1425 int relative_x = (int)ow_map_canvas_.drawn_tile_position().x % 512;
1426 int relative_y = (int)ow_map_canvas_.drawn_tile_position().y % 512;
1427 Text("Current Tile16 Drawn Position (Relative): %d, %d", relative_x,
1428 relative_y);
1429
1430 // Print the size of the overworld map_tiles per world
1431 Text("Light World Map Tiles: %d",
1432 (int)overworld_.mutable_map_tiles()->light_world.size());
1433 Text("Dark World Map Tiles: %d",
1434 (int)overworld_.mutable_map_tiles()->dark_world.size());
1435 Text("Special World Map Tiles: %d",
1436 (int)overworld_.mutable_map_tiles()->special_world.size());
1437
1438 static bool view_lw_map_tiles = false;
1439 static MemoryEditor mem_edit;
1440 // Let's create buttons which let me view containers in the memory editor
1441 if (Button("View Light World Map Tiles")) {
1442 view_lw_map_tiles = !view_lw_map_tiles;
1443 }
1444
1445 if (view_lw_map_tiles) {
1446 mem_edit.DrawContents(
1447 overworld_.mutable_map_tiles()->light_world[current_map_].data(),
1448 overworld_.mutable_map_tiles()->light_world[current_map_].size());
1449 }
1450}
1451
1453 overworld_.Destroy();
1454 current_graphics_set_.clear();
1455 all_gfx_loaded_ = false;
1456 map_blockset_loaded_ = false;
1457 return absl::OkStatus();
1458}
1459
1460} // namespace yaze::editor
static Renderer & Get()
Definition window.h:37
static Flags & get()
Definition features.h:66
void CreateAndRenderBitmap(int width, int height, int depth, const std::vector< uint8_t > &data, gfx::Bitmap &bitmap, gfx::SnesPalette &palette)
Definition window.h:64
void UpdateBitmap(gfx::Bitmap *bitmap)
Definition window.h:60
void RenderBitmap(gfx::Bitmap *bitmap)
Definition window.h:56
absl::Status Clear() override
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)
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)
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.
void DrawOverworldEntrances(ImVec2 canvas_p, ImVec2 scrolling, bool holes=false)
zelda3::GameEntity * current_entity_
static Arena & Get()
Definition arena.cc:10
Represents a bitmap image.
Definition bitmap.h:59
void WriteToPixel(int position, uint8_t value)
Write a value to a pixel at the given position.
Definition bitmap.cc:383
void set_modified(bool modified)
Definition bitmap.h:165
#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
Tilemap CreateTilemap(std::vector< uint8_t > &data, int width, int height, int tile_size, int num_tiles, SnesPalette &palette)
Definition tilemap.cc:12
void UpdateTilemap(Tilemap &tilemap, const std::vector< uint8_t > &data)
Definition tilemap.cc:25
std::vector< uint8_t > GetTilemapData(Tilemap &tilemap, int tile_id)
Definition tilemap.cc:193
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:162
void AddTableColumn(Table &table, const std::string &label, GuiElement element)
Definition input.cc:371
void EndNoPadding()
Definition style.cc:382
void DrawTable(Table &params)
Definition input.cc:377
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:176
void BeginChildWithScrollbar(const char *str_id)
Definition style.cc:384
std::string HexByte(uint8_t byte, HexStringParams params)
Definition hex.cc:30
constexpr std::string_view kEntranceNames[]
Definition common.h:48
constexpr int kNumTile16Individual
Definition overworld.h:96
constexpr int kNumOverworldMaps
Definition overworld.h:95
const std::vector< std::string > kSecretItemNames
absl::StatusOr< OverworldEntranceTileTypes > LoadEntranceTileTypes(Rom *rom)