yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
dungeon_object_selector.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <iterator>
5#include <cstring>
6
7#include "app/core/window.h"
8#include "app/gfx/arena.h"
10#include "app/gui/canvas.h"
12#include "app/rom.h"
16#include "imgui/imgui.h"
17
18namespace yaze::editor {
19
20using ImGui::BeginChild;
21using ImGui::EndChild;
22using ImGui::EndTabBar;
23using ImGui::EndTabItem;
24using ImGui::Separator;
25
27 if (ImGui::BeginTabBar("##TabBar", ImGuiTabBarFlags_FittingPolicyScroll)) {
28 if (ImGui::BeginTabItem("Room Graphics")) {
29 if (ImGuiID child_id = ImGui::GetID((void *)(intptr_t)3);
30 BeginChild(child_id, ImGui::GetContentRegionAvail(), true,
31 ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
33 }
34 EndChild();
35 EndTabItem();
36 }
37
38 if (ImGui::BeginTabItem("Object Renderer")) {
40 EndTabItem();
41 }
42 EndTabBar();
43 }
44}
45
47 // Use AssetBrowser for better object selection
48 if (ImGui::BeginTable("DungeonObjectEditorTable", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV, ImVec2(0, 0))) {
49 ImGui::TableSetupColumn("Object Browser", ImGuiTableColumnFlags_WidthFixed, 400);
50 ImGui::TableSetupColumn("Preview Canvas", ImGuiTableColumnFlags_WidthStretch);
51 ImGui::TableHeadersRow();
52
53 // Left column: AssetBrowser for object selection
54 ImGui::TableNextColumn();
55 ImGui::BeginChild("AssetBrowser", ImVec2(0, 0), true, ImGuiWindowFlags_AlwaysVerticalScrollbar);
56
58
59 ImGui::EndChild();
60
61 // Right column: Preview and placement controls
62 ImGui::TableNextColumn();
63 ImGui::BeginChild("PreviewCanvas", ImVec2(0, 0), true);
64
65 // Object placement controls
66 ImGui::SeparatorText("Object Placement");
67 static int place_x = 0, place_y = 0;
68 ImGui::InputInt("X Position", &place_x);
69 ImGui::InputInt("Y Position", &place_y);
70
71 if (ImGui::Button("Place Object") && object_loaded_) {
72 PlaceObjectAtPosition(place_x, place_y);
73 }
74
75 ImGui::Separator();
76
77 // Preview canvas
78 object_canvas_.DrawBackground(ImVec2(256 + 1, 0x10 * 0x40 + 1));
81
82 // Render selected object preview with primitive fallback
84 int preview_x = 128 - 16; // Center horizontally
85 int preview_y = 128 - 16; // Center vertically
86
87 // TODO: Implement preview using ObjectDrawer + small BackgroundBuffer
88 // For now, use primitive shape rendering (shows object ID and rough dimensions)
89 RenderObjectPrimitive(preview_object_, preview_x, preview_y);
90 }
91
93 ImGui::EndChild();
94 ImGui::EndTable();
95 }
96
97 // Object details window
98 if (object_loaded_) {
99 ImGui::Begin("Object Details", &object_loaded_, 0);
100 ImGui::Text("Object ID: 0x%02X", preview_object_.id_);
101 ImGui::Text("Position: (%d, %d)", preview_object_.x_, preview_object_.y_);
102 ImGui::Text("Size: 0x%02X", preview_object_.size_);
103 ImGui::Text("Layer: %d", static_cast<int>(preview_object_.layer_));
104
105 // Add object placement controls
106 ImGui::Separator();
107 ImGui::Text("Placement Controls:");
108 static int place_x = 0, place_y = 0;
109 ImGui::InputInt("X Position", &place_x);
110 ImGui::InputInt("Y Position", &place_y);
111
112 if (ImGui::Button("Place Object")) {
113 // TODO: Implement object placement in the main canvas
114 ImGui::Text("Object placed at (%d, %d)", place_x, place_y);
115 }
116
117 ImGui::End();
118 }
119}
120
122 static int selected_object_type = 0;
123 static int selected_object_id = 0;
124
125 // Object type selector
126 const char* object_types[] = {"Type 1 (0x00-0xFF)", "Type 2 (0x100-0x1FF)", "Type 3 (0x200+)"};
127 if (ImGui::Combo("Object Type", &selected_object_type, object_types, 3)) {
128 selected_object_id = 0; // Reset selection when changing type
129 }
130
131 ImGui::Separator();
132
133 // Object list with previews - optimized for 300px column width
134 const int preview_size = 48; // Larger 48x48 pixel preview for better visibility
135 const int items_per_row = 5; // 5 items per row to fit in 300px column
136
137 if (rom_ && rom_->is_loaded()) {
138 auto palette = rom_->palette_group().dungeon_main[current_palette_group_id_];
139
140 // Determine object range based on type
141 int start_id, end_id;
142 switch (selected_object_type) {
143 case 0: start_id = 0x00; end_id = 0xFF; break;
144 case 1: start_id = 0x100; end_id = 0x1FF; break;
145 case 2: start_id = 0x200; end_id = 0x2FF; break;
146 default: start_id = 0x00; end_id = 0xFF; break;
147 }
148
149 // Create a grid layout for object previews
150 int current_row = 0;
151 int current_col = 0;
152
153 for (int obj_id = start_id; obj_id <= end_id && obj_id <= start_id + 63; ++obj_id) { // Limit to 64 objects for performance
154 // Create object for preview
155 auto test_object = zelda3::RoomObject(obj_id, 0, 0, 0x12, 0);
156 test_object.set_rom(rom_);
157 test_object.EnsureTilesLoaded();
158
159 // Calculate position in grid - better sizing for 300px column
160 float available_width = ImGui::GetContentRegionAvail().x;
161 float spacing = ImGui::GetStyle().ItemSpacing.x;
162 float item_width = (available_width - (items_per_row - 1) * spacing) / items_per_row;
163 float item_height = preview_size + 30; // Preview + text (reduced padding)
164
165 ImGui::PushID(obj_id);
166
167 // Create a selectable button with preview
168 bool is_selected = (selected_object_id == obj_id);
169 if (ImGui::Selectable("", is_selected, ImGuiSelectableFlags_None, ImVec2(item_width, item_height))) {
170 selected_object_id = obj_id;
171
172 // Update preview object
173 preview_object_ = test_object;
174 preview_palette_ = palette;
175 object_loaded_ = true;
176
177 // Notify the main editor that an object was selected
180 }
181 }
182
183 // Draw preview image
184 ImVec2 cursor_pos = ImGui::GetCursorScreenPos();
185 ImVec2 preview_pos = ImVec2(cursor_pos.x + (item_width - preview_size) / 2,
186 cursor_pos.y - item_height + 5);
187
188 // Draw simplified primitive preview for object selector
189 ImGui::SetCursorScreenPos(preview_pos);
190
191 // Draw object as colored rectangle with ID
192 ImU32 object_color = GetObjectTypeColor(obj_id);
193 ImGui::GetWindowDrawList()->AddRectFilled(
194 preview_pos,
195 ImVec2(preview_pos.x + preview_size, preview_pos.y + preview_size),
196 object_color);
197
198 // Draw border
199 ImGui::GetWindowDrawList()->AddRect(
200 preview_pos,
201 ImVec2(preview_pos.x + preview_size, preview_pos.y + preview_size),
202 IM_COL32(0, 0, 0, 255), 0.0f, 0, 2.0f);
203
204 // Draw object type symbol in center
205 std::string symbol = GetObjectTypeSymbol(obj_id);
206 ImVec2 text_size = ImGui::CalcTextSize(symbol.c_str());
207 ImVec2 text_pos = ImVec2(
208 preview_pos.x + (preview_size - text_size.x) / 2,
209 preview_pos.y + (preview_size - text_size.y) / 2);
210
211 ImGui::GetWindowDrawList()->AddText(
212 text_pos, IM_COL32(255, 255, 255, 255), symbol.c_str());
213
214 // Draw object ID below preview
215 ImGui::SetCursorScreenPos(ImVec2(preview_pos.x, preview_pos.y + preview_size + 2));
216 ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(255, 255, 255, 255));
217 ImGui::Text("0x%02X", obj_id);
218 ImGui::PopStyleColor();
219
220 // Try to get object name
221 std::string object_name = "Unknown";
222 if (obj_id < 0x100) { // Type1RoomObjectNames has 248 elements (0-247, 0x00-0xF7)
223 if (obj_id < std::size(zelda3::Type1RoomObjectNames)) {
224 const char* name_ptr = zelda3::Type1RoomObjectNames[obj_id];
225 if (name_ptr != nullptr) {
226 object_name = std::string(name_ptr);
227 }
228 }
229 } else if (obj_id < 0x140) { // Type2RoomObjectNames has 64 elements (0x100-0x13F)
230 int type2_index = obj_id - 0x100;
231 if (type2_index >= 0 && type2_index < std::size(zelda3::Type2RoomObjectNames)) {
232 const char* name_ptr = zelda3::Type2RoomObjectNames[type2_index];
233 if (name_ptr != nullptr) {
234 object_name = std::string(name_ptr);
235 }
236 }
237 } else if (obj_id < 0x1C0) { // Type3RoomObjectNames has 128 elements (0x140-0x1BF)
238 int type3_index = obj_id - 0x140;
239 if (type3_index >= 0 && type3_index < std::size(zelda3::Type3RoomObjectNames)) {
240 const char* name_ptr = zelda3::Type3RoomObjectNames[type3_index];
241 if (name_ptr != nullptr) {
242 object_name = std::string(name_ptr);
243 }
244 }
245 }
246
247 // Draw object name with better sizing
248 ImGui::SetCursorScreenPos(ImVec2(cursor_pos.x + 2, cursor_pos.y - 8));
249 ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(200, 200, 200, 255));
250 // Truncate long names to fit
251 if (object_name.length() > 8) {
252 object_name = object_name.substr(0, 8) + "...";
253 }
254 ImGui::Text("%s", object_name.c_str());
255 ImGui::PopStyleColor();
256
257 ImGui::PopID();
258
259 // Move to next position
260 current_col++;
261 if (current_col >= items_per_row) {
262 current_col = 0;
263 current_row++;
264 ImGui::NewLine();
265 } else {
266 ImGui::SameLine();
267 }
268 }
269 } else {
270 ImGui::Text("ROM not loaded");
271 }
272
273 ImGui::Separator();
274
275 // Selected object info
276 if (object_loaded_) {
277 ImGui::Text("Selected: 0x%03X", selected_object_id);
278 ImGui::Text("Layer: %d", static_cast<int>(preview_object_.layer_));
279 ImGui::Text("Size: 0x%02X", preview_object_.size_);
280 }
281}
282
284 if (ImGui::BeginTabBar("##ObjectSelectorTabBar")) {
285 // Object Selector tab - for placing objects with new AssetBrowser
286 if (ImGui::BeginTabItem("Object Selector")) {
288 ImGui::EndTabItem();
289 }
290
291 // Room Graphics tab - 8 bitmaps viewer
292 if (ImGui::BeginTabItem("Room Graphics")) {
294 ImGui::EndTabItem();
295 }
296
297 // Object Editor tab - experimental editor
298 if (ImGui::BeginTabItem("Object Editor")) {
300 ImGui::EndTabItem();
301 }
302
303 ImGui::EndTabBar();
304 }
305}
306
308 const auto height = 0x40;
312
313 if (rom_ && rom_->is_loaded() && rooms_) {
314 int active_room_id = current_room_id_;
315 auto& room = (*rooms_)[active_room_id];
316 auto blocks = room.blocks();
317
318 // Load graphics for this room if not already loaded
319 if (blocks.empty()) {
320 room.LoadRoomGraphics(room.blockset);
321 blocks = room.blocks();
322 }
323
324 int current_block = 0;
325 const int max_blocks_per_row = 2; // 2 blocks per row for 300px column
326 const int block_width = 128; // Reduced size to fit column
327 const int block_height = 32; // Reduced height
328
329 for (int block : blocks) {
330 if (current_block >= 16) break; // Only show first 16 blocks
331
332 // Ensure the graphics sheet is loaded and has a valid texture
333 if (block < gfx::Arena::Get().gfx_sheets().size()) {
334 auto& gfx_sheet = gfx::Arena::Get().gfx_sheets()[block];
335
336 // Calculate position in a grid layout instead of horizontal concatenation
337 int row = current_block / max_blocks_per_row;
338 int col = current_block % max_blocks_per_row;
339
340 int x = room_gfx_canvas_.zero_point().x + 2 + (col * block_width);
341 int y = room_gfx_canvas_.zero_point().y + 2 + (row * block_height);
342
343 // Ensure we don't exceed canvas bounds
344 if (x + block_width <= room_gfx_canvas_.zero_point().x + room_gfx_canvas_.width() &&
345 y + block_height <= room_gfx_canvas_.zero_point().y + room_gfx_canvas_.height()) {
346
347 // Only draw if the texture is valid
348 if (gfx_sheet.texture() != 0) {
349 room_gfx_canvas_.draw_list()->AddImage(
350 (ImTextureID)(intptr_t)gfx_sheet.texture(),
351 ImVec2(x, y),
352 ImVec2(x + block_width, y + block_height));
353 }
354 }
355 }
356 current_block += 1;
357 }
358 }
361}
362
365 ImGui::Text("Editor systems not initialized");
366 return;
367 }
368
369 // Create a tabbed interface for different editing modes
370 if (ImGui::BeginTabBar("##EditingPanels")) {
371 // Object Editor Tab
372 if (ImGui::BeginTabItem("Objects")) {
374 ImGui::EndTabItem();
375 }
376
377 // Sprite Editor Tab
378 if (ImGui::BeginTabItem("Sprites")) {
380 ImGui::EndTabItem();
381 }
382
383 // Item Editor Tab
384 if (ImGui::BeginTabItem("Items")) {
386 ImGui::EndTabItem();
387 }
388
389 // Entrance Editor Tab
390 if (ImGui::BeginTabItem("Entrances")) {
392 ImGui::EndTabItem();
393 }
394
395 // Door Editor Tab
396 if (ImGui::BeginTabItem("Doors")) {
398 ImGui::EndTabItem();
399 }
400
401 // Chest Editor Tab
402 if (ImGui::BeginTabItem("Chests")) {
404 ImGui::EndTabItem();
405 }
406
407 // Properties Tab
408 if (ImGui::BeginTabItem("Properties")) {
410 ImGui::EndTabItem();
411 }
412
413 ImGui::EndTabBar();
414 }
415}
416
418 if (!object_editor_) {
419 ImGui::Text("Object editor not initialized");
420 return;
421 }
422
423 auto& editor = *object_editor_;
424
425 ImGui::Text("Object Editor");
426 Separator();
427
428 // Display current editing mode
429 auto mode = editor.GetMode();
430 const char *mode_names[] = {"Select", "Insert", "Delete", "Edit", "Layer", "Preview"};
431 ImGui::Text("Mode: %s", mode_names[static_cast<int>(mode)]);
432
433 // Compact mode selection
434 if (ImGui::Button("Select"))
436 ImGui::SameLine();
437 if (ImGui::Button("Insert"))
439 ImGui::SameLine();
440 if (ImGui::Button("Edit"))
442
443 // Layer and object type selection
444 int current_layer = editor.GetCurrentLayer();
445 if (ImGui::SliderInt("Layer", &current_layer, 0, 2)) {
446 editor.SetCurrentLayer(current_layer);
447 }
448
449 int current_object_type = editor.GetCurrentObjectType();
450 if (ImGui::InputInt("Object Type", &current_object_type, 1, 16)) {
451 if (current_object_type >= 0 && current_object_type <= 0x3FF) {
452 editor.SetCurrentObjectType(current_object_type);
453 }
454 }
455
456 // Quick configuration checkboxes
457 auto config = editor.GetConfig();
458 if (ImGui::Checkbox("Snap to Grid", &config.snap_to_grid)) {
459 editor.SetConfig(config);
460 }
461 ImGui::SameLine();
462 if (ImGui::Checkbox("Show Grid", &config.show_grid)) {
463 editor.SetConfig(config);
464 }
465
466 // Object count and selection info
467 Separator();
468 ImGui::Text("Objects: %zu", editor.GetObjectCount());
469
470 auto selection = editor.GetSelection();
471 if (!selection.selected_objects.empty()) {
472 ImGui::Text("Selected: %zu", selection.selected_objects.size());
473 }
474
475 // Undo/Redo buttons
476 Separator();
477 if (ImGui::Button("Undo") && editor.CanUndo()) {
478 (void)editor.Undo();
479 }
480 ImGui::SameLine();
481 if (ImGui::Button("Redo") && editor.CanRedo()) {
482 (void)editor.Redo();
483 }
484}
485
487 // Color-code objects based on their type and function
488 if (object_id >= 0x10 && object_id <= 0x1F) {
489 return IM_COL32(128, 128, 128, 255); // Gray for walls
490 } else if (object_id >= 0x20 && object_id <= 0x2F) {
491 return IM_COL32(139, 69, 19, 255); // Brown for floors
492 } else if (object_id == 0xF9 || object_id == 0xFA) {
493 return IM_COL32(255, 215, 0, 255); // Gold for chests
494 } else if (object_id >= 0x17 && object_id <= 0x1E) {
495 return IM_COL32(139, 69, 19, 255); // Brown for doors
496 } else if (object_id == 0x2F || object_id == 0x2B) {
497 return IM_COL32(160, 82, 45, 255); // Saddle brown for pots
498 } else if (object_id >= 0x138 && object_id <= 0x13B) {
499 return IM_COL32(255, 255, 0, 255); // Yellow for stairs
500 } else if (object_id >= 0x30 && object_id <= 0x3F) {
501 return IM_COL32(105, 105, 105, 255); // Dim gray for decorations
502 } else {
503 return IM_COL32(96, 96, 96, 255); // Default gray
504 }
505}
506
508 // Return symbol representing object type
509 if (object_id >= 0x10 && object_id <= 0x1F) {
510 return "■"; // Wall
511 } else if (object_id >= 0x20 && object_id <= 0x2F) {
512 return "□"; // Floor
513 } else if (object_id == 0xF9 || object_id == 0xFA) {
514 return "⬛"; // Chest
515 } else if (object_id >= 0x17 && object_id <= 0x1E) {
516 return "◊"; // Door
517 } else if (object_id == 0x2F || object_id == 0x2B) {
518 return "●"; // Pot
519 } else if (object_id >= 0x138 && object_id <= 0x13B) {
520 return "▲"; // Stairs
521 } else if (object_id >= 0x30 && object_id <= 0x3F) {
522 return "◆"; // Decoration
523 } else {
524 return "?"; // Unknown
525 }
526}
527
529 // Render object as primitive shape on canvas
530 ImU32 color = GetObjectTypeColor(object.id_);
531
532 // Calculate object size with proper wall length handling
533 int obj_width, obj_height;
534 CalculateObjectDimensions(object, obj_width, obj_height);
535
536 // Draw object rectangle
537 ImVec4 color_vec = ImGui::ColorConvertU32ToFloat4(color);
538 object_canvas_.DrawRect(x, y, obj_width, obj_height, color_vec);
539 object_canvas_.DrawRect(x, y, obj_width, obj_height, ImVec4(0.0f, 0.0f, 0.0f, 1.0f));
540
541 // Draw object ID as text
542 std::string obj_text = absl::StrFormat("0x%X", object.id_);
543 object_canvas_.DrawText(obj_text, x + obj_width + 2, y + 4);
544}
545
547 ImGui::SeparatorText("Dungeon Objects");
548
549 // Debug info
550 ImGui::Text("Asset Browser Debug: Available width: %.1f", ImGui::GetContentRegionAvail().x);
551
552 // Object type filter
553 static int object_type_filter = 0;
554 const char* object_types[] = {"All", "Walls", "Floors", "Chests", "Doors", "Decorations", "Stairs"};
555 if (ImGui::Combo("Object Type", &object_type_filter, object_types, 7)) {
556 // Filter will be applied in the loop below
557 }
558
559 ImGui::Separator();
560
561 // Create asset browser-style grid
562 const float item_size = 64.0f;
563 const float item_spacing = 8.0f;
564 const int columns = std::max(1, static_cast<int>((ImGui::GetContentRegionAvail().x - item_spacing) / (item_size + item_spacing)));
565
566 ImGui::Text("Columns: %d, Item size: %.1f", columns, item_size);
567
568 int current_column = 0;
569 int items_drawn = 0;
570
571 // Draw object grid based on filter
572 for (int obj_id = 0; obj_id <= 0xFF && items_drawn < 100; ++obj_id) {
573 // Apply object type filter
574 if (object_type_filter > 0 && !MatchesObjectFilter(obj_id, object_type_filter)) {
575 continue;
576 }
577
578 if (current_column > 0) {
579 ImGui::SameLine();
580 }
581
582 ImGui::PushID(obj_id);
583
584 // Create selectable button for object
585 bool is_selected = (selected_object_id_ == obj_id);
586 ImVec2 button_size(item_size, item_size);
587
588 if (ImGui::Selectable("", is_selected, ImGuiSelectableFlags_None, button_size)) {
589 selected_object_id_ = obj_id;
590
591 // Create and update preview object
592 preview_object_ = zelda3::RoomObject(obj_id, 0, 0, 0x12, 0);
594 if (rom_) {
595 auto palette = rom_->palette_group().dungeon_main[current_palette_group_id_];
596 preview_palette_ = palette;
597 }
598 object_loaded_ = true;
599
600 // Notify callback
603 }
604 }
605
606 // Draw object preview on the button
607 ImVec2 button_pos = ImGui::GetItemRectMin();
608 ImDrawList* draw_list = ImGui::GetWindowDrawList();
609
610 // Draw object as colored rectangle with symbol
611 ImU32 obj_color = GetObjectTypeColor(obj_id);
612 draw_list->AddRectFilled(button_pos,
613 ImVec2(button_pos.x + item_size, button_pos.y + item_size),
614 obj_color);
615
616 // Draw border
617 ImU32 border_color = is_selected ? IM_COL32(255, 255, 0, 255) : IM_COL32(0, 0, 0, 255);
618 draw_list->AddRect(button_pos,
619 ImVec2(button_pos.x + item_size, button_pos.y + item_size),
620 border_color, 0.0f, 0, is_selected ? 3.0f : 1.0f);
621
622 // Draw object symbol
623 std::string symbol = GetObjectTypeSymbol(obj_id);
624 ImVec2 text_size = ImGui::CalcTextSize(symbol.c_str());
625 ImVec2 text_pos = ImVec2(
626 button_pos.x + (item_size - text_size.x) / 2,
627 button_pos.y + (item_size - text_size.y) / 2);
628 draw_list->AddText(text_pos, IM_COL32(255, 255, 255, 255), symbol.c_str());
629
630 // Draw object ID at bottom
631 std::string id_text = absl::StrFormat("%02X", obj_id);
632 ImVec2 id_size = ImGui::CalcTextSize(id_text.c_str());
633 ImVec2 id_pos = ImVec2(
634 button_pos.x + (item_size - id_size.x) / 2,
635 button_pos.y + item_size - id_size.y - 2);
636 draw_list->AddText(id_pos, IM_COL32(255, 255, 255, 255), id_text.c_str());
637
638 ImGui::PopID();
639
640 current_column = (current_column + 1) % columns;
641 if (current_column == 0) {
642 // Force new line
643 }
644
645 items_drawn++;
646 }
647
648 ImGui::Separator();
649 ImGui::Text("Items drawn: %d", items_drawn);
650}
651
652bool DungeonObjectSelector::MatchesObjectFilter(int obj_id, int filter_type) {
653 switch (filter_type) {
654 case 1: // Walls
655 return obj_id >= 0x10 && obj_id <= 0x1F;
656 case 2: // Floors
657 return obj_id >= 0x20 && obj_id <= 0x2F;
658 case 3: // Chests
659 return obj_id == 0xF9 || obj_id == 0xFA;
660 case 4: // Doors
661 return obj_id >= 0x17 && obj_id <= 0x1E;
662 case 5: // Decorations
663 return obj_id >= 0x30 && obj_id <= 0x3F;
664 case 6: // Stairs
665 return obj_id >= 0x138 && obj_id <= 0x13B;
666 default: // All
667 return true;
668 }
669}
670
671void DungeonObjectSelector::CalculateObjectDimensions(const zelda3::RoomObject& object, int& width, int& height) {
672 // Default base size
673 width = 16;
674 height = 16;
675
676 // For walls, use the size field to determine length
677 if (object.id_ >= 0x10 && object.id_ <= 0x1F) {
678 // Wall objects: size determines length and orientation
679 uint8_t size_x = object.size_ & 0x0F;
680 uint8_t size_y = (object.size_ >> 4) & 0x0F;
681
682 // Walls can be horizontal or vertical based on size parameters
683 if (size_x > size_y) {
684 // Horizontal wall
685 width = 16 + size_x * 16; // Each unit adds 16 pixels
686 height = 16;
687 } else if (size_y > size_x) {
688 // Vertical wall
689 width = 16;
690 height = 16 + size_y * 16;
691 } else {
692 // Square wall or corner
693 width = 16 + size_x * 8;
694 height = 16 + size_y * 8;
695 }
696 } else {
697 // For other objects, use standard size calculation
698 width = 16 + (object.size_ & 0x0F) * 8;
699 height = 16 + ((object.size_ >> 4) & 0x0F) * 8;
700 }
701
702 // Clamp to reasonable limits
703 width = std::min(width, 256);
704 height = std::min(height, 256);
705}
706
709 return;
710 }
711
712 // Create object with specified position
713 auto placed_object = preview_object_;
714 placed_object.set_x(static_cast<uint8_t>(x));
715 placed_object.set_y(static_cast<uint8_t>(y));
716
717 // Call placement callback
718 object_placement_callback_(placed_object);
719}
720
723 ImGui::Text("Dungeon editor system not initialized");
724 return;
725 }
726
727 auto& system = **dungeon_editor_system_;
728
729 ImGui::Text("Sprite Editor");
730 Separator();
731
732 // Display current room sprites
733 auto current_room = system.GetCurrentRoom();
734 auto sprites_result = system.GetSpritesByRoom(current_room);
735
736 if (sprites_result.ok()) {
737 auto sprites = sprites_result.value();
738 ImGui::Text("Sprites in room %d: %zu", current_room, sprites.size());
739
740 // Show first few sprites in compact format
741 int display_count = std::min(3, static_cast<int>(sprites.size()));
742 for (int i = 0; i < display_count; ++i) {
743 const auto &sprite = sprites[i];
744 ImGui::Text("ID:%d Type:%d (%d,%d)", sprite.sprite_id,
745 static_cast<int>(sprite.type), sprite.x, sprite.y);
746 }
747 if (sprites.size() > 3) {
748 ImGui::Text("... and %zu more", sprites.size() - 3);
749 }
750 } else {
751 ImGui::Text("Error loading sprites");
752 }
753
754 // Quick sprite placement
755 Separator();
756 ImGui::Text("Quick Add Sprite");
757
758 static int new_sprite_id = 0;
759 static int new_sprite_x = 0;
760 static int new_sprite_y = 0;
761
762 ImGui::InputInt("ID", &new_sprite_id);
763 ImGui::InputInt("X", &new_sprite_x);
764 ImGui::InputInt("Y", &new_sprite_y);
765
766 if (ImGui::Button("Add Sprite")) {
768 sprite_data.sprite_id = new_sprite_id;
770 sprite_data.x = new_sprite_x;
771 sprite_data.y = new_sprite_y;
772 sprite_data.layer = 0;
773
774 auto status = system.AddSprite(sprite_data);
775 if (!status.ok()) {
776 ImGui::Text("Error adding sprite");
777 }
778 }
779}
780
783 ImGui::Text("Dungeon editor system not initialized");
784 return;
785 }
786
787 auto& system = **dungeon_editor_system_;
788
789 ImGui::Text("Item Editor");
790 Separator();
791
792 // Display current room items
793 auto current_room = system.GetCurrentRoom();
794 auto items_result = system.GetItemsByRoom(current_room);
795
796 if (items_result.ok()) {
797 auto items = items_result.value();
798 ImGui::Text("Items in room %d: %zu", current_room, items.size());
799
800 // Show first few items in compact format
801 int display_count = std::min(3, static_cast<int>(items.size()));
802 for (int i = 0; i < display_count; ++i) {
803 const auto &item = items[i];
804 ImGui::Text("ID:%d Type:%d (%d,%d)", item.item_id,
805 static_cast<int>(item.type), item.x, item.y);
806 }
807 if (items.size() > 3) {
808 ImGui::Text("... and %zu more", items.size() - 3);
809 }
810 } else {
811 ImGui::Text("Error loading items");
812 }
813
814 // Quick item placement
815 Separator();
816 ImGui::Text("Quick Add Item");
817
818 static int new_item_id = 0;
819 static int new_item_x = 0;
820 static int new_item_y = 0;
821
822 ImGui::InputInt("ID", &new_item_id);
823 ImGui::InputInt("X", &new_item_x);
824 ImGui::InputInt("Y", &new_item_y);
825
826 if (ImGui::Button("Add Item")) {
828 item_data.item_id = new_item_id;
830 item_data.x = new_item_x;
831 item_data.y = new_item_y;
832 item_data.room_id = current_room;
833 item_data.is_hidden = false;
834
835 auto status = system.AddItem(item_data);
836 if (!status.ok()) {
837 ImGui::Text("Error adding item");
838 }
839 }
840}
841
844 ImGui::Text("Dungeon editor system not initialized");
845 return;
846 }
847
848 auto& system = **dungeon_editor_system_;
849
850 ImGui::Text("Entrance Editor");
851 Separator();
852
853 // Display current room entrances
854 auto current_room = system.GetCurrentRoom();
855 auto entrances_result = system.GetEntrancesByRoom(current_room);
856
857 if (entrances_result.ok()) {
858 auto entrances = entrances_result.value();
859 ImGui::Text("Entrances: %zu", entrances.size());
860
861 for (const auto &entrance : entrances) {
862 ImGui::Text("ID:%d -> Room:%d (%d,%d)", entrance.entrance_id,
863 entrance.target_room_id, entrance.target_x,
864 entrance.target_y);
865 }
866 } else {
867 ImGui::Text("Error loading entrances");
868 }
869
870 // Quick room connection
871 Separator();
872 ImGui::Text("Connect Rooms");
873
874 static int target_room_id = 0;
875 static int source_x = 0;
876 static int source_y = 0;
877 static int target_x = 0;
878 static int target_y = 0;
879
880 ImGui::InputInt("Target Room", &target_room_id);
881 ImGui::InputInt("Source X", &source_x);
882 ImGui::InputInt("Source Y", &source_y);
883 ImGui::InputInt("Target X", &target_x);
884 ImGui::InputInt("Target Y", &target_y);
885
886 if (ImGui::Button("Connect")) {
887 auto status = system.ConnectRooms(current_room, target_room_id, source_x, source_y, target_x, target_y);
888 if (!status.ok()) {
889 ImGui::Text("Error connecting rooms");
890 }
891 }
892}
893
896 ImGui::Text("Dungeon editor system not initialized");
897 return;
898 }
899
900 auto& system = **dungeon_editor_system_;
901
902 ImGui::Text("Door Editor");
903 Separator();
904
905 // Display current room doors
906 auto current_room = system.GetCurrentRoom();
907 auto doors_result = system.GetDoorsByRoom(current_room);
908
909 if (doors_result.ok()) {
910 auto doors = doors_result.value();
911 ImGui::Text("Doors: %zu", doors.size());
912
913 for (const auto &door : doors) {
914 ImGui::Text("ID:%d (%d,%d) -> Room:%d", door.door_id, door.x, door.y,
915 door.target_room_id);
916 }
917 } else {
918 ImGui::Text("Error loading doors");
919 }
920
921 // Quick door creation
922 Separator();
923 ImGui::Text("Add Door");
924
925 static int door_x = 0;
926 static int door_y = 0;
927 static int door_direction = 0;
928 static int door_target_room = 0;
929
930 ImGui::InputInt("X", &door_x);
931 ImGui::InputInt("Y", &door_y);
932 ImGui::SliderInt("Dir", &door_direction, 0, 3);
933 ImGui::InputInt("Target", &door_target_room);
934
935 if (ImGui::Button("Add Door")) {
937 door_data.room_id = current_room;
938 door_data.x = door_x;
939 door_data.y = door_y;
940 door_data.direction = door_direction;
941 door_data.target_room_id = door_target_room;
942 door_data.target_x = door_x;
943 door_data.target_y = door_y;
944 door_data.is_locked = false;
945 door_data.requires_key = false;
946 door_data.key_type = 0;
947
948 auto status = system.AddDoor(door_data);
949 if (!status.ok()) {
950 ImGui::Text("Error adding door");
951 }
952 }
953}
954
957 ImGui::Text("Dungeon editor system not initialized");
958 return;
959 }
960
961 auto& system = **dungeon_editor_system_;
962
963 ImGui::Text("Chest Editor");
964 Separator();
965
966 // Display current room chests
967 auto current_room = system.GetCurrentRoom();
968 auto chests_result = system.GetChestsByRoom(current_room);
969
970 if (chests_result.ok()) {
971 auto chests = chests_result.value();
972 ImGui::Text("Chests: %zu", chests.size());
973
974 for (const auto &chest : chests) {
975 ImGui::Text("ID:%d (%d,%d) Item:%d", chest.chest_id, chest.x, chest.y,
976 chest.item_id);
977 }
978 } else {
979 ImGui::Text("Error loading chests");
980 }
981
982 // Quick chest creation
983 Separator();
984 ImGui::Text("Add Chest");
985
986 static int chest_x = 0;
987 static int chest_y = 0;
988 static int chest_item_id = 0;
989 static bool chest_big = false;
990
991 ImGui::InputInt("X", &chest_x);
992 ImGui::InputInt("Y", &chest_y);
993 ImGui::InputInt("Item ID", &chest_item_id);
994 ImGui::Checkbox("Big", &chest_big);
995
996 if (ImGui::Button("Add Chest")) {
998 chest_data.room_id = current_room;
999 chest_data.x = chest_x;
1000 chest_data.y = chest_y;
1001 chest_data.is_big_chest = chest_big;
1002 chest_data.item_id = chest_item_id;
1003 chest_data.item_quantity = 1;
1004
1005 auto status = system.AddChest(chest_data);
1006 if (!status.ok()) {
1007 ImGui::Text("Error adding chest");
1008 }
1009 }
1010}
1011
1014 ImGui::Text("Dungeon editor system not initialized");
1015 return;
1016 }
1017
1018 auto& system = **dungeon_editor_system_;
1019
1020 ImGui::Text("Room Properties");
1021 Separator();
1022
1023 auto current_room = system.GetCurrentRoom();
1024 auto properties_result = system.GetRoomProperties(current_room);
1025
1026 if (properties_result.ok()) {
1027 auto properties = properties_result.value();
1028
1029 static char room_name[128] = {0};
1030 static int dungeon_id = 0;
1031 static int floor_level = 0;
1032 static bool is_boss_room = false;
1033 static bool is_save_room = false;
1034 static int music_id = 0;
1035
1036 // Copy current values
1037 // Safe string copy with bounds checking
1038 size_t name_len = std::min(properties.name.length(), sizeof(room_name) - 1);
1039 std::memcpy(room_name, properties.name.c_str(), name_len);
1040 room_name[name_len] = '\0';
1041 dungeon_id = properties.dungeon_id;
1042 floor_level = properties.floor_level;
1043 is_boss_room = properties.is_boss_room;
1044 is_save_room = properties.is_save_room;
1045 music_id = properties.music_id;
1046
1047 ImGui::InputText("Name", room_name, sizeof(room_name));
1048 ImGui::InputInt("Dungeon ID", &dungeon_id);
1049 ImGui::InputInt("Floor", &floor_level);
1050 ImGui::InputInt("Music", &music_id);
1051 ImGui::Checkbox("Boss Room", &is_boss_room);
1052 ImGui::Checkbox("Save Room", &is_save_room);
1053
1054 if (ImGui::Button("Save Properties")) {
1056 new_properties.room_id = current_room;
1057 new_properties.name = room_name;
1058 new_properties.dungeon_id = dungeon_id;
1059 new_properties.floor_level = floor_level;
1060 new_properties.is_boss_room = is_boss_room;
1061 new_properties.is_save_room = is_save_room;
1062 new_properties.music_id = music_id;
1063
1064 auto status = system.SetRoomProperties(current_room, new_properties);
1065 if (!status.ok()) {
1066 ImGui::Text("Error saving properties");
1067 }
1068 }
1069 } else {
1070 ImGui::Text("Error loading properties");
1071 }
1072
1073 // Dungeon settings summary
1074 Separator();
1075 ImGui::Text("Dungeon Settings");
1076
1077 auto dungeon_settings_result = system.GetDungeonSettings();
1078 if (dungeon_settings_result.ok()) {
1079 auto settings = dungeon_settings_result.value();
1080 ImGui::Text("Dungeon: %s", settings.name.c_str());
1081 ImGui::Text("Rooms: %d", settings.total_rooms);
1082 ImGui::Text("Start: %d", settings.starting_room_id);
1083 ImGui::Text("Boss: %d", settings.boss_room_id);
1084 }
1085}
1086
1087} // namespace yaze::editor
auto palette_group() const
Definition rom.h:213
bool is_loaded() const
Definition rom.h:197
zelda3::DungeonObjectEditor * object_editor_
std::array< zelda3::Room, 0x128 > * rooms_
void CalculateObjectDimensions(const zelda3::RoomObject &object, int &width, int &height)
void RenderObjectPrimitive(const zelda3::RoomObject &object, int x, int y)
std::function< void(const zelda3::RoomObject &)> object_selected_callback_
std::unique_ptr< zelda3::DungeonEditorSystem > * dungeon_editor_system_
std::function< void(const zelda3::RoomObject &)> object_placement_callback_
bool MatchesObjectFilter(int obj_id, int filter_type)
std::array< gfx::Bitmap, 223 > & gfx_sheets()
Get reference to all graphics sheets.
Definition arena.h:78
static Arena & Get()
Definition arena.cc:15
auto height() const
Definition canvas.h:349
void DrawContextMenu()
Definition canvas.cc:441
auto draw_list() const
Definition canvas.h:309
auto width() const
Definition canvas.h:348
bool DrawTileSelector(int size, int size_y=0)
Definition canvas.cc:920
void DrawRect(int x, int y, int w, int h, ImVec4 color)
Definition canvas.cc:1329
auto zero_point() const
Definition canvas.h:310
void DrawBackground(ImVec2 canvas_size=ImVec2(0, 0))
Definition canvas.cc:381
void DrawGrid(float grid_step=64.0f, int tile_id_offset=8)
Definition canvas.cc:1386
void DrawText(const std::string &text, int x, int y)
Definition canvas.cc:1334
void set_x(uint8_t x)
Definition room_object.h:72
void set_rom(Rom *rom)
Definition room_object.h:67
Editors are the view controllers for the application.
Legacy chest data structure.
Definition zelda.h:437
Treasure chest.
Definition zelda.h:424
uint8_t y
Definition zelda.h:426
uint8_t x
Definition zelda.h:425