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 {
34namespace editor {
35
36using core::Renderer;
37using namespace ImGui;
38
39constexpr int kTile16Size = 0x10;
40
42 // Load zeml string from layouts/overworld.zeml
43 std::string layout = gui::zeml::LoadFile("overworld.zeml");
44 // Parse the zeml string into a Node object
46
47 gui::zeml::Bind(&*layout_node_.GetNode("OverworldCanvas"),
48 [this]() { DrawOverworldCanvas(); });
49 gui::zeml::Bind(&*layout_node_.GetNode("OverworldTileSelector"),
50 [this]() { status_ = DrawTileSelector(); });
51 gui::zeml::Bind(&*layout_node_.GetNode("OwUsageStats"), [this]() {
52 if (rom_->is_loaded()) {
53 status_ = UpdateUsageStats();
54 }
55 });
56 gui::zeml::Bind(&*layout_node_.GetNode("owToolset"),
57 [this]() { DrawToolset(); });
58 gui::zeml::Bind(&*layout_node_.GetNode("OwTile16Editor"), [this]() {
59 if (rom_->is_loaded()) {
60 status_ = tile16_editor_.Update();
61 }
62 });
63 gui::zeml::Bind(&*layout_node_.GetNode("OwGfxGroupEditor"), [this]() {
64 if (rom_->is_loaded()) {
65 status_ = gfx_group_editor_.Update();
66 }
67 });
68}
69
70absl::Status OverworldEditor::Load() {
74 *overworld_.mutable_all_tiles_types()));
76 all_gfx_loaded_ = true;
77 return absl::OkStatus();
78}
79
81 status_ = absl::OkStatus();
82
85 }
86
87 // Draw the overworld editor layout from the ZEML file
89 return status_;
90}
91
93 static bool use_work_area = true;
94 static ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration |
95 ImGuiWindowFlags_NoMove |
96 ImGuiWindowFlags_NoSavedSettings;
97 const ImGuiViewport *viewport = ImGui::GetMainViewport();
98 ImGui::SetNextWindowPos(use_work_area ? viewport->WorkPos : viewport->Pos);
99 ImGui::SetNextWindowSize(use_work_area ? viewport->WorkSize : viewport->Size);
100 if (ImGui::Begin("Fullscreen Overworld Editor", &overworld_canvas_fullscreen_,
101 flags)) {
102 // Draws the toolset for editing the Overworld.
103 DrawToolset();
105 }
106 ImGui::End();
107}
108
110 static bool show_gfx_group = false;
111 static bool show_properties = false;
112
113 if (toolset_table_.column_contents.empty()) {
114 gui::AddTableColumn(toolset_table_, "##Undo", [&]() {
115 if (Button(ICON_MD_UNDO)) status_ = Undo();
116 });
117 gui::AddTableColumn(toolset_table_, "##Redo", [&]() {
118 if (Button(ICON_MD_REDO)) status_ = Redo();
119 });
121 gui::AddTableColumn(toolset_table_, "##ZoomOut", [&]() {
122 if (Button(ICON_MD_ZOOM_OUT)) ow_map_canvas_.ZoomOut();
123 });
124 gui::AddTableColumn(toolset_table_, "##ZoomIn", [&]() {
125 if (Button(ICON_MD_ZOOM_IN)) ow_map_canvas_.ZoomIn();
126 });
127 gui::AddTableColumn(toolset_table_, "##Fullscreen", [&]() {
128 if (Button(ICON_MD_OPEN_IN_FULL))
130 HOVER_HINT("Fullscreen Canvas")
131 });
133 gui::AddTableColumn(toolset_table_, "##Pan", [&]() {
136 ow_map_canvas_.set_draggable(true);
137 }
138 HOVER_HINT("Pan (Right click and drag)");
139 });
140 gui::AddTableColumn(toolset_table_, "##DrawTile", [&]() {
143 }
144 HOVER_HINT("Draw Tile");
145 });
146 gui::AddTableColumn(toolset_table_, "##Entrances", [&]() {
147 if (Selectable(ICON_MD_DOOR_FRONT,
150 HOVER_HINT("Entrances");
151 });
152 gui::AddTableColumn(toolset_table_, "##Exits", [&]() {
155 HOVER_HINT("Exits");
156 });
157 gui::AddTableColumn(toolset_table_, "##Items", [&]() {
160 HOVER_HINT("Items");
161 });
162 gui::AddTableColumn(toolset_table_, "##Sprites", [&]() {
163 if (Selectable(ICON_MD_PEST_CONTROL_RODENT,
166 HOVER_HINT("Sprites");
167 });
168 gui::AddTableColumn(toolset_table_, "##Transports", [&]() {
169 if (Selectable(ICON_MD_ADD_LOCATION,
172 HOVER_HINT("Transports");
173 });
174 gui::AddTableColumn(toolset_table_, "##Music", [&]() {
177 HOVER_HINT("Music");
178 });
179 gui::AddTableColumn(toolset_table_, "##Tile16Editor", [&]() {
181 HOVER_HINT("Tile16 Editor");
182 });
183 gui::AddTableColumn(toolset_table_, "##GfxGroupEditor", [&]() {
184 if (Button(ICON_MD_TABLE_CHART)) show_gfx_group = !show_gfx_group;
185 HOVER_HINT("Gfx Group Editor");
186 });
188 gui::AddTableColumn(toolset_table_, "##Properties", [&]() {
189 if (Button(ICON_MD_CONTENT_COPY)) {
190 std::vector<uint8_t> png_data;
191 png_data = maps_bmp_[current_map_].GetPngData();
192 if (png_data.size() > 0) {
194 } else {
195 status_ = absl::InternalError(
196 "Failed to convert overworld map surface to PNG");
197 }
198 }
199 HOVER_HINT("Copy Map to Clipboard");
200 });
201 gui::AddTableColumn(toolset_table_, "##Palette", [&]() {
203 });
205 gui::AddTableColumn(toolset_table_, "##Properties",
206 [&]() { Checkbox("Properties", &show_properties); });
207
208 } else {
210 }
211
213 ImGui::Begin("Tile16 Editor", &show_tile16_editor_,
214 ImGuiWindowFlags_MenuBar);
215 status_ = tile16_editor_.Update();
216 ImGui::End();
217 }
218
219 if (show_gfx_group) {
220 gui::BeginWindowWithDisplaySettings("Gfx Group Editor", &show_gfx_group);
221 status_ = gfx_group_editor_.Update();
223 }
224
225 if (show_properties) {
226 ImGui::Begin("Properties", &show_properties);
228 ImGui::End();
229 }
230
231 // TODO: Customizable shortcuts for the Overworld Editor
232 if (!ImGui::IsAnyItemActive()) {
233 if (ImGui::IsKeyDown(ImGuiKey_1)) {
235 } else if (ImGui::IsKeyDown(ImGuiKey_2)) {
237 } else if (ImGui::IsKeyDown(ImGuiKey_3)) {
239 } else if (ImGui::IsKeyDown(ImGuiKey_4)) {
241 } else if (ImGui::IsKeyDown(ImGuiKey_5)) {
243 } else if (ImGui::IsKeyDown(ImGuiKey_6)) {
245 } else if (ImGui::IsKeyDown(ImGuiKey_7)) {
247 } else if (ImGui::IsKeyDown(ImGuiKey_8)) {
249 }
250 }
251}
252
253constexpr std::array<const char *, 8> kMapSettingsColumnNames = {
254 "##WorldId", "##GfxId", "##PalId", "##SprGfxId",
255 "##5thCol", "##6thCol", "##7thCol", "##8thCol"};
256
258 if (BeginTable(kOWMapTable.data(), 8, kOWMapFlags, ImVec2(0, 0), -1)) {
259 for (const auto &name : kMapSettingsColumnNames)
260 ImGui::TableSetupColumn(name);
261
262 TableNextColumn();
263 ImGui::SetNextItemWidth(120.f);
264 ImGui::Combo("##world", &current_world_, kWorldList.data(), 3);
265
266 TableNextColumn();
267 ImGui::BeginGroup();
268 if (gui::InputHexByte("Gfx",
269 overworld_.mutable_overworld_map(current_map_)
270 ->mutable_area_graphics(),
274 }
275 ImGui::EndGroup();
276
277 TableNextColumn();
278 ImGui::BeginGroup();
279 if (gui::InputHexByte("Palette",
280 overworld_.mutable_overworld_map(current_map_)
281 ->mutable_area_palette(),
286 }
287 ImGui::EndGroup();
288
289 TableNextColumn();
290 ImGui::BeginGroup();
291 gui::InputHexByte("Spr Gfx",
292 overworld_.mutable_overworld_map(current_map_)
293 ->mutable_sprite_graphics(game_state_),
295 ImGui::EndGroup();
296
297 TableNextColumn();
298 ImGui::BeginGroup();
299 gui::InputHexByte("Spr Palette",
300 overworld_.mutable_overworld_map(current_map_)
301 ->mutable_sprite_palette(game_state_),
303 ImGui::EndGroup();
304
305 TableNextColumn();
306 ImGui::BeginGroup();
308 "Msg Id",
309 overworld_.mutable_overworld_map(current_map_)->mutable_message_id(),
310 kInputFieldSize + 20);
311 ImGui::EndGroup();
312
313 TableNextColumn();
314 ImGui::SetNextItemWidth(100.f);
315 ImGui::Combo("##World", &game_state_, kGamePartComboString.data(), 3);
316
317 TableNextColumn();
318 ImGui::Checkbox(
319 "##mosaic",
320 overworld_.mutable_overworld_map(current_map_)->mutable_mosaic());
321 HOVER_HINT("Enable Mosaic effect for the current map");
322
323 ImGui::EndTable();
324 }
325}
326
328 if (BeginTable(kOWMapTable.data(), 15, kOWMapFlags, ImVec2(0, 0), -1)) {
329 for (const auto &name : kMapSettingsColumnNames)
330 ImGui::TableSetupColumn(name);
331
332 TableNextColumn();
333 ImGui::SetNextItemWidth(120.f);
334 ImGui::Combo("##world", &current_world_, kWorldList.data(), 3);
335
336 static const std::array<std::string, 8> kCustomMapSettingsColumnNames = {
337 "TileGfx0", "TileGfx1", "TileGfx2", "TileGfx3",
338 "TileGfx4", "TileGfx5", "TileGfx6", "TileGfx7"};
339 for (int i = 0; i < 8; ++i) {
340 TableNextColumn();
341 ImGui::BeginGroup();
342 if (gui::InputHexByte(kCustomMapSettingsColumnNames[i].data(),
343 overworld_.mutable_overworld_map(current_map_)
344 ->mutable_custom_tileset(i),
348 }
349 ImGui::EndGroup();
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 map_x = (xx * kOverworldMapSize * ow_map_canvas_.global_scale());
408 int map_y = (yy * kOverworldMapSize * ow_map_canvas_.global_scale());
409 ow_map_canvas_.DrawBitmap(maps_bmp_[world_index], map_x, map_y,
410 ow_map_canvas_.global_scale());
411 xx++;
412 if (xx >= 8) {
413 yy++;
414 xx = 0;
415 }
416 }
417}
418
420 // Determine which overworld map the user is currently editing.
421 auto mouse_position = ow_map_canvas_.drawn_tile_position();
422 int map_x = mouse_position.x / kOverworldMapSize;
423 int map_y = mouse_position.y / kOverworldMapSize;
424 current_map_ = map_x + map_y * 8;
425 if (current_world_ == 1) {
426 current_map_ += 0x40;
427 } else if (current_world_ == 2) {
428 current_map_ += 0x80;
429 }
430
431 // Render the updated map bitmap.
434
435 // Calculate the correct superX and superY values
436 int superY = current_map_ / 8;
437 int superX = current_map_ % 8;
438 int mouse_x = mouse_position.x;
439 int mouse_y = mouse_position.y;
440 // Calculate the correct tile16_x and tile16_y positions
441 int tile16_x = (mouse_x % kOverworldMapSize) / (kOverworldMapSize / 32);
442 int tile16_y = (mouse_y % kOverworldMapSize) / (kOverworldMapSize / 32);
443
444 // Update the overworld_.map_tiles() based on tile16 ID and current world
445 auto &selected_world =
446 (current_world_ == 0) ? overworld_.mutable_map_tiles()->light_world
447 : (current_world_ == 1) ? overworld_.mutable_map_tiles()->dark_world
448 : overworld_.mutable_map_tiles()->special_world;
449
450 int index_x = superX * 32 + tile16_x;
451 int index_y = superY * 32 + tile16_y;
452
453 selected_world[index_x][index_y] = current_tile16_;
454}
455
457 const ImVec2 &click_position, const std::vector<uint8_t> &tile_data) {
458 // Calculate the tile index for x and y based on the click_position
459 int tile_index_x =
460 (static_cast<int>(click_position.x) % kOverworldMapSize) / kTile16Size;
461 int tile_index_y =
462 (static_cast<int>(click_position.y) % kOverworldMapSize) / kTile16Size;
463
464 // Calculate the pixel start position based on tile index and tile size
465 ImVec2 start_position;
466 start_position.x = tile_index_x * kTile16Size;
467 start_position.y = tile_index_y * kTile16Size;
468
469 // Update the bitmap's pixel data based on the start_position and tile_data
470 gfx::Bitmap &current_bitmap = maps_bmp_[current_map_];
471 for (int y = 0; y < kTile16Size; ++y) {
472 for (int x = 0; x < kTile16Size; ++x) {
473 int pixel_index =
474 (start_position.y + y) * kOverworldMapSize + (start_position.x + x);
475 current_bitmap.WriteToPixel(pixel_index, tile_data[y * kTile16Size + x]);
476 }
477 }
478
479 current_bitmap.set_modified(true);
480}
481
484
485 // User has selected a tile they want to draw from the blockset.
486 if (!blockset_canvas_.points().empty() &&
487 !ow_map_canvas_.select_rect_active()) {
488 // Left click is pressed
489 if (ow_map_canvas_.DrawTilemapPainter(tile16_blockset_, current_tile16_)) {
491 }
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)) {
612 Renderer::Get().UpdateBitmap(&maps_bmp_[current_map_]);
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() &&
743 overworld_.set_current_map(current_map_);
744 palette_ = overworld_.current_area_palette();
745 gfx::Bitmap bmp;
746 Renderer::Get().CreateAndRenderBitmap(0x80, kOverworldMapSize, 0x08,
747 overworld_.current_graphics(),
748 bmp, palette_);
750 }
751
753 ImGui::BeginGroup();
754 gui::BeginChildWithScrollbar("##AreaGraphicsScrollRegion");
755 current_gfx_canvas_.DrawBackground();
757 {
758 current_gfx_canvas_.DrawContextMenu();
760 /*border_offset=*/2, overworld_.is_loaded());
761 current_gfx_canvas_.DrawTileSelector(32.0f);
762 current_gfx_canvas_.DrawGrid();
763 current_gfx_canvas_.DrawOverlay();
764 }
765 EndChild();
766 ImGui::EndGroup();
767 return absl::OkStatus();
768}
769
771 if (BeginTabBar(kTileSelectorTab.data(),
772 ImGuiTabBarFlags_FittingPolicyScroll)) {
773 if (BeginTabItem("Tile16")) {
775 EndTabItem();
776 }
777 if (BeginTabItem("Tile8")) {
779 gui::BeginChildWithScrollbar("##Tile8SelectorScrollRegion");
781 EndChild();
783 EndTabItem();
784 }
785 if (BeginTabItem("Area Graphics")) {
787 EndTabItem();
788 }
789 EndTabBar();
790 }
791 return absl::OkStatus();
792}
793
794void OverworldEditor::DrawOverworldEntrances(ImVec2 canvas_p0, ImVec2 scrolling,
795 bool holes) {
796 int i = 0;
797 for (auto &each : overworld_.entrances()) {
798 if (each.map_id_ < 0x40 + (current_world_ * 0x40) &&
799 each.map_id_ >= (current_world_ * 0x40) && !each.deleted) {
800 auto color = ImVec4(255, 255, 0, 100);
801 if (each.is_hole_) {
802 color = ImVec4(255, 255, 255, 200);
803 }
804 ow_map_canvas_.DrawRect(each.x_, each.y_, 16, 16, color);
805 std::string str = util::HexByte(each.entrance_id_);
806
808 HandleEntityDragging(&each, canvas_p0, scrolling, is_dragging_entity_,
810
811 if (IsMouseHoveringOverEntity(each, canvas_p0, scrolling) &&
812 ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
813 jump_to_tab_ = each.entrance_id_;
814 }
815
816 if (IsMouseHoveringOverEntity(each, canvas_p0, scrolling) &&
817 ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
819 current_entrance_ = each;
820 }
821 }
822
823 ow_map_canvas_.DrawText(str, each.x_, each.y_);
824 }
825 i++;
826 }
827
829 // Get the deleted entrance ID and insert it at the mouse position
830 auto deleted_entrance_id = overworld_.deleted_entrances().back();
831 overworld_.deleted_entrances().pop_back();
832 auto &entrance = overworld_.entrances()[deleted_entrance_id];
833 entrance.map_id_ = current_map_;
834 entrance.entrance_id_ = deleted_entrance_id;
835 entrance.x_ = ow_map_canvas_.hover_mouse_pos().x;
836 entrance.y_ = ow_map_canvas_.hover_mouse_pos().y;
837 entrance.deleted = false;
838 }
839
841 const auto is_hovering =
842 IsMouseHoveringOverEntity(current_entrance_, canvas_p0, scrolling);
843
844 if (!is_hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
845 ImGui::OpenPopup("Entrance Inserter");
846 } else {
848 overworld_.entrances()[current_entrance_id_])) {
850 }
851
852 if (overworld_.entrances()[current_entrance_id_].deleted) {
853 overworld_.mutable_deleted_entrances()->emplace_back(
855 }
856 }
857 }
858}
859
860void OverworldEditor::DrawOverworldExits(ImVec2 canvas_p0, ImVec2 scrolling) {
861 int i = 0;
862 for (auto &each : *overworld_.mutable_exits()) {
863 if (each.map_id_ < 0x40 + (current_world_ * 0x40) &&
864 each.map_id_ >= (current_world_ * 0x40) && !each.deleted_) {
865 ow_map_canvas_.DrawRect(each.x_, each.y_, 16, 16,
866 ImVec4(255, 255, 255, 150));
868 each.entity_id_ = i;
869 HandleEntityDragging(&each, ow_map_canvas_.zero_point(),
872
873 if (IsMouseHoveringOverEntity(each, canvas_p0, scrolling) &&
874 ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
875 jump_to_tab_ = each.room_id_;
876 }
877
878 if (IsMouseHoveringOverEntity(each, canvas_p0, scrolling) &&
879 ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
881 current_exit_ = each;
882 current_entity_ = &each;
883 current_entity_->entity_id_ = i;
884 ImGui::OpenPopup("Exit editor");
885 }
886 }
887
888 std::string str = util::HexByte(i);
889 ow_map_canvas_.DrawText(str, each.x_, each.y_);
890 }
891 i++;
892 }
893
896 const auto hovering = IsMouseHoveringOverEntity(
897 overworld_.mutable_exits()->at(current_exit_id_),
898 ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling());
899
900 if (!hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
901 ImGui::OpenPopup("Exit Inserter");
902 } else {
904 overworld_.mutable_exits()->at(current_exit_id_))) {
905 overworld_.mutable_exits()->at(current_exit_id_) = current_exit_;
906 }
907 }
908 }
909}
910
912 int i = 0;
913 for (auto &item : *overworld_.mutable_all_items()) {
914 // Get the item's bitmap and real X and Y positions
915 if (item.room_map_id_ < 0x40 + (current_world_ * 0x40) &&
916 item.room_map_id_ >= (current_world_ * 0x40) && !item.deleted) {
917 ow_map_canvas_.DrawRect(item.x_, item.y_, 16, 16, ImVec4(255, 0, 0, 150));
918
920 // Check if this item is being clicked and dragged
921 HandleEntityDragging(&item, ow_map_canvas_.zero_point(),
924
925 const auto hovering = IsMouseHoveringOverEntity(
926 item, ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling());
927 if (hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
929 current_item_ = item;
930 current_entity_ = &item;
931 }
932 }
933 std::string item_name = "";
934 if (item.id_ < zelda3::kSecretItemNames.size()) {
935 item_name = zelda3::kSecretItemNames[item.id_];
936 } else {
937 item_name = absl::StrFormat("0x%02X", item.id_);
938 }
939 ow_map_canvas_.DrawText(item_name, item.x_, item.y_);
940 }
941 i++;
942 }
943
946 const auto hovering = IsMouseHoveringOverEntity(
947 overworld_.mutable_all_items()->at(current_item_id_),
948 ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling());
949
950 if (!hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
951 ImGui::OpenPopup("Item Inserter");
952 } else {
954 overworld_.mutable_all_items()->at(current_item_id_))) {
955 overworld_.mutable_all_items()->at(current_item_id_) = current_item_;
956 }
957 }
958 }
959}
960
962 int i = 0;
963 for (auto &sprite : *overworld_.mutable_sprites(game_state_)) {
964 if (!sprite.deleted()) {
965 int map_x = sprite.map_x();
966 int map_y = sprite.map_y();
967 ow_map_canvas_.DrawRect(map_x, map_y, kTile16Size, kTile16Size,
968 /*magenta=*/ImVec4(255, 0, 255, 150));
970 HandleEntityDragging(&sprite, ow_map_canvas_.zero_point(),
973 if (IsMouseHoveringOverEntity(sprite, ow_map_canvas_.zero_point(),
974 ow_map_canvas_.scrolling()) &&
975 ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
977 current_sprite_ = sprite;
978 }
979 }
980 if (core::FeatureFlags::get().overworld.kDrawOverworldSprites) {
981 if (sprite_previews_[sprite.id()].is_active()) {
982 ow_map_canvas_.DrawBitmap(sprite_previews_[sprite.id()], map_x, map_y,
983 2.0f);
984 }
985 }
986
987 ow_map_canvas_.DrawText(absl::StrFormat("%s", sprite.name()), map_x,
988 map_y);
989 }
990 i++;
991 }
992
995 const auto hovering = IsMouseHoveringOverEntity(
996 overworld_.mutable_sprites(game_state_)->at(current_sprite_id_),
997 ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling());
998
999 if (!hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
1000 ImGui::OpenPopup("Sprite Inserter");
1001 } else {
1002 if (DrawSpriteEditorPopup(overworld_.mutable_sprites(game_state_)
1003 ->at(current_sprite_id_))) {
1004 overworld_.mutable_sprites(game_state_)->at(current_sprite_id_) =
1006 }
1007 }
1008 }
1009}
1010
1012 if (core::FeatureFlags::get().overworld.kSaveOverworldMaps) {
1013 RETURN_IF_ERROR(overworld_.CreateTile32Tilemap());
1014 RETURN_IF_ERROR(overworld_.SaveMap32Tiles());
1015 RETURN_IF_ERROR(overworld_.SaveMap16Tiles());
1016 RETURN_IF_ERROR(overworld_.SaveOverworldMaps());
1017 }
1018 if (core::FeatureFlags::get().overworld.kSaveOverworldEntrances) {
1019 RETURN_IF_ERROR(overworld_.SaveEntrances());
1020 }
1021 if (core::FeatureFlags::get().overworld.kSaveOverworldExits) {
1022 RETURN_IF_ERROR(overworld_.SaveExits());
1023 }
1024 if (core::FeatureFlags::get().overworld.kSaveOverworldItems) {
1025 RETURN_IF_ERROR(overworld_.SaveItems());
1026 }
1027 if (core::FeatureFlags::get().overworld.kSaveOverworldProperties) {
1028 RETURN_IF_ERROR(overworld_.SaveMapProperties());
1029 }
1030 return absl::OkStatus();
1031}
1032
1034 util::logf("Loading overworld.");
1035 // Load the Link to the Past overworld.
1037 palette_ = overworld_.current_area_palette();
1038
1039 util::logf("Loading overworld graphics.");
1040 // Create the area graphics image
1041 Renderer::Get().CreateAndRenderBitmap(0x80, kOverworldMapSize, 0x40,
1042 overworld_.current_graphics(),
1044
1045 util::logf("Loading overworld tileset.");
1046 // Create the tile16 blockset image
1047 Renderer::Get().CreateAndRenderBitmap(
1048 0x80, 0x2000, 0x08, overworld_.tile16_blockset_data(),
1050 map_blockset_loaded_ = true;
1051
1052 // Copy the tile16 data into individual tiles.
1053 auto tile16_blockset_data = overworld_.tile16_blockset_data();
1054 util::logf("Loading overworld tile16 graphics.");
1055
1057 gfx::CreateTilemap(tile16_blockset_data, 0x80, 0x2000, kTile16Size,
1059
1060 util::logf("Loading overworld maps.");
1061 // Render the overworld maps loaded from the ROM.
1062 for (int i = 0; i < zelda3::kNumOverworldMaps; ++i) {
1063 overworld_.set_current_map(i);
1064 auto palette = overworld_.current_area_palette();
1065 try {
1066 Renderer::Get().CreateAndRenderBitmap(
1068 overworld_.current_map_bitmap_data(), maps_bmp_[i], palette);
1069 } catch (const std::bad_alloc &e) {
1070 std::cout << "Error: " << e.what() << std::endl;
1071 continue;
1072 }
1073 }
1074
1075 if (core::FeatureFlags::get().overworld.kDrawOverworldSprites) {
1077 }
1078
1079 return absl::OkStatus();
1080}
1081
1083 // Render the sprites for each Overworld map
1084 const int depth = 0x10;
1085 for (int i = 0; i < 3; i++)
1086 for (auto const &sprite : *overworld_.mutable_sprites(i)) {
1087 int width = sprite.width();
1088 int height = sprite.height();
1089 if (width == 0 || height == 0) {
1090 continue;
1091 }
1092 if (sprite_previews_.size() < sprite.id()) {
1093 sprite_previews_.resize(sprite.id() + 1);
1094 }
1095 sprite_previews_[sprite.id()].Create(width, height, depth,
1096 *sprite.preview_graphics());
1097 sprite_previews_[sprite.id()].SetPalette(palette_);
1098 Renderer::Get().RenderBitmap(&(sprite_previews_[sprite.id()]));
1099 }
1100 return absl::OkStatus();
1101}
1102
1104 overworld_.mutable_overworld_map(map_index)->LoadAreaGraphics();
1105 status_ = overworld_.mutable_overworld_map(map_index)->BuildTileset();
1107 status_ = overworld_.mutable_overworld_map(map_index)->BuildTiles16Gfx(
1108 *overworld_.mutable_tiles16(), overworld_.tiles16().size());
1110 status_ = overworld_.mutable_overworld_map(map_index)->BuildBitmap(
1111 overworld_.GetMapTiles(current_world_));
1112 maps_bmp_[map_index].set_data(
1113 overworld_.mutable_overworld_map(map_index)->bitmap_data());
1114 maps_bmp_[map_index].set_modified(true);
1116}
1117
1119 std::vector<std::future<void>> futures;
1120 int indices[4];
1121
1122 auto refresh_map_async = [this](int map_index) {
1123 RefreshChildMap(map_index);
1124 };
1125
1126 int source_map_id = current_map_;
1127 bool is_large = overworld_.overworld_map(current_map_)->is_large_map();
1128 if (is_large) {
1129 source_map_id = current_parent_;
1130 // We need to update the map and its siblings if it's a large map
1131 for (int i = 1; i < 4; i++) {
1132 int sibling_index = overworld_.overworld_map(source_map_id)->parent() + i;
1133 if (i >= 2) sibling_index += 6;
1134 futures.push_back(
1135 std::async(std::launch::async, refresh_map_async, sibling_index));
1136 indices[i] = sibling_index;
1137 }
1138 }
1139 indices[0] = source_map_id;
1140 futures.push_back(
1141 std::async(std::launch::async, refresh_map_async, source_map_id));
1142
1143 for (auto &each : futures) {
1144 each.wait();
1145 each.get();
1146 }
1147 int n = is_large ? 4 : 1;
1148 // We do texture updating on the main thread
1149 for (int i = 0; i < n; ++i) {
1150 Renderer::Get().UpdateBitmap(&maps_bmp_[indices[i]]);
1151 }
1152}
1153
1156 overworld_.mutable_overworld_map(current_map_)->LoadPalette());
1157 const auto current_map_palette = overworld_.current_area_palette();
1158
1159 if (overworld_.overworld_map(current_map_)->is_large_map()) {
1160 // We need to update the map and its siblings if it's a large map
1161 for (int i = 1; i < 4; i++) {
1162 int sibling_index = overworld_.overworld_map(current_map_)->parent() + i;
1163 if (i >= 2) sibling_index += 6;
1165 overworld_.mutable_overworld_map(sibling_index)->LoadPalette());
1166 maps_bmp_[sibling_index].SetPalette(current_map_palette);
1167 }
1168 }
1169
1170 maps_bmp_[current_map_].SetPalette(current_map_palette);
1171 return absl::OkStatus();
1172}
1173
1175 auto &current_ow_map = *overworld_.mutable_overworld_map(current_map_);
1176 if (current_ow_map.is_large_map()) {
1177 // We need to copy the properties from the parent map to the children
1178 for (int i = 1; i < 4; i++) {
1179 int sibling_index = current_ow_map.parent() + i;
1180 if (i >= 2) {
1181 sibling_index += 6;
1182 }
1183 auto &map = *overworld_.mutable_overworld_map(sibling_index);
1184 map.set_area_graphics(current_ow_map.area_graphics());
1185 map.set_area_palette(current_ow_map.area_palette());
1186 map.set_sprite_graphics(game_state_,
1187 current_ow_map.sprite_graphics(game_state_));
1188 map.set_sprite_palette(game_state_,
1189 current_ow_map.sprite_palette(game_state_));
1190 map.set_message_id(current_ow_map.message_id());
1191 }
1192 }
1193}
1194
1196 if (current_blockset_ ==
1197 overworld_.overworld_map(current_map_)->area_graphics()) {
1198 return absl::OkStatus();
1199 }
1200 current_blockset_ = overworld_.overworld_map(current_map_)->area_graphics();
1201
1202 overworld_.set_current_map(current_map_);
1203 palette_ = overworld_.current_area_palette();
1204
1205 const auto tile16_data = overworld_.tile16_blockset_data();
1206
1208 tile16_blockset_.atlas.SetPalette(palette_);
1209 return absl::OkStatus();
1210}
1211
1213 static bool init_properties = false;
1214
1215 if (!init_properties) {
1216 for (int i = 0; i < 0x40; i++) {
1217 std::string area_graphics_str = absl::StrFormat(
1218 "%02hX", overworld_.overworld_map(i)->area_graphics());
1220 ->push_back(area_graphics_str);
1221
1222 area_graphics_str = absl::StrFormat(
1223 "%02hX", overworld_.overworld_map(i + 0x40)->area_graphics());
1225 ->push_back(area_graphics_str);
1226
1227 std::string area_palette_str =
1228 absl::StrFormat("%02hX", overworld_.overworld_map(i)->area_palette());
1230 ->push_back(area_palette_str);
1231
1232 area_palette_str = absl::StrFormat(
1233 "%02hX", overworld_.overworld_map(i + 0x40)->area_palette());
1235 ->push_back(area_palette_str);
1236 std::string sprite_gfx_str = absl::StrFormat(
1237 "%02hX", overworld_.overworld_map(i)->sprite_graphics(1));
1239 ->push_back(sprite_gfx_str);
1240
1241 sprite_gfx_str = absl::StrFormat(
1242 "%02hX", overworld_.overworld_map(i)->sprite_graphics(2));
1244 ->push_back(sprite_gfx_str);
1245
1246 sprite_gfx_str = absl::StrFormat(
1247 "%02hX", overworld_.overworld_map(i + 0x40)->sprite_graphics(1));
1249 ->push_back(sprite_gfx_str);
1250
1251 sprite_gfx_str = absl::StrFormat(
1252 "%02hX", overworld_.overworld_map(i + 0x40)->sprite_graphics(2));
1254 ->push_back(sprite_gfx_str);
1255
1256 std::string sprite_palette_str = absl::StrFormat(
1257 "%02hX", overworld_.overworld_map(i)->sprite_palette(1));
1259 ->push_back(sprite_palette_str);
1260
1261 sprite_palette_str = absl::StrFormat(
1262 "%02hX", overworld_.overworld_map(i)->sprite_palette(2));
1264 ->push_back(sprite_palette_str);
1265
1266 sprite_palette_str = absl::StrFormat(
1267 "%02hX", overworld_.overworld_map(i + 0x40)->sprite_palette(1));
1269 ->push_back(sprite_palette_str);
1270
1271 sprite_palette_str = absl::StrFormat(
1272 "%02hX", overworld_.overworld_map(i + 0x40)->sprite_palette(2));
1274 ->push_back(sprite_palette_str);
1275 }
1276 init_properties = true;
1277 }
1278
1279 Text("Area Gfx LW/DW");
1280 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 8, 2.0f, 32,
1282 SameLine();
1283 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 8, 2.0f, 32,
1285 ImGui::Separator();
1286
1287 Text("Sprite Gfx LW/DW");
1288 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 8, 2.0f, 32,
1290 SameLine();
1291 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 8, 2.0f, 32,
1293 SameLine();
1294 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 8, 2.0f, 32,
1296 SameLine();
1297 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 8, 2.0f, 32,
1299 ImGui::Separator();
1300
1301 Text("Area Pal LW/DW");
1302 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 8, 2.0f, 32,
1304 SameLine();
1305 properties_canvas_.UpdateInfoGrid(ImVec2(256, 256), 8, 2.0f, 32,
1307
1308 static bool show_gfx_group = false;
1309 Checkbox("Show Gfx Group Editor", &show_gfx_group);
1310 if (show_gfx_group) {
1311 gui::BeginWindowWithDisplaySettings("Gfx Group Editor", &show_gfx_group);
1312 status_ = gfx_group_editor_.Update();
1314 }
1315}
1316
1318 if (BeginTable("UsageStatsTable", 3, kOWEditFlags, ImVec2(0, 0))) {
1319 TableSetupColumn("Entrances");
1320 TableSetupColumn("Grid", ImGuiTableColumnFlags_WidthStretch,
1321 ImGui::GetContentRegionAvail().x);
1322 TableSetupColumn("Usage", ImGuiTableColumnFlags_WidthFixed, 256);
1323 TableHeadersRow();
1324 TableNextRow();
1325
1326 TableNextColumn();
1327 if (BeginChild("UnusedSpritesetScroll", ImVec2(0, 0), true,
1328 ImGuiWindowFlags_HorizontalScrollbar)) {
1329 for (int i = 0; i < 0x81; i++) {
1330 auto entrance_name = rom_->resource_label()->CreateOrGetLabel(
1331 "Dungeon Entrance Names", util::HexByte(i),
1332 zelda3::kEntranceNames[i].data());
1333 std::string str = absl::StrFormat("%#x - %s", i, entrance_name);
1334 if (Selectable(str.c_str(), selected_entrance_ == i,
1335 overworld_.entrances().at(i).deleted
1336 ? ImGuiSelectableFlags_Disabled
1337 : 0)) {
1339 selected_usage_map_ = overworld_.entrances().at(i).map_id_;
1340 properties_canvas_.set_highlight_tile_id(selected_usage_map_);
1341 }
1342 if (IsItemHovered()) {
1343 BeginTooltip();
1344 Text("Entrance ID: %d", i);
1345 Text("Map ID: %d", overworld_.entrances().at(i).map_id_);
1346 Text("Entrance ID: %d", overworld_.entrances().at(i).entrance_id_);
1347 Text("X: %d", overworld_.entrances().at(i).x_);
1348 Text("Y: %d", overworld_.entrances().at(i).y_);
1349 Text("Deleted? %s",
1350 overworld_.entrances().at(i).deleted ? "Yes" : "No");
1351 EndTooltip();
1352 }
1353 }
1354 EndChild();
1355 }
1356
1357 TableNextColumn();
1358 DrawUsageGrid();
1359
1360 TableNextColumn();
1362
1363 EndTable();
1364 }
1365 return absl::OkStatus();
1366}
1367
1369 // Create a grid of 8x8 squares
1370 int total_squares = 128;
1371 int squares_wide = 8;
1372 int squares_tall = (total_squares + squares_wide - 1) /
1373 squares_wide; // Ceiling of total_squares/squares_wide
1374
1375 // Loop through each row
1376 for (int row = 0; row < squares_tall; ++row) {
1377 NewLine();
1378
1379 for (int col = 0; col < squares_wide; ++col) {
1380 if (row * squares_wide + col >= total_squares) {
1381 break;
1382 }
1383 // Determine if this square should be highlighted
1384 bool highlight = selected_usage_map_ == (row * squares_wide + col);
1385
1386 // Set highlight color if needed
1387 if (highlight) {
1388 PushStyleColor(ImGuiCol_Button,
1389 ImVec4(1.0f, 0.5f, 0.0f,
1390 1.0f)); // Or any highlight color
1391 }
1392
1393 // Create a button or selectable for each square
1394 if (Button("##square", ImVec2(20, 20))) {
1395 // Switch over to the room editor tab
1396 // and add a room tab by the ID of the square
1397 // that was clicked
1398 }
1399
1400 // Reset style if it was highlighted
1401 if (highlight) {
1402 PopStyleColor();
1403 }
1404
1405 // Check if the square is hovered
1406 if (IsItemHovered()) {
1407 // Display a tooltip with all the room properties
1408 }
1409
1410 // Keep squares in the same line
1411 SameLine();
1412 }
1413 }
1414}
1415
1417 Text("Current Map: %d", current_map_);
1418 Text("Current Tile16: %d", current_tile16_);
1419 int relative_x = (int)ow_map_canvas_.drawn_tile_position().x % 512;
1420 int relative_y = (int)ow_map_canvas_.drawn_tile_position().y % 512;
1421 Text("Current Tile16 Drawn Position (Relative): %d, %d", relative_x,
1422 relative_y);
1423
1424 // Print the size of the overworld map_tiles per world
1425 Text("Light World Map Tiles: %d",
1426 (int)overworld_.mutable_map_tiles()->light_world.size());
1427 Text("Dark World Map Tiles: %d",
1428 (int)overworld_.mutable_map_tiles()->dark_world.size());
1429 Text("Special World Map Tiles: %d",
1430 (int)overworld_.mutable_map_tiles()->special_world.size());
1431
1432 static bool view_lw_map_tiles = false;
1433 static MemoryEditor mem_edit;
1434 // Let's create buttons which let me view containers in the memory editor
1435 if (Button("View Light World Map Tiles")) {
1436 view_lw_map_tiles = !view_lw_map_tiles;
1437 }
1438
1439 if (view_lw_map_tiles) {
1440 mem_edit.DrawContents(
1441 overworld_.mutable_map_tiles()->light_world[current_map_].data(),
1442 overworld_.mutable_map_tiles()->light_world[current_map_].size());
1443 }
1444}
1445
1447 overworld_.Destroy();
1448 current_graphics_set_.clear();
1449 all_gfx_loaded_ = false;
1450 map_blockset_loaded_ = false;
1451 return absl::OkStatus();
1452}
1453
1454} // namespace editor
1455} // namespace yaze
static Flags & get()
Definition features.h:66
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)
absl::Status Undo() override
zelda3::GameEntity * dragged_entity_
std::array< gfx::Bitmap, zelda3::kNumOverworldMaps > maps_bmp_
void CheckForOverworldEdits()
Check for changes to the overworld map.
zelda3::OverworldExit current_exit_
void RenderUpdatedMapBitmap(const ImVec2 &click_position, const std::vector< uint8_t > &tile_data)
absl::Status Redo() override
void CheckForSelectRectangle()
Draw and create the tile16 IDs that are currently selected.
absl::Status Load() override
std::vector< gfx::Bitmap > sprite_previews_
absl::Status LoadGraphics()
Load the Bitmap objects for each OverworldMap.
void DrawOverworldEntrances(ImVec2 canvas_p, ImVec2 scrolling, bool holes=false)
zelda3::GameEntity * current_entity_
static Renderer & Get()
Definition renderer.h:26
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)
Main namespace for the application.
Definition controller.cc:18