yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
entity.cc
Go to the documentation of this file.
2
3#include "app/gui/icons.h"
4#include "app/gui/input.h"
5#include "app/gui/style.h"
6#include "util/hex.h"
7
8namespace yaze {
9namespace editor {
10
11using ImGui::BeginChild;
12using ImGui::Button;
13using ImGui::Checkbox;
14using ImGui::EndChild;
15using ImGui::SameLine;
16using ImGui::Selectable;
17using ImGui::Text;
18
19constexpr float kInputFieldSize = 30.f;
20
22 ImVec2 canvas_p0, ImVec2 scrolling) {
23 // Get the mouse position relative to the canvas
24 const ImGuiIO &io = ImGui::GetIO();
25 const ImVec2 origin(canvas_p0.x + scrolling.x, canvas_p0.y + scrolling.y);
26 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
27
28 // Check if the mouse is hovering over the entity
29 if (mouse_pos.x >= entity.x_ && mouse_pos.x <= entity.x_ + 16 &&
30 mouse_pos.y >= entity.y_ && mouse_pos.y <= entity.y_ + 16) {
31 return true;
32 }
33 return false;
34}
35
36void MoveEntityOnGrid(zelda3::GameEntity *entity, ImVec2 canvas_p0,
37 ImVec2 scrolling, bool free_movement) {
38 // Get the mouse position relative to the canvas
39 const ImGuiIO &io = ImGui::GetIO();
40 const ImVec2 origin(canvas_p0.x + scrolling.x, canvas_p0.y + scrolling.y);
41 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
42
43 // Calculate the new position on the 16x16 grid
44 int new_x = static_cast<int>(mouse_pos.x) / 16 * 16;
45 int new_y = static_cast<int>(mouse_pos.y) / 16 * 16;
46 if (free_movement) {
47 new_x = static_cast<int>(mouse_pos.x) / 8 * 8;
48 new_y = static_cast<int>(mouse_pos.y) / 8 * 8;
49 }
50
51 // Update the entity position
52 entity->set_x(new_x);
53 entity->set_y(new_y);
54}
55
56
57
59 bool set_done = false;
60 if (set_done) {
61 set_done = false;
62 }
63 if (ImGui::BeginPopup("Entrance Inserter")) {
64 static int entrance_id = 0;
65 gui::InputHex("Entrance ID", &entrance_id);
66
67 if (Button(ICON_MD_DONE)) {
68 set_done = true;
69 ImGui::CloseCurrentPopup();
70 }
71
72 SameLine();
73 if (Button(ICON_MD_CANCEL)) {
74 ImGui::CloseCurrentPopup();
75 }
76
77 ImGui::EndPopup();
78 }
79 return set_done;
80}
81
83 static bool set_done = false;
84 if (set_done) {
85 set_done = false;
86 return true;
87 }
88
89 if (ImGui::BeginPopupModal("Entrance Editor", NULL,
90 ImGuiWindowFlags_AlwaysAutoResize)) {
91 ImGui::Text("Entrance ID: %d", entrance.entrance_id_);
92 ImGui::Separator();
93
94 gui::InputHexWord("Map ID", &entrance.map_id_);
95 gui::InputHexByte("Entrance ID", &entrance.entrance_id_,
96 kInputFieldSize + 20);
97 gui::InputHex("X Position", &entrance.x_);
98 gui::InputHex("Y Position", &entrance.y_);
99
100 ImGui::Checkbox("Is Hole", &entrance.is_hole_);
101
102 ImGui::Separator();
103
104 if (Button("Save")) {
105 set_done = true;
106 ImGui::CloseCurrentPopup();
107 }
108 ImGui::SameLine();
109 if (Button("Delete")) {
110 entrance.deleted = true;
111 set_done = true;
112 ImGui::CloseCurrentPopup();
113 }
114 ImGui::SameLine();
115 if (Button("Cancel")) {
116 ImGui::CloseCurrentPopup();
117 }
118
119 ImGui::EndPopup();
120 }
121 return set_done;
122}
123
125 if (ImGui::BeginPopup("Exit Inserter")) {
126 static int exit_id = 0;
127 static int room_id = 0;
128 static int x_pos = 0;
129 static int y_pos = 0;
130
131 ImGui::Text("Insert New Exit");
132 ImGui::Separator();
133
134 gui::InputHex("Exit ID", &exit_id);
135 gui::InputHex("Room ID", &room_id);
136 gui::InputHex("X Position", &x_pos);
137 gui::InputHex("Y Position", &y_pos);
138
139 if (Button("Create Exit")) {
140 // This would need to be connected to the overworld editor to actually create the exit
141 ImGui::CloseCurrentPopup();
142 }
143
144 SameLine();
145 if (Button("Cancel")) {
146 ImGui::CloseCurrentPopup();
147 }
148
149 ImGui::EndPopup();
150 }
151}
152
154 static bool set_done = false;
155 if (set_done) {
156 set_done = false;
157 }
158 if (ImGui::BeginPopupModal("Exit editor", NULL,
159 ImGuiWindowFlags_AlwaysAutoResize)) {
160 // Normal door: None = 0, Wooden = 1, Bombable = 2
161 static int doorType = exit.door_type_1_;
162 // Fancy door: None = 0, Sanctuary = 1, Palace = 2
163 static int fancyDoorType = exit.door_type_2_;
164
165 static int xPos = 0;
166 static int yPos = 0;
167
168 // Special overworld exit properties
169 static int centerY = 0;
170 static int centerX = 0;
171 static int unk1 = 0;
172 static int unk2 = 0;
173 static int linkPosture = 0;
174 static int spriteGFX = 0;
175 static int bgGFX = 0;
176 static int palette = 0;
177 static int sprPal = 0;
178 static int top = 0;
179 static int bottom = 0;
180 static int left = 0;
181 static int right = 0;
182 static int leftEdgeOfMap = 0;
183
184 gui::InputHexWord("Room", &exit.room_id_);
185 SameLine();
186 gui::InputHex("Entity ID", &exit.entity_id_, 4);
187 gui::InputHexWord("Map", &exit.map_id_);
188 SameLine();
189 Checkbox("Automatic", &exit.is_automatic_);
190
191 gui::InputHex("X Positon", &exit.x_);
192 SameLine();
193 gui::InputHex("Y Position", &exit.y_);
194
195 gui::InputHexByte("X Camera", &exit.x_camera_);
196 SameLine();
197 gui::InputHexByte("Y Camera", &exit.y_camera_);
198
199 gui::InputHexWord("X Scroll", &exit.x_scroll_);
200 SameLine();
201 gui::InputHexWord("Y Scroll", &exit.y_scroll_);
202
203 ImGui::Separator();
204
205 static bool show_properties = false;
206 Checkbox("Show properties", &show_properties);
207 if (show_properties) {
208 Text("Deleted? %s", exit.deleted_ ? "true" : "false");
209 Text("Hole? %s", exit.is_hole_ ? "true" : "false");
210 Text("Large Map? %s", exit.large_map_ ? "true" : "false");
211 }
212
213 gui::TextWithSeparators("Unimplemented below");
214
215 ImGui::RadioButton("None", &doorType, 0);
216 SameLine();
217 ImGui::RadioButton("Wooden", &doorType, 1);
218 SameLine();
219 ImGui::RadioButton("Bombable", &doorType, 2);
220 // If door type is not None, input positions
221 if (doorType != 0) {
222 gui::InputHex("Door X pos", &xPos);
223 gui::InputHex("Door Y pos", &yPos);
224 }
225
226 ImGui::RadioButton("None##Fancy", &fancyDoorType, 0);
227 SameLine();
228 ImGui::RadioButton("Sanctuary", &fancyDoorType, 1);
229 SameLine();
230 ImGui::RadioButton("Palace", &fancyDoorType, 2);
231 // If fancy door type is not None, input positions
232 if (fancyDoorType != 0) {
233 // Placeholder for fancy door's X position
234 gui::InputHex("Fancy Door X pos", &xPos);
235 // Placeholder for fancy door's Y position
236 gui::InputHex("Fancy Door Y pos", &yPos);
237 }
238
239 static bool special_exit = false;
240 Checkbox("Special exit", &special_exit);
241 if (special_exit) {
242 gui::InputHex("Center X", &centerX);
243
244 gui::InputHex("Center Y", &centerY);
245 gui::InputHex("Unk1", &unk1);
246 gui::InputHex("Unk2", &unk2);
247
248 gui::InputHex("Link's posture", &linkPosture);
249 gui::InputHex("Sprite GFX", &spriteGFX);
250 gui::InputHex("BG GFX", &bgGFX);
251 gui::InputHex("Palette", &palette);
252 gui::InputHex("Spr Pal", &sprPal);
253
254 gui::InputHex("Top", &top);
255 gui::InputHex("Bottom", &bottom);
256 gui::InputHex("Left", &left);
257 gui::InputHex("Right", &right);
258
259 gui::InputHex("Left edge of map", &leftEdgeOfMap);
260 }
261
262 if (Button(ICON_MD_DONE)) {
263 ImGui::CloseCurrentPopup();
264 }
265
266 SameLine();
267
268 if (Button(ICON_MD_CANCEL)) {
269 set_done = true;
270 ImGui::CloseCurrentPopup();
271 }
272
273 SameLine();
274 if (Button(ICON_MD_DELETE)) {
275 exit.deleted_ = true;
276 ImGui::CloseCurrentPopup();
277 }
278
279 ImGui::EndPopup();
280 }
281
282 return set_done;
283}
284
286 // Contents of the Context Menu
287 if (ImGui::BeginPopup("Item Inserter")) {
288 static size_t new_item_id = 0;
289 Text("Add Item");
290 BeginChild("ScrollRegion", ImVec2(150, 150), true,
291 ImGuiWindowFlags_AlwaysVerticalScrollbar);
292 for (size_t i = 0; i < zelda3::kSecretItemNames.size(); i++) {
293 if (Selectable(zelda3::kSecretItemNames[i].c_str(), i == new_item_id)) {
294 new_item_id = i;
295 }
296 }
297 EndChild();
298
299 if (Button(ICON_MD_DONE)) {
300 // Add the new item to the overworld
301 new_item_id = 0;
302 ImGui::CloseCurrentPopup();
303 }
304 SameLine();
305
306 if (Button(ICON_MD_CANCEL)) {
307 ImGui::CloseCurrentPopup();
308 }
309
310 ImGui::EndPopup();
311 }
312}
313
314// TODO: Implement deleting OverworldItem objects, currently only hides them
316 static bool set_done = false;
317 if (set_done) {
318 set_done = false;
319 }
320 if (ImGui::BeginPopupModal("Item editor", NULL,
321 ImGuiWindowFlags_AlwaysAutoResize)) {
322 BeginChild("ScrollRegion", ImVec2(150, 150), true,
323 ImGuiWindowFlags_AlwaysVerticalScrollbar);
324 ImGui::BeginGroup();
325 for (size_t i = 0; i < zelda3::kSecretItemNames.size(); i++) {
326 if (Selectable(zelda3::kSecretItemNames[i].c_str(), item.id_ == i)) {
327 item.id_ = i;
328 }
329 }
330 ImGui::EndGroup();
331 EndChild();
332
333 if (Button(ICON_MD_DONE)) ImGui::CloseCurrentPopup();
334 SameLine();
335 if (Button(ICON_MD_CLOSE)) {
336 set_done = true;
337 ImGui::CloseCurrentPopup();
338 }
339 SameLine();
340 if (Button(ICON_MD_DELETE)) {
341 item.deleted = true;
342 ImGui::CloseCurrentPopup();
343 }
344
345 ImGui::EndPopup();
346 }
347 return set_done;
348}
349
350const ImGuiTableSortSpecs *SpriteItem::s_current_sort_specs = nullptr;
351
352void DrawSpriteTable(std::function<void(int)> onSpriteSelect) {
353 static ImGuiTextFilter filter;
354 static int selected_id = 0;
355 static std::vector<SpriteItem> items;
356
357 // Initialize items if empty
358 if (items.empty()) {
359 for (int i = 0; i < 256; ++i) {
360 items.push_back(SpriteItem{i, zelda3::kSpriteDefaultNames[i].data()});
361 }
362 }
363
364 filter.Draw("Filter", 180);
365
366 if (ImGui::BeginTable("##sprites", 2,
367 ImGuiTableFlags_Sortable | ImGuiTableFlags_Resizable)) {
368 ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_DefaultSort, 0.0f,
370 ImGui::TableSetupColumn("Name", 0, 0.0f, SpriteItemColumnID_Name);
371 ImGui::TableHeadersRow();
372
373 // Handle sorting
374 if (ImGuiTableSortSpecs *sort_specs = ImGui::TableGetSortSpecs()) {
375 if (sort_specs->SpecsDirty) {
376 SpriteItem::SortWithSortSpecs(sort_specs, items);
377 sort_specs->SpecsDirty = false;
378 }
379 }
380
381 // Display filtered and sorted items
382 for (const auto &item : items) {
383 if (filter.PassFilter(item.name)) {
384 ImGui::TableNextRow();
385 ImGui::TableSetColumnIndex(0);
386 Text("%d", item.id);
387 ImGui::TableSetColumnIndex(1);
388
389 if (Selectable(item.name, selected_id == item.id,
390 ImGuiSelectableFlags_SpanAllColumns)) {
391 selected_id = item.id;
392 onSpriteSelect(item.id);
393 }
394 }
395 }
396 ImGui::EndTable();
397 }
398}
399
401 if (ImGui::BeginPopup("Sprite Inserter")) {
402 static int new_sprite_id = 0;
403 static int x_pos = 0;
404 static int y_pos = 0;
405
406 ImGui::Text("Add New Sprite");
407 ImGui::Separator();
408
409 BeginChild("ScrollRegion", ImVec2(250, 200), true,
410 ImGuiWindowFlags_AlwaysVerticalScrollbar);
411 DrawSpriteTable([](int selected_id) { new_sprite_id = selected_id; });
412 EndChild();
413
414 ImGui::Separator();
415 ImGui::Text("Position:");
416 gui::InputHex("X Position", &x_pos);
417 gui::InputHex("Y Position", &y_pos);
418
419 if (Button("Add Sprite")) {
420 // This would need to be connected to the overworld editor to actually create the sprite
421 new_sprite_id = 0;
422 x_pos = 0;
423 y_pos = 0;
424 ImGui::CloseCurrentPopup();
425 }
426 SameLine();
427
428 if (Button("Cancel")) {
429 ImGui::CloseCurrentPopup();
430 }
431
432 ImGui::EndPopup();
433 }
434}
435
437 static bool set_done = false;
438 if (set_done) {
439 set_done = false;
440 }
441 if (ImGui::BeginPopupModal("Sprite editor", NULL,
442 ImGuiWindowFlags_AlwaysAutoResize)) {
443 BeginChild("ScrollRegion", ImVec2(350, 350), true,
444 ImGuiWindowFlags_AlwaysVerticalScrollbar);
445 ImGui::BeginGroup();
446 Text("%s", sprite.name().c_str());
447
448 DrawSpriteTable([&sprite](int selected_id) {
449 sprite.set_id(selected_id);
450 sprite.UpdateMapProperties(sprite.map_id());
451 });
452 ImGui::EndGroup();
453 EndChild();
454
455 if (Button(ICON_MD_DONE)) ImGui::CloseCurrentPopup();
456 SameLine();
457 if (Button(ICON_MD_CLOSE)) {
458 set_done = true;
459 ImGui::CloseCurrentPopup();
460 }
461 SameLine();
462 if (Button(ICON_MD_DELETE)) {
463 sprite.set_deleted(true);
464 ImGui::CloseCurrentPopup();
465 }
466
467 ImGui::EndPopup();
468 }
469 return set_done;
470}
471
472} // namespace editor
473} // namespace yaze
Base class for all overworld and dungeon entities.
Definition common.h:19
auto set_x(int x)
Definition common.h:39
auto set_y(int y)
Definition common.h:40
A class for managing sprites in the overworld and underworld.
Definition sprite.h:279
auto map_id() const
Definition sprite.h:344
auto set_id(uint8_t id)
Definition sprite.h:339
void UpdateMapProperties(uint16_t map_id) override
Definition sprite.cc:9
auto set_deleted(bool deleted)
Definition sprite.h:356
#define ICON_MD_CANCEL
Definition icons.h:362
#define ICON_MD_DONE
Definition icons.h:605
#define ICON_MD_DELETE
Definition icons.h:528
#define ICON_MD_CLOSE
Definition icons.h:416
void DrawExitInserterPopup()
Definition entity.cc:124
void MoveEntityOnGrid(zelda3::GameEntity *entity, ImVec2 canvas_p0, ImVec2 scrolling, bool free_movement)
Definition entity.cc:36
bool DrawSpriteEditorPopup(zelda3::Sprite &sprite)
Definition entity.cc:436
void DrawItemInsertPopup()
Definition entity.cc:285
bool DrawEntranceInserterPopup()
Definition entity.cc:58
void DrawSpriteInserterPopup()
Definition entity.cc:400
void DrawSpriteTable(std::function< void(int)> onSpriteSelect)
Definition entity.cc:352
bool DrawOverworldEntrancePopup(zelda3::OverworldEntrance &entrance)
Definition entity.cc:82
bool IsMouseHoveringOverEntity(const zelda3::GameEntity &entity, ImVec2 canvas_p0, ImVec2 scrolling)
Definition entity.cc:21
bool DrawItemEditorPopup(zelda3::OverworldItem &item)
Definition entity.cc:315
constexpr float kInputFieldSize
Definition entity.cc:19
@ SpriteItemColumnID_ID
Definition entity.h:37
@ SpriteItemColumnID_Name
Definition entity.h:38
bool DrawExitEditorPopup(zelda3::OverworldExit &exit)
Definition entity.cc:153
bool InputHexWord(const char *label, uint16_t *data, float input_width, bool no_step)
Definition input.cc:175
bool InputHex(const char *label, uint64_t *data)
Definition input.cc:156
bool InputHexByte(const char *label, uint8_t *data, float input_width, bool no_step)
Definition input.cc:189
void TextWithSeparators(const absl::string_view &text)
Definition style.cc:1280
const std::vector< std::string > kSecretItemNames
Main namespace for the application.
static const ImGuiTableSortSpecs * s_current_sort_specs
Definition entity.h:45
static void SortWithSortSpecs(ImGuiTableSortSpecs *sort_specs, std::vector< SpriteItem > &items)
Definition entity.h:47