yaze 0.2.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
screen_editor.cc
Go to the documentation of this file.
1#include "screen_editor.h"
2
3#include <fstream>
4#include <iostream>
5#include <string>
6
7#include "absl/strings/str_format.h"
8#include "absl/strings/string_view.h"
11#include "app/gfx/arena.h"
12#include "app/gfx/bitmap.h"
13#include "app/gfx/snes_tile.h"
14#include "app/gui/canvas.h"
15#include "app/gui/color.h"
16#include "app/gui/icons.h"
17#include "app/gui/input.h"
18#include "imgui/imgui.h"
19#include "util/hex.h"
20#include "util/macro.h"
21
22namespace yaze {
23namespace editor {
24
25using core::Renderer;
26
27constexpr uint32_t kRedPen = 0xFF0000FF;
28
30
31absl::Status ScreenEditor::Load() {
35 tile16_blockset_, *rom(), rom()->graphics_buffer(), false));
36 // TODO: Load roomset gfx based on dungeon ID
37 sheets_.try_emplace(0, gfx::Arena::Get().gfx_sheets()[212]);
38 sheets_.try_emplace(1, gfx::Arena::Get().gfx_sheets()[213]);
39 sheets_.try_emplace(2, gfx::Arena::Get().gfx_sheets()[214]);
40 sheets_.try_emplace(3, gfx::Arena::Get().gfx_sheets()[215]);
41 int current_tile8 = 0;
42 int tile_data_offset = 0;
43 for (int i = 0; i < 4; ++i) {
44 for (int j = 0; j < 32; j++) {
45 std::vector<uint8_t> tile_data(64, 0); // 8x8 tile (64 bytes
46 int tile_index = current_tile8 + j;
47 int x = (j % 8) * 8;
48 int y = (j / 8) * 8;
49 sheets_[i].Get8x8Tile(tile_index, x, y, tile_data, tile_data_offset);
50 tile8_individual_.emplace_back(gfx::Bitmap(8, 8, 4, tile_data));
51 tile8_individual_.back().SetPalette(*rom()->mutable_dungeon_palette(3));
53 }
54 tile_data_offset = 0;
55 }
56 return absl::OkStatus();
57}
58
59absl::Status ScreenEditor::Update() {
60 if (ImGui::BeginTabBar("##ScreenEditorTabBar")) {
61 if (ImGui::BeginTabItem("Dungeon Maps")) {
63 ImGui::EndTabItem();
64 }
69 ImGui::EndTabBar();
70 }
71 return status_;
72}
73
75 if (ImGui::BeginTabItem("Inventory Menu")) {
76 static bool create = false;
77 if (!create && rom()->is_loaded()) {
78 status_ = inventory_.Create();
79 palette_ = inventory_.palette();
80 create = true;
81 }
82
84
85 if (ImGui::BeginTable("InventoryScreen", 3, ImGuiTableFlags_Resizable)) {
86 ImGui::TableSetupColumn("Canvas");
87 ImGui::TableSetupColumn("Tiles");
88 ImGui::TableSetupColumn("Palette");
89 ImGui::TableHeadersRow();
90
91 ImGui::TableNextColumn();
92 screen_canvas_.DrawBackground();
93 screen_canvas_.DrawContextMenu();
94 screen_canvas_.DrawBitmap(inventory_.bitmap(), 2, create);
95 screen_canvas_.DrawGrid(32.0f);
96 screen_canvas_.DrawOverlay();
97
98 ImGui::TableNextColumn();
99 tilesheet_canvas_.DrawBackground(ImVec2(128 * 2 + 2, (192 * 2) + 4));
100 tilesheet_canvas_.DrawContextMenu();
101 tilesheet_canvas_.DrawBitmap(inventory_.tilesheet(), 2, create);
102 tilesheet_canvas_.DrawGrid(16.0f);
103 tilesheet_canvas_.DrawOverlay();
104
105 ImGui::TableNextColumn();
107
108 ImGui::EndTable();
109 }
110 ImGui::Separator();
111 ImGui::EndTabItem();
112 }
113}
114
116 if (ImGui::BeginTable("InventoryToolset", 8, ImGuiTableFlags_SizingFixedFit,
117 ImVec2(0, 0))) {
118 ImGui::TableSetupColumn("#drawTool");
119 ImGui::TableSetupColumn("#sep1");
120 ImGui::TableSetupColumn("#zoomOut");
121 ImGui::TableSetupColumn("#zoomIN");
122 ImGui::TableSetupColumn("#sep2");
123 ImGui::TableSetupColumn("#bg2Tool");
124 ImGui::TableSetupColumn("#bg3Tool");
125 ImGui::TableSetupColumn("#itemTool");
126
127 ImGui::TableNextColumn();
128 if (ImGui::Button(ICON_MD_UNDO)) {
129 // status_ = inventory_.Undo();
130 }
131 ImGui::TableNextColumn();
132 if (ImGui::Button(ICON_MD_REDO)) {
133 // status_ = inventory_.Redo();
134 }
135 ImGui::TableNextColumn();
136 ImGui::Text(ICON_MD_MORE_VERT);
137 ImGui::TableNextColumn();
138 if (ImGui::Button(ICON_MD_ZOOM_OUT)) {
139 screen_canvas_.ZoomOut();
140 }
141 ImGui::TableNextColumn();
142 if (ImGui::Button(ICON_MD_ZOOM_IN)) {
143 screen_canvas_.ZoomIn();
144 }
145 ImGui::TableNextColumn();
146 ImGui::Text(ICON_MD_MORE_VERT);
147 ImGui::TableNextColumn();
148 if (ImGui::Button(ICON_MD_DRAW)) {
150 }
151 ImGui::TableNextColumn();
152 if (ImGui::Button(ICON_MD_BUILD)) {
153 // current_mode_ = EditingMode::BUILD;
154 }
155
156 ImGui::EndTable();
157 }
158}
159
161 auto &current_dungeon = dungeon_maps_[selected_dungeon];
162 if (ImGui::BeginTabBar("##DungeonMapTabs")) {
163 auto nbr_floors =
164 current_dungeon.nbr_of_floor + current_dungeon.nbr_of_basement;
165 for (int i = 0; i < nbr_floors; i++) {
166 int basement_num = current_dungeon.nbr_of_basement - i;
167 std::string tab_name = absl::StrFormat("Basement %d", basement_num);
168 if (i >= current_dungeon.nbr_of_basement) {
169 tab_name = absl::StrFormat("Floor %d",
170 i - current_dungeon.nbr_of_basement + 1);
171 }
172
173 if (ImGui::BeginTabItem(tab_name.c_str())) {
174 floor_number = i;
175 screen_canvas_.DrawBackground(ImVec2(325, 325));
176 screen_canvas_.DrawTileSelector(64.f);
177
178 auto boss_room = current_dungeon.boss_room;
179 for (int j = 0; j < zelda3::kNumRooms; j++) {
180 if (current_dungeon.floor_rooms[floor_number][j] != 0x0F) {
181 int tile16_id = current_dungeon.floor_gfx[floor_number][j];
182 int posX = ((j % 5) * 32);
183 int posY = ((j / 5) * 32);
184
186 screen_canvas_.DrawBitmap(tile16_blockset_.tile_bitmaps[tile16_id],
187 (posX * 2), (posY * 2), 4.0f);
188
189 if (current_dungeon.floor_rooms[floor_number][j] == boss_room) {
190 screen_canvas_.DrawOutlineWithColor((posX * 2), (posY * 2), 64,
191 64, kRedPen);
192 }
193
194 std::string label =
196 screen_canvas_.DrawText(label, (posX * 2), (posY * 2));
197 std::string gfx_id = util::HexByte(tile16_id);
198 screen_canvas_.DrawText(gfx_id, (posX * 2), (posY * 2) + 16);
199 }
200 }
201
202 screen_canvas_.DrawGrid(64.f, 5);
203 screen_canvas_.DrawOverlay();
204
205 if (!screen_canvas_.points().empty()) {
206 int x = screen_canvas_.points().front().x / 64;
207 int y = screen_canvas_.points().front().y / 64;
208 selected_room = x + (y * 5);
209 }
210 ImGui::EndTabItem();
211 }
212 }
213 ImGui::EndTabBar();
214 }
215
217 "Selected Room",
218 &current_dungeon.floor_rooms[floor_number].at(selected_room));
219
220 gui::InputHexWord("Boss Room", &current_dungeon.boss_room);
221
222 const ImVec2 button_size = ImVec2(130, 0);
223
224 // Add Floor Button
225 if (ImGui::Button("Add Floor", button_size) &&
226 current_dungeon.nbr_of_floor < 8) {
227 current_dungeon.nbr_of_floor++;
229 }
230 ImGui::SameLine();
231 if (ImGui::Button("Remove Floor", button_size) &&
232 current_dungeon.nbr_of_floor > 0) {
233 current_dungeon.nbr_of_floor--;
235 }
236
237 // Add Basement Button
238 if (ImGui::Button("Add Basement", button_size) &&
239 current_dungeon.nbr_of_basement < 8) {
240 current_dungeon.nbr_of_basement++;
242 }
243 ImGui::SameLine();
244 if (ImGui::Button("Remove Basement", button_size) &&
245 current_dungeon.nbr_of_basement > 0) {
246 current_dungeon.nbr_of_basement--;
248 }
249
250 if (ImGui::Button("Copy Floor", button_size)) {
251 copy_button_pressed = true;
252 }
253 ImGui::SameLine();
254 if (ImGui::Button("Paste Floor", button_size)) {
256 }
257}
258
260 if (ImGui::BeginChild("##DungeonMapTiles", ImVec2(0, 0), true)) {
261 tilesheet_canvas_.DrawBackground(ImVec2((256 * 2) + 2, (192 * 2) + 4));
262 tilesheet_canvas_.DrawContextMenu();
263 if (tilesheet_canvas_.DrawTileSelector(32.f)) {
264 selected_tile16_ = tilesheet_canvas_.points().front().x / 32 +
265 (tilesheet_canvas_.points().front().y / 32) * 16;
267 std::copy(tile16_blockset_.tile_info[selected_tile16_].begin(),
268 tile16_blockset_.tile_info[selected_tile16_].end(),
269 current_tile16_info.begin());
270 }
271 tilesheet_canvas_.DrawBitmap(tile16_blockset_.atlas, 1, 1, 2.0f);
272 tilesheet_canvas_.DrawGrid(32.f);
273 tilesheet_canvas_.DrawOverlay();
274
275 if (!tilesheet_canvas_.points().empty()) {
276 if (!screen_canvas_.points().empty()) {
279 tilesheet_canvas_.mutable_points()->clear();
280 }
281 }
282
283 ImGui::Separator();
284 current_tile_canvas_.DrawBackground(); // ImVec2(64 * 2 + 2, 64 * 2 + 4));
285 current_tile_canvas_.DrawContextMenu();
286 // if
287 // (current_tile_canvas_.DrawTilePainter(tile8_individual_[selected_tile8_],
288 // 16)) {
289 // Modify the tile16 based on the selected tile and current_tile16_info
290 // }
291 current_tile_canvas_.DrawBitmap(
292 tile16_blockset_.tile_bitmaps[selected_tile16_], 2, 4.0f);
293 current_tile_canvas_.DrawGrid(16.f);
294 current_tile_canvas_.DrawOverlay();
295
297 ImGui::SameLine();
300 ImGui::SameLine();
302
303 if (ImGui::Button("Modify Tile16")) {
304 gfx::ModifyTile16(tile16_blockset_, rom()->graphics_buffer(),
309 }
310 }
311 ImGui::EndChild();
312}
313
315 if (ImGui::Button(ICON_MD_DRAW)) {
317 }
318 ImGui::SameLine();
319 if (ImGui::Button(ICON_MD_EDIT)) {
321 }
322 ImGui::SameLine();
323 if (ImGui::Button(ICON_MD_SAVE)) {
325 }
326
327 static std::vector<std::string> dungeon_names = {
328 "Sewers/Sanctuary", "Hyrule Castle", "Eastern Palace",
329 "Desert Palace", "Tower of Hera", "Agahnim's Tower",
330 "Palace of Darkness", "Swamp Palace", "Skull Woods",
331 "Thieves' Town", "Ice Palace", "Misery Mire",
332 "Turtle Rock", "Ganon's Tower"};
333
334 if (ImGui::BeginTable("DungeonMapsTable", 4,
335 ImGuiTableFlags_Resizable |
336 ImGuiTableFlags_Reorderable |
337 ImGuiTableFlags_Hideable)) {
338 ImGui::TableSetupColumn("Dungeon");
339 ImGui::TableSetupColumn("Map");
340 ImGui::TableSetupColumn("Rooms Gfx");
341 ImGui::TableSetupColumn("Tiles Gfx");
342 ImGui::TableHeadersRow();
343
344 ImGui::TableNextColumn();
345 for (int i = 0; i < dungeon_names.size(); i++) {
347 selected_dungeon == i, "Dungeon Names", absl::StrFormat("%d", i),
348 dungeon_names[i]);
349 if (ImGui::IsItemClicked()) {
351 }
352 }
353
354 ImGui::TableNextColumn();
356
357 ImGui::TableNextColumn();
359
360 ImGui::TableNextColumn();
361 tilemap_canvas_.DrawBackground();
362 tilemap_canvas_.DrawContextMenu();
363 if (tilemap_canvas_.DrawTileSelector(16.f)) {
364 // Get the tile8 ID to use for the tile16 drawing above
365 selected_tile8_ = tilemap_canvas_.GetTileIdFromMousePos();
366 }
367 tilemap_canvas_.DrawBitmapTable(sheets_);
368 tilemap_canvas_.DrawGrid();
369 tilemap_canvas_.DrawOverlay();
370
371 ImGui::Text("Selected tile8: %d", selected_tile8_);
372 ImGui::Separator();
373 ImGui::Text("For use with custom inserted graphics assembly patches.");
374 if (ImGui::Button("Load GFX from BIN file")) LoadBinaryGfx();
375
376 ImGui::EndTable();
377 }
378}
379
381 std::string bin_file = core::FileDialogWrapper::ShowOpenFileDialog();
382 if (!bin_file.empty()) {
383 std::ifstream file(bin_file, std::ios::binary);
384 if (file.is_open()) {
385 // Read the gfx data into a buffer
386 std::vector<uint8_t> bin_data((std::istreambuf_iterator<char>(file)),
387 std::istreambuf_iterator<char>());
388 auto converted_bin = gfx::SnesTo8bppSheet(bin_data, 4, 4);
390 true)
391 .ok()) {
392 sheets_.clear();
393 std::vector<std::vector<uint8_t>> gfx_sheets;
394 for (int i = 0; i < 4; i++) {
395 gfx_sheets.emplace_back(converted_bin.begin() + (i * 0x1000),
396 converted_bin.begin() + ((i + 1) * 0x1000));
397 sheets_.emplace(i, gfx::Bitmap(128, 32, 8, gfx_sheets[i]));
398 sheets_[i].SetPalette(*rom()->mutable_dungeon_palette(3));
400 }
401 binary_gfx_loaded_ = true;
402 } else {
403 status_ = absl::InternalError("Failed to load dungeon map tile16");
404 }
405 file.close();
406 }
407 }
408}
409
411 if (ImGui::BeginTabItem("Title Screen")) {
412 ImGui::EndTabItem();
413 }
414}
415
417 if (ImGui::BeginTabItem("Naming Screen")) {
418 ImGui::EndTabItem();
419 }
420}
421
423 if (ImGui::BeginTabItem("Overworld Map")) {
424 ImGui::EndTabItem();
425 }
426}
427
429 static bool show_bg1 = true;
430 static bool show_bg2 = true;
431 static bool show_bg3 = true;
432
433 static bool drawing_bg1 = true;
434 static bool drawing_bg2 = false;
435 static bool drawing_bg3 = false;
436
437 ImGui::Checkbox("Show BG1", &show_bg1);
438 ImGui::SameLine();
439 ImGui::Checkbox("Show BG2", &show_bg2);
440
441 ImGui::Checkbox("Draw BG1", &drawing_bg1);
442 ImGui::SameLine();
443 ImGui::Checkbox("Draw BG2", &drawing_bg2);
444 ImGui::SameLine();
445 ImGui::Checkbox("Draw BG3", &drawing_bg3);
446}
447
448} // namespace editor
449} // namespace yaze
ResourceLabelManager * resource_label()
Definition rom.h:194
static std::string ShowOpenFileDialog()
ShowOpenFileDialog opens a file dialog and returns the selected filepath.
void RenderBitmap(gfx::Bitmap *bitmap)
Definition renderer.h:45
static Renderer & Get()
Definition renderer.h:26
std::vector< gfx::Bitmap > tile8_individual_
gfx::SnesPalette palette_
std::array< gfx::TileInfo, 4 > current_tile16_info
absl::Status Load() override
absl::Status Update() override
zelda3::Inventory inventory_
zelda3::DungeonMapLabels dungeon_map_labels_
std::vector< zelda3::DungeonMap > dungeon_maps_
static Arena & Get()
Definition arena.cc:10
Represents a bitmap image.
Definition bitmap.h:59
#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_REDO
Definition icons.h:1568
#define ICON_MD_EDIT
Definition icons.h:643
#define ICON_MD_BUILD
Definition icons.h:326
#define ICON_MD_ZOOM_IN
Definition icons.h:2189
#define ICON_MD_SAVE
Definition icons.h:1642
#define ICON_MD_UNDO
Definition icons.h:2034
#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
Editors are the view controllers for the application.
constexpr uint32_t kRedPen
void ModifyTile16(Tilemap &tilemap, const std::vector< uint8_t > &data, const TileInfo &top_left, const TileInfo &top_right, const TileInfo &bottom_left, const TileInfo &bottom_right, int sheet_offset, int tile_id)
Definition tilemap.cc:146
void UpdateTile16(Tilemap &tilemap, int tile_id)
Definition tilemap.cc:59
void RenderTile16(Tilemap &tilemap, int tile_id)
Definition tilemap.cc:42
std::vector< uint8_t > SnesTo8bppSheet(std::span< uint8_t > sheet, int bpp, int num_sheets)
Definition snes_tile.cc:129
bool InputHexWord(const char *label, uint16_t *data, float input_width, bool no_step)
Definition input.cc:162
bool InputTileInfo(const char *label, gfx::TileInfo *tile_info)
Definition input.cc:292
IMGUI_API bool DisplayPalette(gfx::SnesPalette &palette, bool loaded)
Definition color.cc:54
bool InputHexByte(const char *label, uint8_t *data, float input_width, bool no_step)
Definition input.cc:176
std::string HexByte(uint8_t byte, HexStringParams params)
Definition hex.cc:30
absl::Status LoadDungeonMapTile16(gfx::Tilemap &tile16_blockset, Rom &rom, const std::vector< uint8_t > &gfx_data, bool bin_mode)
Load the dungeon map tile16 from the ROM.
constexpr int kNumRooms
Definition dungeon_map.h:35
absl::Status SaveDungeonMapTile16(gfx::Tilemap &tile16_blockset, Rom &rom)
Save the dungeon map tile16 to the ROM.
absl::StatusOr< std::vector< DungeonMap > > LoadDungeonMaps(Rom &rom, DungeonMapLabels &dungeon_map_labels)
Load the dungeon maps from the ROM.
Main namespace for the application.
Definition controller.cc:18
void SelectableLabelWithNameEdit(bool selected, const std::string &type, const std::string &key, const std::string &defaultValue)
Definition project.cc:146