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/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 current_parent_ = overworld_.overworld_map(current_map_)->parent();
590
591 if (overworld_.overworld_map(current_map_)->is_large_map() ||
592 overworld_.overworld_map(current_map_)->large_index() != 0) {
593 const int highlight_parent =
594 overworld_.overworld_map(current_highlighted_map)->parent();
595 const int parent_map_x = highlight_parent % 8;
596 const int parent_map_y = highlight_parent / 8;
597 ow_map_canvas_.DrawOutline(parent_map_x * kOverworldMapSize,
598 parent_map_y * kOverworldMapSize, large_map_size,
599 large_map_size);
600 } else {
601 const int current_map_x = current_highlighted_map % 8;
602 const int current_map_y = current_highlighted_map / 8;
603 ow_map_canvas_.DrawOutline(current_map_x * kOverworldMapSize,
604 current_map_y * kOverworldMapSize,
606 }
607
608 if (maps_bmp_[current_map_].modified() ||
609 ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
613 maps_bmp_[current_map_].set_modified(false);
614 }
615
616 return absl::OkStatus();
617}
618
620 if (ImGui::IsMouseDragging(ImGuiMouseButton_Middle)) {
623 ow_map_canvas_.set_draggable(true);
625 }
626 if (ImGui::IsMouseReleased(ImGuiMouseButton_Middle) &&
629 ow_map_canvas_.set_draggable(false);
631 }
632}
633
635 if (all_gfx_loaded_) {
636 if (core::FeatureFlags::get().overworld.kLoadCustomOverworld) {
638 } else {
640 }
641 Separator();
642 }
643
646 ow_map_canvas_.DrawBackground();
648
651 ow_map_canvas_.DrawContextMenu();
652 } else {
653 ow_map_canvas_.set_draggable(false);
654 }
655
656 if (overworld_.is_loaded()) {
658 DrawOverworldExits(ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling());
660 ow_map_canvas_.scrolling());
665 }
666 if (IsItemHovered()) status_ = CheckForCurrentMap();
667 }
668
669 ow_map_canvas_.DrawGrid();
670 ow_map_canvas_.DrawOverlay();
671 EndChild();
672
673 // Handle mouse wheel activity
674 if (ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows) &&
675 ImGui::IsMouseDragging(ImGuiMouseButton_Middle)) {
676 ImGui::SetScrollX(ImGui::GetScrollX() + ImGui::GetIO().MouseWheelH * 16.0f);
677 ImGui::SetScrollY(ImGui::GetScrollY() + ImGui::GetIO().MouseWheel * 16.0f);
678 }
679}
680
683 ImGui::BeginGroup();
684 gui::BeginChildWithScrollbar("##Tile16SelectorScrollRegion");
685 blockset_canvas_.DrawBackground();
687 {
688 blockset_canvas_.DrawContextMenu();
689 blockset_canvas_.DrawBitmap(tile16_blockset_.atlas, /*border_offset=*/2,
690 map_blockset_loaded_, /*scale=*/2);
691
692 if (blockset_canvas_.DrawTileSelector(32.0f)) {
693 // Open the tile16 editor to the tile
694 auto tile_pos = blockset_canvas_.points().front();
695 int grid_x = static_cast<int>(tile_pos.x / 32);
696 int grid_y = static_cast<int>(tile_pos.y / 32);
697 int id = grid_x + grid_y * 8;
698 RETURN_IF_ERROR(tile16_editor_.SetCurrentTile(id));
699 show_tile16_editor_ = true;
700 }
701
702 if (ImGui::IsItemClicked() && !blockset_canvas_.points().empty()) {
703 int x = blockset_canvas_.points().front().x / 32;
704 int y = blockset_canvas_.points().front().y / 32;
705 current_tile16_ = x + (y * 8);
706 }
707
708 blockset_canvas_.DrawGrid();
709 blockset_canvas_.DrawOverlay();
710 }
711 EndChild();
712 ImGui::EndGroup();
713 return absl::OkStatus();
714}
715
717 graphics_bin_canvas_.DrawBackground();
718 graphics_bin_canvas_.DrawContextMenu();
719 if (all_gfx_loaded_) {
720 int key = 0;
721 for (auto &value : gfx::Arena::Get().gfx_sheets()) {
722 int offset = 0x40 * (key + 1);
723 int top_left_y = graphics_bin_canvas_.zero_point().y + 2;
724 if (key >= 1) {
725 top_left_y = graphics_bin_canvas_.zero_point().y + 0x40 * key;
726 }
727 auto texture = value.texture();
728 graphics_bin_canvas_.draw_list()->AddImage(
729 (ImTextureID)(intptr_t)texture,
730 ImVec2(graphics_bin_canvas_.zero_point().x + 2, top_left_y),
731 ImVec2(graphics_bin_canvas_.zero_point().x + 0x100,
732 graphics_bin_canvas_.zero_point().y + offset));
733 key++;
734 }
735 }
736 graphics_bin_canvas_.DrawGrid();
737 graphics_bin_canvas_.DrawOverlay();
738}
739
741 if (overworld_.is_loaded() && current_graphics_set_.contains(current_map_)) {
742 overworld_.set_current_map(current_map_);
743 palette_ = overworld_.current_area_palette();
744 gfx::Bitmap bmp;
746 overworld_.current_graphics(), bmp,
747 palette_);
749 }
750
752 ImGui::BeginGroup();
753 gui::BeginChildWithScrollbar("##AreaGraphicsScrollRegion");
754 current_gfx_canvas_.DrawBackground();
756 {
757 current_gfx_canvas_.DrawContextMenu();
759 /*border_offset=*/2, overworld_.is_loaded());
760 current_gfx_canvas_.DrawTileSelector(32.0f);
761 current_gfx_canvas_.DrawGrid();
762 current_gfx_canvas_.DrawOverlay();
763 }
764 EndChild();
765 ImGui::EndGroup();
766 return absl::OkStatus();
767}
768
770 if (BeginTabBar(kTileSelectorTab.data(),
771 ImGuiTabBarFlags_FittingPolicyScroll)) {
772 if (BeginTabItem("Tile16")) {
774 EndTabItem();
775 }
776 if (BeginTabItem("Tile8")) {
778 gui::BeginChildWithScrollbar("##Tile8SelectorScrollRegion");
780 EndChild();
782 EndTabItem();
783 }
784 if (BeginTabItem("Area Graphics")) {
786 EndTabItem();
787 }
788 EndTabBar();
789 }
790 return absl::OkStatus();
791}
792
793void OverworldEditor::DrawOverworldEntrances(ImVec2 canvas_p0, ImVec2 scrolling,
794 bool holes) {
795 int i = 0;
796 for (auto &each : overworld_.entrances()) {
797 if (each.map_id_ < 0x40 + (current_world_ * 0x40) &&
798 each.map_id_ >= (current_world_ * 0x40) && !each.deleted) {
799 auto color = ImVec4(255, 255, 0, 100);
800 if (each.is_hole_) {
801 color = ImVec4(255, 255, 255, 200);
802 }
803 ow_map_canvas_.DrawRect(each.x_, each.y_, 16, 16, color);
804 std::string str = util::HexByte(each.entrance_id_);
805
807 HandleEntityDragging(&each, canvas_p0, scrolling, is_dragging_entity_,
809
810 if (IsMouseHoveringOverEntity(each, canvas_p0, scrolling) &&
811 ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
812 jump_to_tab_ = each.entrance_id_;
813 }
814
815 if (IsMouseHoveringOverEntity(each, canvas_p0, scrolling) &&
816 ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
818 current_entrance_ = each;
819 }
820 }
821
822 ow_map_canvas_.DrawText(str, each.x_, each.y_);
823 }
824 i++;
825 }
826
828 // Get the deleted entrance ID and insert it at the mouse position
829 auto deleted_entrance_id = overworld_.deleted_entrances().back();
830 overworld_.deleted_entrances().pop_back();
831 auto &entrance = overworld_.entrances()[deleted_entrance_id];
832 entrance.map_id_ = current_map_;
833 entrance.entrance_id_ = deleted_entrance_id;
834 entrance.x_ = ow_map_canvas_.hover_mouse_pos().x;
835 entrance.y_ = ow_map_canvas_.hover_mouse_pos().y;
836 entrance.deleted = false;
837 }
838
840 const auto is_hovering =
841 IsMouseHoveringOverEntity(current_entrance_, canvas_p0, scrolling);
842
843 if (!is_hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
844 ImGui::OpenPopup("Entrance Inserter");
845 } else {
847 overworld_.entrances()[current_entrance_id_])) {
849 }
850
851 if (overworld_.entrances()[current_entrance_id_].deleted) {
852 overworld_.mutable_deleted_entrances()->emplace_back(
854 }
855 }
856 }
857}
858
859void OverworldEditor::DrawOverworldExits(ImVec2 canvas_p0, ImVec2 scrolling) {
860 int i = 0;
861 for (auto &each : *overworld_.mutable_exits()) {
862 if (each.map_id_ < 0x40 + (current_world_ * 0x40) &&
863 each.map_id_ >= (current_world_ * 0x40) && !each.deleted_) {
864 ow_map_canvas_.DrawRect(each.x_, each.y_, 16, 16,
865 ImVec4(255, 255, 255, 150));
867 each.entity_id_ = i;
868 HandleEntityDragging(&each, ow_map_canvas_.zero_point(),
871
872 if (IsMouseHoveringOverEntity(each, canvas_p0, scrolling) &&
873 ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
874 jump_to_tab_ = each.room_id_;
875 }
876
877 if (IsMouseHoveringOverEntity(each, canvas_p0, scrolling) &&
878 ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
880 current_exit_ = each;
881 current_entity_ = &each;
882 current_entity_->entity_id_ = i;
883 ImGui::OpenPopup("Exit editor");
884 }
885 }
886
887 std::string str = util::HexByte(i);
888 ow_map_canvas_.DrawText(str, each.x_, each.y_);
889 }
890 i++;
891 }
892
895 const auto hovering = IsMouseHoveringOverEntity(
896 overworld_.mutable_exits()->at(current_exit_id_),
897 ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling());
898
899 if (!hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
900 ImGui::OpenPopup("Exit Inserter");
901 } else {
903 overworld_.mutable_exits()->at(current_exit_id_))) {
904 overworld_.mutable_exits()->at(current_exit_id_) = current_exit_;
905 }
906 }
907 }
908}
909
911 int i = 0;
912 for (auto &item : *overworld_.mutable_all_items()) {
913 // Get the item's bitmap and real X and Y positions
914 if (item.room_map_id_ < 0x40 + (current_world_ * 0x40) &&
915 item.room_map_id_ >= (current_world_ * 0x40) && !item.deleted) {
916 ow_map_canvas_.DrawRect(item.x_, item.y_, 16, 16, ImVec4(255, 0, 0, 150));
917
919 // Check if this item is being clicked and dragged
920 HandleEntityDragging(&item, ow_map_canvas_.zero_point(),
923
924 const auto hovering = IsMouseHoveringOverEntity(
925 item, ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling());
926 if (hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
928 current_item_ = item;
929 current_entity_ = &item;
930 }
931 }
932 std::string item_name = "";
933 if (item.id_ < zelda3::kSecretItemNames.size()) {
934 item_name = zelda3::kSecretItemNames[item.id_];
935 } else {
936 item_name = absl::StrFormat("0x%02X", item.id_);
937 }
938 ow_map_canvas_.DrawText(item_name, item.x_, item.y_);
939 }
940 i++;
941 }
942
945 const auto hovering = IsMouseHoveringOverEntity(
946 overworld_.mutable_all_items()->at(current_item_id_),
947 ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling());
948
949 if (!hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
950 ImGui::OpenPopup("Item Inserter");
951 } else {
953 overworld_.mutable_all_items()->at(current_item_id_))) {
954 overworld_.mutable_all_items()->at(current_item_id_) = current_item_;
955 }
956 }
957 }
958}
959
961 int i = 0;
962 for (auto &sprite : *overworld_.mutable_sprites(game_state_)) {
963 if (!sprite.deleted()) {
964 int map_x = sprite.map_x();
965 int map_y = sprite.map_y();
966 ow_map_canvas_.DrawRect(map_x, map_y, kTile16Size, kTile16Size,
967 /*magenta=*/ImVec4(255, 0, 255, 150));
969 HandleEntityDragging(&sprite, ow_map_canvas_.zero_point(),
972 if (IsMouseHoveringOverEntity(sprite, ow_map_canvas_.zero_point(),
973 ow_map_canvas_.scrolling()) &&
974 ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
976 current_sprite_ = sprite;
977 }
978 }
979 if (core::FeatureFlags::get().overworld.kDrawOverworldSprites) {
980 if (sprite_previews_[sprite.id()].is_active()) {
981 ow_map_canvas_.DrawBitmap(sprite_previews_[sprite.id()], map_x, map_y,
982 2.0f);
983 }
984 }
985
986 ow_map_canvas_.DrawText(absl::StrFormat("%s", sprite.name()), map_x,
987 map_y);
988 }
989 i++;
990 }
991
994 const auto hovering = IsMouseHoveringOverEntity(
995 overworld_.mutable_sprites(game_state_)->at(current_sprite_id_),
996 ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling());
997
998 if (!hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
999 ImGui::OpenPopup("Sprite Inserter");
1000 } else {
1001 if (DrawSpriteEditorPopup(overworld_.mutable_sprites(game_state_)
1002 ->at(current_sprite_id_))) {
1003 overworld_.mutable_sprites(game_state_)->at(current_sprite_id_) =
1005 }
1006 }
1007 }
1008}
1009
1011 if (core::FeatureFlags::get().overworld.kSaveOverworldMaps) {
1012 RETURN_IF_ERROR(overworld_.CreateTile32Tilemap());
1013 RETURN_IF_ERROR(overworld_.SaveMap32Tiles());
1014 RETURN_IF_ERROR(overworld_.SaveMap16Tiles());
1015 RETURN_IF_ERROR(overworld_.SaveOverworldMaps());
1016 }
1017 if (core::FeatureFlags::get().overworld.kSaveOverworldEntrances) {
1018 RETURN_IF_ERROR(overworld_.SaveEntrances());
1019 }
1020 if (core::FeatureFlags::get().overworld.kSaveOverworldExits) {
1021 RETURN_IF_ERROR(overworld_.SaveExits());
1022 }
1023 if (core::FeatureFlags::get().overworld.kSaveOverworldItems) {
1024 RETURN_IF_ERROR(overworld_.SaveItems());
1025 }
1026 if (core::FeatureFlags::get().overworld.kSaveOverworldProperties) {
1027 RETURN_IF_ERROR(overworld_.SaveMapProperties());
1028 }
1029 return absl::OkStatus();
1030}
1031
1033 util::logf("Loading overworld.");
1034 // Load the Link to the Past overworld.
1036 palette_ = overworld_.current_area_palette();
1037
1038 util::logf("Loading overworld graphics.");
1039 // Create the area graphics image
1041 overworld_.current_graphics(),
1043
1044 util::logf("Loading overworld tileset.");
1045 // Create the tile16 blockset image
1046 Renderer::Get().CreateAndRenderBitmap(0x80, 0x2000, 0x08,
1047 overworld_.tile16_blockset_data(),
1049 map_blockset_loaded_ = true;
1050
1051 // Copy the tile16 data into individual tiles.
1052 auto tile16_blockset_data = overworld_.tile16_blockset_data();
1053 util::logf("Loading overworld tile16 graphics.");
1054
1056 gfx::CreateTilemap(tile16_blockset_data, 0x80, 0x2000, kTile16Size,
1058
1059 util::logf("Loading overworld maps.");
1060 // Render the overworld maps loaded from the ROM.
1061 for (int i = 0; i < zelda3::kNumOverworldMaps; ++i) {
1062 overworld_.set_current_map(i);
1063 auto palette = overworld_.current_area_palette();
1064 try {
1067 overworld_.current_map_bitmap_data(), maps_bmp_[i], palette);
1068 } catch (const std::bad_alloc &e) {
1069 std::cout << "Error: " << e.what() << std::endl;
1070 continue;
1071 }
1072 }
1073
1074 if (core::FeatureFlags::get().overworld.kDrawOverworldSprites) {
1076 }
1077
1078 return absl::OkStatus();
1079}
1080
1082 // Render the sprites for each Overworld map
1083 const int depth = 0x10;
1084 for (int i = 0; i < 3; i++)
1085 for (auto const &sprite : *overworld_.mutable_sprites(i)) {
1086 int width = sprite.width();
1087 int height = sprite.height();
1088 if (width == 0 || height == 0) {
1089 continue;
1090 }
1091 if (sprite_previews_.size() < sprite.id()) {
1092 sprite_previews_.resize(sprite.id() + 1);
1093 }
1094 sprite_previews_[sprite.id()].Create(width, height, depth,
1095 *sprite.preview_graphics());
1096 sprite_previews_[sprite.id()].SetPalette(palette_);
1097 Renderer::Get().RenderBitmap(&(sprite_previews_[sprite.id()]));
1098 }
1099 return absl::OkStatus();
1100}
1101
1103 overworld_.mutable_overworld_map(map_index)->LoadAreaGraphics();
1104 status_ = overworld_.mutable_overworld_map(map_index)->BuildTileset();
1106 status_ = overworld_.mutable_overworld_map(map_index)->BuildTiles16Gfx(
1107 *overworld_.mutable_tiles16(), overworld_.tiles16().size());
1109 status_ = overworld_.mutable_overworld_map(map_index)->BuildBitmap(
1110 overworld_.GetMapTiles(current_world_));
1111 maps_bmp_[map_index].set_data(
1112 overworld_.mutable_overworld_map(map_index)->bitmap_data());
1113 maps_bmp_[map_index].set_modified(true);
1115}
1116
1118 std::vector<std::future<void>> futures;
1119 std::array<int, 4> indices = {0, 0, 0, 0};
1120
1121 auto refresh_map_async = [this](int map_index) {
1122 RefreshChildMap(map_index);
1123 };
1124
1125 int source_map_id = current_map_;
1126 bool is_large = overworld_.overworld_map(current_map_)->is_large_map();
1127 if (is_large) {
1128 source_map_id = current_parent_;
1129 // We need to update the map and its siblings if it's a large map
1130 for (int i = 1; i < 4; i++) {
1131 int sibling_index = overworld_.overworld_map(source_map_id)->parent() + i;
1132 if (i >= 2) sibling_index += 6;
1133 futures.push_back(
1134 std::async(std::launch::async, refresh_map_async, sibling_index));
1135 indices[i] = sibling_index;
1136 }
1137 }
1138 indices[0] = source_map_id;
1139 futures.push_back(
1140 std::async(std::launch::async, refresh_map_async, source_map_id));
1141
1142 for (auto &each : futures) {
1143 each.wait();
1144 each.get();
1145 }
1146 int n = is_large ? 4 : 1;
1147 // We do texture updating on the main thread
1148 for (int i = 0; i < n; ++i) {
1149 Renderer::Get().UpdateBitmap(&maps_bmp_[indices[i]]);
1150 }
1151}
1152
1155 overworld_.mutable_overworld_map(current_map_)->LoadPalette());
1156 const auto current_map_palette = overworld_.current_area_palette();
1157
1158 if (overworld_.overworld_map(current_map_)->is_large_map()) {
1159 // We need to update the map and its siblings if it's a large map
1160 for (int i = 1; i < 4; i++) {
1161 int sibling_index = overworld_.overworld_map(current_map_)->parent() + i;
1162 if (i >= 2) sibling_index += 6;
1164 overworld_.mutable_overworld_map(sibling_index)->LoadPalette());
1165 maps_bmp_[sibling_index].SetPalette(current_map_palette);
1166 }
1167 }
1168
1169 maps_bmp_[current_map_].SetPalette(current_map_palette);
1170 return absl::OkStatus();
1171}
1172
1174 const auto &current_ow_map = *overworld_.mutable_overworld_map(current_map_);
1175 if (current_ow_map.is_large_map()) {
1176 // We need to copy the properties from the parent map to the children
1177 for (int i = 1; i < 4; i++) {
1178 int sibling_index = current_ow_map.parent() + i;
1179 if (i >= 2) {
1180 sibling_index += 6;
1181 }
1182 auto &map = *overworld_.mutable_overworld_map(sibling_index);
1183 map.set_area_graphics(current_ow_map.area_graphics());
1184 map.set_area_palette(current_ow_map.area_palette());
1185 map.set_sprite_graphics(game_state_,
1186 current_ow_map.sprite_graphics(game_state_));
1187 map.set_sprite_palette(game_state_,
1188 current_ow_map.sprite_palette(game_state_));
1189 map.set_message_id(current_ow_map.message_id());
1190 }
1191 }
1192}
1193
1195 if (current_blockset_ ==
1196 overworld_.overworld_map(current_map_)->area_graphics()) {
1197 return absl::OkStatus();
1198 }
1199 current_blockset_ = overworld_.overworld_map(current_map_)->area_graphics();
1200
1201 overworld_.set_current_map(current_map_);
1202 palette_ = overworld_.current_area_palette();
1203
1204 const auto tile16_data = overworld_.tile16_blockset_data();
1205
1207 tile16_blockset_.atlas.SetPalette(palette_);
1208 return absl::OkStatus();
1209}
1210
1212 static bool init_properties = false;
1213
1214 if (!init_properties) {
1215 for (int i = 0; i < 0x40; i++) {
1216 std::string area_graphics_str = absl::StrFormat(
1217 "%02hX", overworld_.overworld_map(i)->area_graphics());
1219 ->push_back(area_graphics_str);
1220
1221 area_graphics_str = absl::StrFormat(
1222 "%02hX", overworld_.overworld_map(i + 0x40)->area_graphics());
1224 ->push_back(area_graphics_str);
1225
1226 std::string area_palette_str =
1227 absl::StrFormat("%02hX", overworld_.overworld_map(i)->area_palette());
1229 ->push_back(area_palette_str);
1230
1231 area_palette_str = absl::StrFormat(
1232 "%02hX", overworld_.overworld_map(i + 0x40)->area_palette());
1234 ->push_back(area_palette_str);
1235 std::string sprite_gfx_str = absl::StrFormat(
1236 "%02hX", overworld_.overworld_map(i)->sprite_graphics(1));
1238 ->push_back(sprite_gfx_str);
1239
1240 sprite_gfx_str = absl::StrFormat(
1241 "%02hX", overworld_.overworld_map(i)->sprite_graphics(2));
1243 ->push_back(sprite_gfx_str);
1244
1245 sprite_gfx_str = absl::StrFormat(
1246 "%02hX", overworld_.overworld_map(i + 0x40)->sprite_graphics(1));
1248 ->push_back(sprite_gfx_str);
1249
1250 sprite_gfx_str = absl::StrFormat(
1251 "%02hX", overworld_.overworld_map(i + 0x40)->sprite_graphics(2));
1253 ->push_back(sprite_gfx_str);
1254
1255 std::string sprite_palette_str = absl::StrFormat(
1256 "%02hX", overworld_.overworld_map(i)->sprite_palette(1));
1258 ->push_back(sprite_palette_str);
1259
1260 sprite_palette_str = absl::StrFormat(
1261 "%02hX", overworld_.overworld_map(i)->sprite_palette(2));
1263 ->push_back(sprite_palette_str);
1264
1265 sprite_palette_str = absl::StrFormat(
1266 "%02hX", overworld_.overworld_map(i + 0x40)->sprite_palette(1));
1268 ->push_back(sprite_palette_str);
1269
1270 sprite_palette_str = absl::StrFormat(
1271 "%02hX", overworld_.overworld_map(i + 0x40)->sprite_palette(2));
1273 ->push_back(sprite_palette_str);
1274 }
1275 init_properties = true;
1276 }
1277
1278 Text("Area Gfx LW/DW");
1279 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 32,
1281 SameLine();
1282 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 32,
1284 ImGui::Separator();
1285
1286 Text("Sprite Gfx LW/DW");
1287 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 32,
1289 SameLine();
1290 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 32,
1292 SameLine();
1293 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 32,
1295 SameLine();
1296 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 32,
1298 ImGui::Separator();
1299
1300 Text("Area Pal LW/DW");
1301 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 32,
1303 SameLine();
1304 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 32,
1306
1307 static bool show_gfx_group = false;
1308 Checkbox("Show Gfx Group Editor", &show_gfx_group);
1309 if (show_gfx_group) {
1310 gui::BeginWindowWithDisplaySettings("Gfx Group Editor", &show_gfx_group);
1311 status_ = gfx_group_editor_.Update();
1313 }
1314}
1315
1317 if (BeginTable("UsageStatsTable", 3, kOWEditFlags, ImVec2(0, 0))) {
1318 TableSetupColumn("Entrances");
1319 TableSetupColumn("Grid", ImGuiTableColumnFlags_WidthStretch,
1320 ImGui::GetContentRegionAvail().x);
1321 TableSetupColumn("Usage", ImGuiTableColumnFlags_WidthFixed, 256);
1322 TableHeadersRow();
1323 TableNextRow();
1324
1325 TableNextColumn();
1326 if (BeginChild("UnusedSpritesetScroll", ImVec2(0, 0), true,
1327 ImGuiWindowFlags_HorizontalScrollbar)) {
1328 for (int i = 0; i < 0x81; i++) {
1329 auto entrance_name = rom_->resource_label()->CreateOrGetLabel(
1330 "Dungeon Entrance Names", util::HexByte(i),
1331 zelda3::kEntranceNames[i].data());
1332 std::string str = absl::StrFormat("%#x - %s", i, entrance_name);
1333 if (Selectable(str.c_str(), selected_entrance_ == i,
1334 overworld_.entrances().at(i).deleted
1335 ? ImGuiSelectableFlags_Disabled
1336 : 0)) {
1338 selected_usage_map_ = overworld_.entrances().at(i).map_id_;
1339 properties_canvas_.set_highlight_tile_id(selected_usage_map_);
1340 }
1341 if (IsItemHovered()) {
1342 BeginTooltip();
1343 Text("Entrance ID: %d", i);
1344 Text("Map ID: %d", overworld_.entrances().at(i).map_id_);
1345 Text("Entrance ID: %d", overworld_.entrances().at(i).entrance_id_);
1346 Text("X: %d", overworld_.entrances().at(i).x_);
1347 Text("Y: %d", overworld_.entrances().at(i).y_);
1348 Text("Deleted? %s",
1349 overworld_.entrances().at(i).deleted ? "Yes" : "No");
1350 EndTooltip();
1351 }
1352 }
1353 EndChild();
1354 }
1355
1356 TableNextColumn();
1357 DrawUsageGrid();
1358
1359 TableNextColumn();
1361
1362 EndTable();
1363 }
1364 return absl::OkStatus();
1365}
1366
1368 // Create a grid of 8x8 squares
1369 int total_squares = 128;
1370 int squares_wide = 8;
1371 int squares_tall = (total_squares + squares_wide - 1) /
1372 squares_wide; // Ceiling of total_squares/squares_wide
1373
1374 // Loop through each row
1375 for (int row = 0; row < squares_tall; ++row) {
1376 NewLine();
1377
1378 for (int col = 0; col < squares_wide; ++col) {
1379 if (row * squares_wide + col >= total_squares) {
1380 break;
1381 }
1382 // Determine if this square should be highlighted
1383 bool highlight = selected_usage_map_ == (row * squares_wide + col);
1384
1385 // Set highlight color if needed
1386 if (highlight) {
1387 PushStyleColor(ImGuiCol_Button,
1388 ImVec4(1.0f, 0.5f, 0.0f,
1389 1.0f)); // Or any highlight color
1390 }
1391
1392 // Create a button or selectable for each square
1393 if (Button("##square", ImVec2(20, 20))) {
1394 // Switch over to the room editor tab
1395 // and add a room tab by the ID of the square
1396 // that was clicked
1397 }
1398
1399 // Reset style if it was highlighted
1400 if (highlight) {
1401 PopStyleColor();
1402 }
1403
1404 // Check if the square is hovered
1405 if (IsItemHovered()) {
1406 // Display a tooltip with all the room properties
1407 }
1408
1409 // Keep squares in the same line
1410 SameLine();
1411 }
1412 }
1413}
1414
1416 Text("Current Map: %d", current_map_);
1417 Text("Current Tile16: %d", current_tile16_);
1418 int relative_x = (int)ow_map_canvas_.drawn_tile_position().x % 512;
1419 int relative_y = (int)ow_map_canvas_.drawn_tile_position().y % 512;
1420 Text("Current Tile16 Drawn Position (Relative): %d, %d", relative_x,
1421 relative_y);
1422
1423 // Print the size of the overworld map_tiles per world
1424 Text("Light World Map Tiles: %d",
1425 (int)overworld_.mutable_map_tiles()->light_world.size());
1426 Text("Dark World Map Tiles: %d",
1427 (int)overworld_.mutable_map_tiles()->dark_world.size());
1428 Text("Special World Map Tiles: %d",
1429 (int)overworld_.mutable_map_tiles()->special_world.size());
1430
1431 static bool view_lw_map_tiles = false;
1432 static MemoryEditor mem_edit;
1433 // Let's create buttons which let me view containers in the memory editor
1434 if (Button("View Light World Map Tiles")) {
1435 view_lw_map_tiles = !view_lw_map_tiles;
1436 }
1437
1438 if (view_lw_map_tiles) {
1439 mem_edit.DrawContents(
1440 overworld_.mutable_map_tiles()->light_world[current_map_].data(),
1441 overworld_.mutable_map_tiles()->light_world[current_map_].size());
1442 }
1443}
1444
1446 overworld_.Destroy();
1447 current_graphics_set_.clear();
1448 all_gfx_loaded_ = false;
1449 map_blockset_loaded_ = false;
1450 return absl::OkStatus();
1451}
1452
1453} // namespace yaze::editor
static Renderer & Get()
Definition renderer.h:26
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 renderer.h:53
void UpdateBitmap(gfx::Bitmap *bitmap)
Definition renderer.h:49
void RenderBitmap(gfx::Bitmap *bitmap)
Definition renderer.h:45
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:97
constexpr int kNumOverworldMaps
Definition overworld.h:96
const std::vector< std::string > kSecretItemNames
absl::StatusOr< OverworldEntranceTileTypes > LoadEntranceTileTypes(Rom *rom)