yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
selection_properties_panel.cc
Go to the documentation of this file.
2
3#include <cstdio>
4
5#include "absl/strings/str_cat.h"
6#include "absl/strings/str_format.h"
13#include "imgui/imgui.h"
14#include "rom/rom.h"
15#include "zelda3/dungeon/room.h"
18
19namespace yaze {
20namespace editor {
21
23 switch (type) {
25 return "None";
27 return "Dungeon Room";
29 return "Dungeon Object";
31 return "Dungeon Sprite";
33 return "Dungeon Entrance";
35 return "Overworld Map";
37 return "Overworld Tile";
39 return "Overworld Sprite";
41 return "Overworld Entrance";
43 return "Overworld Exit";
45 return "Overworld Item";
47 return "Graphics Sheet";
49 return "Palette";
50 default:
51 return "Unknown";
52 }
53}
54
58
62
66 ImGui::Spacing();
67 }
68
69 switch (selection_.type) {
72 break;
75 break;
78 break;
81 break;
84 break;
87 break;
90 break;
93 break;
96 break;
99 break;
102 break;
105 break;
108 break;
109 }
110
111 // Advanced options toggle
112 ImGui::Spacing();
113 ImGui::Separator();
114 ImGui::Spacing();
115 ImGui::Checkbox("Show Advanced", &show_advanced_);
116 ImGui::SameLine();
117 ImGui::Checkbox("Raw Data", &show_raw_data_);
118}
119
121 const auto& theme = gui::ThemeManager::Get().GetCurrentTheme();
122
123 gui::ColoredText(ICON_MD_TOUCH_APP " Select an Item",
125
126 ImGui::Spacing();
127 ImGui::TextWrapped(
128 "Click on an object in the editor to view and edit its properties.");
129
130 ImGui::Spacing();
131 ImGui::Separator();
132 ImGui::Spacing();
133
134 // Show quick reference for what can be selected
135 ImGui::TextDisabled("Selectable Items:");
136 ImGui::BulletText("Dungeon: Rooms, Objects, Sprites");
137 ImGui::BulletText("Overworld: Maps, Tiles, Entities");
138 ImGui::BulletText("Graphics: Sheets, Palettes");
139}
140
142 const char* title) {
143 std::string full_title = absl::StrFormat("%s %s", icon, title);
144 if (!selection_.display_name.empty()) {
145 absl::StrAppend(&full_title, " - ", selection_.display_name);
146 }
147 if (selection_.read_only) {
148 absl::StrAppend(&full_title, " (Read Only)");
149 }
150
151 gui::SectionHeader(full_title.c_str());
152 ImGui::Spacing();
153}
154
157 ImGui::Separator();
158
159 ImGui::Text("Type: %s", GetSelectionTypeName(selection_.type));
160 if (!selection_.display_name.empty()) {
161 ImGui::Text("Name: %s", selection_.display_name.c_str());
162 }
163 if (selection_.id >= 0) {
164 ImGui::Text("ID: %d (0x%X)", selection_.id, selection_.id);
165 }
166 if (selection_.secondary_id >= 0) {
167 ImGui::Text("Secondary: %d (0x%X)", selection_.secondary_id,
169 }
170 if (selection_.read_only) {
171 ImGui::TextDisabled("Read Only");
172 }
173
174 ImGui::Spacing();
175 bool wrote_action = false;
176 if (selection_.id >= 0) {
177 if (gui::ThemedButton("Copy ID")) {
178 char buffer[32];
179 std::snprintf(buffer, sizeof(buffer), "%d", selection_.id);
180 ImGui::SetClipboardText(buffer);
181 }
182 wrote_action = true;
183 }
184 if (selection_.id >= 0) {
185 if (wrote_action)
186 ImGui::SameLine();
187 if (gui::ThemedButton("Copy Hex")) {
188 char buffer[32];
189 std::snprintf(buffer, sizeof(buffer), "0x%X", selection_.id);
190 ImGui::SetClipboardText(buffer);
191 }
192 wrote_action = true;
193 }
194 if (!selection_.display_name.empty()) {
195 if (wrote_action)
196 ImGui::SameLine();
197 if (gui::ThemedButton("Copy Name")) {
198 ImGui::SetClipboardText(selection_.display_name.c_str());
199 }
200 wrote_action = true;
201 }
202
204
205 if (show_raw_data_) {
206 ImGui::Spacing();
207 ImGui::TextDisabled("Data Ptr: %p", selection_.data);
208 }
209}
210
212 if (!send_to_agent_) {
213 return;
214 }
215
216 ImGui::Spacing();
217 ImGui::Separator();
218 ImGui::Spacing();
219
220 ImGui::TextDisabled("%s Agent Actions", ICON_MD_SMART_TOY);
221 bool no_selection = (selection_.type == SelectionType::kNone);
222 ImGui::BeginDisabled(no_selection);
223 if (gui::ThemedButton("Explain")) {
224 SendAgentPrompt("Explain this selection and how to edit it safely.");
225 }
226 ImGui::SameLine();
227 if (gui::ThemedButton("Suggest Fixes")) {
228 SendAgentPrompt("Suggest improvements or checks for this selection.");
229 }
230 ImGui::SameLine();
231 if (gui::ThemedButton("Audit")) {
232 SendAgentPrompt("Audit this selection for possible issues or conflicts.");
233 }
234 ImGui::EndDisabled();
235 if (no_selection &&
236 ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
237 ImGui::SetTooltip("Select an object first to use agent actions");
238 }
239}
240
242 std::string context = absl::StrFormat("Selection Type: %s",
244 if (!selection_.display_name.empty()) {
245 context += absl::StrFormat("\nName: %s", selection_.display_name);
246 }
247 if (selection_.id >= 0) {
248 context += absl::StrFormat("\nID: 0x%X", selection_.id);
249 }
250 if (selection_.secondary_id >= 0) {
251 context += absl::StrFormat("\nSecondary ID: 0x%X", selection_.secondary_id);
252 }
253 if (selection_.read_only) {
254 context += "\nRead Only: true";
255 }
256 return context;
257}
258
260 const char* intent) const {
261 std::string prompt = intent ? intent : "Review this selection.";
262 prompt += "\n\n";
263 prompt += BuildSelectionContext();
264 prompt += "\n\nProvide actionable steps inside Yaze where possible.";
265 return prompt;
266}
267
269 if (!send_to_agent_) {
270 return;
271 }
273 if (focus_agent_panel_) {
275 }
276}
277
278bool SelectionPropertiesPanel::DrawPositionEditor(const char* label, int* x,
279 int* y, int min_val,
280 int max_val) {
281 bool changed = false;
282 ImGui::PushID(label);
283
284 ImGui::Text("%s", label);
285 ImGui::PushItemWidth(80);
286
287 ImGui::Text("X:");
288 ImGui::SameLine();
289 if (ImGui::InputInt("##X", x, 1, 8)) {
290 *x = std::clamp(*x, min_val, max_val);
291 changed = true;
292 }
293
294 ImGui::SameLine();
295 ImGui::Text("Y:");
296 ImGui::SameLine();
297 if (ImGui::InputInt("##Y", y, 1, 8)) {
298 *y = std::clamp(*y, min_val, max_val);
299 changed = true;
300 }
301
302 ImGui::PopItemWidth();
303 ImGui::PopID();
304
305 return changed;
306}
307
308bool SelectionPropertiesPanel::DrawSizeEditor(const char* label, int* width,
309 int* height) {
310 bool changed = false;
311 ImGui::PushID(label);
312
313 ImGui::Text("%s", label);
314 ImGui::PushItemWidth(80);
315
316 ImGui::Text("W:");
317 ImGui::SameLine();
318 if (ImGui::InputInt("##W", width, 1, 8)) {
319 *width = std::max(1, *width);
320 changed = true;
321 }
322
323 ImGui::SameLine();
324 ImGui::Text("H:");
325 ImGui::SameLine();
326 if (ImGui::InputInt("##H", height, 1, 8)) {
327 *height = std::max(1, *height);
328 changed = true;
329 }
330
331 ImGui::PopItemWidth();
332 ImGui::PopID();
333
334 return changed;
335}
336
338 uint8_t* value,
339 const char* tooltip) {
340 bool changed = false;
341 int val = *value;
342
343 ImGui::PushItemWidth(80);
344 if (ImGui::InputInt(label, &val, 1, 16)) {
345 *value = static_cast<uint8_t>(std::clamp(val, 0, 255));
346 changed = true;
347 }
348 ImGui::PopItemWidth();
349
350 if (tooltip && ImGui::IsItemHovered()) {
351 ImGui::SetTooltip("%s", tooltip);
352 }
353
354 return changed;
355}
356
358 uint16_t* value,
359 const char* tooltip) {
360 bool changed = false;
361 int val = *value;
362
363 ImGui::PushItemWidth(100);
364 if (ImGui::InputInt(label, &val, 1, 256)) {
365 *value = static_cast<uint16_t>(std::clamp(val, 0, 65535));
366 changed = true;
367 }
368 ImGui::PopItemWidth();
369
370 if (tooltip && ImGui::IsItemHovered()) {
371 ImGui::SetTooltip("%s", tooltip);
372 }
373
374 return changed;
375}
376
378 int* current_item,
379 const char* const items[],
380 int items_count) {
381 return ImGui::Combo(label, current_item, items, items_count);
382}
383
385 uint8_t* flags,
386 const char* const flag_names[],
387 int flag_count) {
388 bool changed = false;
389
390 if (ImGui::TreeNode(label)) {
391 for (int i = 0; i < flag_count && i < 8; ++i) {
392 bool bit_set = (*flags >> i) & 1;
393 if (ImGui::Checkbox(flag_names[i], &bit_set)) {
394 if (bit_set) {
395 *flags |= (1 << i);
396 } else {
397 *flags &= ~(1 << i);
398 }
399 changed = true;
400 }
401 }
402 ImGui::TreePop();
403 }
404
405 return changed;
406}
407
409 const char* value) {
410 ImGui::Text("%s:", label);
411 ImGui::SameLine();
412 ImGui::TextDisabled("%s", value);
413}
414
416 uint32_t value, int digits) {
417 ImGui::Text("%s:", label);
418 ImGui::SameLine();
419 char fmt[16];
420 snprintf(fmt, sizeof(fmt), "0x%%0%dX", digits);
421 ImGui::TextDisabled(fmt, value);
422}
423
429
430// ============================================================================
431// Type-specific property editors
432// ============================================================================
433
435 DrawPropertyHeader(ICON_MD_GRID_VIEW, "Dungeon Room");
436
437 if (!selection_.data) {
438 ImGui::TextDisabled("No room data available.");
439 return;
440 }
441
442 auto* room = static_cast<zelda3::Room*>(selection_.data);
443
444 if (ImGui::CollapsingHeader("Identity", ImGuiTreeNodeFlags_DefaultOpen)) {
445 DrawReadOnlyHex("Room ID", selection_.id, 4);
447 }
448
449 if (ImGui::CollapsingHeader("Graphics & Layout",
450 ImGuiTreeNodeFlags_DefaultOpen)) {
451 uint8_t blockset = room->blockset();
452 if (DrawByteProperty("Blockset", &blockset,
453 "Tile graphics and layout definition")) {
454 room->SetBlockset(blockset);
455 NotifyChange();
456 }
457
458 uint8_t palette = room->palette();
459 if (DrawByteProperty("Palette", &palette, "Room color scheme")) {
460 room->SetPalette(palette);
461 NotifyChange();
462 }
463
464 uint8_t spriteset = room->spriteset();
465 if (DrawByteProperty("Spriteset", &spriteset,
466 "Enemy graphics and behavior")) {
467 room->SetSpriteset(spriteset);
468 NotifyChange();
469 }
470 }
471
472 if (show_advanced_ &&
473 ImGui::CollapsingHeader("Advanced Settings",
474 ImGuiTreeNodeFlags_DefaultOpen)) {
475
476 // Room Tags with manifest integration
477 auto draw_tag_combo = [&](const char* label, zelda3::TagKey current,
478 std::function<void(zelda3::TagKey)> setter) {
479 std::string current_label =
480 zelda3::GetRoomTagLabel(static_cast<int>(current));
481 if (ImGui::BeginCombo(label, current_label.c_str())) {
482 const auto& vanilla_tags = zelda3::Zelda3Labels::GetRoomTagNames();
483 for (int i = 0; i < static_cast<int>(vanilla_tags.size()); ++i) {
484 std::string item_label = zelda3::GetRoomTagLabel(i);
485 if (ImGui::Selectable(item_label.c_str(),
486 static_cast<int>(current) == i)) {
487 setter(static_cast<zelda3::TagKey>(i));
488 NotifyChange();
489 }
490 }
491 ImGui::EndCombo();
492 }
493 };
494
495 draw_tag_combo("Tag 1", room->tag1(),
496 [&](zelda3::TagKey val) { room->SetTag1(val); });
497 draw_tag_combo("Tag 2", room->tag2(),
498 [&](zelda3::TagKey val) { room->SetTag2(val); });
499
500 // Room Effect
501 int effect = static_cast<int>(room->effect());
502 const auto& effects = zelda3::Zelda3Labels::GetRoomEffectNames();
503 std::vector<const char*> effect_ptrs;
504 for (const auto& s : effects)
505 effect_ptrs.push_back(s.c_str());
506
507 if (ImGui::Combo("Effect", &effect, effect_ptrs.data(),
508 static_cast<int>(effect_ptrs.size()))) {
509 room->SetEffect(static_cast<zelda3::EffectKey>(effect));
510 NotifyChange();
511 }
512 }
513}
514
516 DrawPropertyHeader(ICON_MD_CATEGORY, "Dungeon Object");
517
518 if (ImGui::CollapsingHeader("Transform", ImGuiTreeNodeFlags_DefaultOpen)) {
519 // Placeholder for actual object data
520 int x = 0, y = 0;
521 if (DrawPositionEditor("Position", &x, &y, 0, 63)) {
522 NotifyChange();
523 }
524
525 int w = 1, h = 1;
526 if (DrawSizeEditor("Size", &w, &h)) {
527 NotifyChange();
528 }
529 }
530
531 if (ImGui::CollapsingHeader("Object Type", ImGuiTreeNodeFlags_DefaultOpen)) {
532 ImGui::TextDisabled("Object ID: --");
533 ImGui::TextDisabled("Subtype: --");
534 ImGui::TextDisabled("Layer: --");
535 }
536
537 if (show_raw_data_ &&
538 ImGui::CollapsingHeader("Raw Data", ImGuiTreeNodeFlags_None)) {
539 ImGui::TextDisabled("Byte 1: 0x00");
540 ImGui::TextDisabled("Byte 2: 0x00");
541 ImGui::TextDisabled("Byte 3: 0x00");
542 }
543}
544
546 DrawPropertyHeader(ICON_MD_PEST_CONTROL, "Dungeon Sprite");
547
548 if (ImGui::CollapsingHeader("Position", ImGuiTreeNodeFlags_DefaultOpen)) {
549 int x = 0, y = 0;
550 if (DrawPositionEditor("Position", &x, &y, 0, 255)) {
551 NotifyChange();
552 }
553 }
554
555 if (ImGui::CollapsingHeader("Sprite Data", ImGuiTreeNodeFlags_DefaultOpen)) {
556 ImGui::TextDisabled("Sprite ID: --");
557 ImGui::TextDisabled("Subtype: --");
558 ImGui::TextDisabled("Overlord: No");
559 }
560}
561
563 DrawPropertyHeader(ICON_MD_DOOR_FRONT, "Dungeon Entrance");
564
565 if (ImGui::CollapsingHeader("Target", ImGuiTreeNodeFlags_DefaultOpen)) {
566 ImGui::TextDisabled("Target Room: --");
567 ImGui::TextDisabled("Entry Position: --");
568 }
569
570 if (ImGui::CollapsingHeader("Properties", ImGuiTreeNodeFlags_DefaultOpen)) {
571 ImGui::TextDisabled("Door Type: --");
572 ImGui::TextDisabled("Direction: --");
573 }
574}
575
577 DrawPropertyHeader(ICON_MD_MAP, "Overworld Map");
578
579 if (ImGui::CollapsingHeader("Identity", ImGuiTreeNodeFlags_DefaultOpen)) {
580 DrawReadOnlyHex("Map ID", selection_.id, 2);
582 }
583
584 if (ImGui::CollapsingHeader("Graphics", ImGuiTreeNodeFlags_DefaultOpen)) {
585 ImGui::TextDisabled("GFX Set: --");
586 ImGui::TextDisabled("Palette: --");
587 ImGui::TextDisabled("Sprite GFX: --");
588 ImGui::TextDisabled("Sprite Palette: --");
589 }
590
591 if (ImGui::CollapsingHeader("Properties", ImGuiTreeNodeFlags_DefaultOpen)) {
592 ImGui::TextDisabled("Large Map: No");
593 ImGui::TextDisabled("Area Size: 1x1");
594 ImGui::TextDisabled("Parent ID: --");
595 }
596}
597
599 DrawPropertyHeader(ICON_MD_GRID_ON, "Overworld Tile");
600
601 if (ImGui::CollapsingHeader("Tile Info", ImGuiTreeNodeFlags_DefaultOpen)) {
602 DrawReadOnlyHex("Tile16 ID", selection_.id, 4);
603 ImGui::TextDisabled("Position: --");
604 }
605
606 if (show_advanced_ &&
607 ImGui::CollapsingHeader("Tile16 Data", ImGuiTreeNodeFlags_None)) {
608 ImGui::TextDisabled("TL: 0x0000");
609 ImGui::TextDisabled("TR: 0x0000");
610 ImGui::TextDisabled("BL: 0x0000");
611 ImGui::TextDisabled("BR: 0x0000");
612 }
613}
614
616 DrawPropertyHeader(ICON_MD_PEST_CONTROL, "Overworld Sprite");
617
618 if (ImGui::CollapsingHeader("Position", ImGuiTreeNodeFlags_DefaultOpen)) {
619 int x = 0, y = 0;
620 if (DrawPositionEditor("Position", &x, &y, 0, 8191)) {
621 NotifyChange();
622 }
623 }
624
625 if (ImGui::CollapsingHeader("Sprite", ImGuiTreeNodeFlags_DefaultOpen)) {
626 ImGui::TextDisabled("Sprite ID: --");
627 ImGui::TextDisabled("Map ID: --");
628 }
629}
630
632 DrawPropertyHeader(ICON_MD_DOOR_FRONT, "Overworld Entrance");
633
634 if (ImGui::CollapsingHeader("Position", ImGuiTreeNodeFlags_DefaultOpen)) {
635 int x = 0, y = 0;
636 if (DrawPositionEditor("Position", &x, &y)) {
637 NotifyChange();
638 }
639 }
640
641 if (ImGui::CollapsingHeader("Target", ImGuiTreeNodeFlags_DefaultOpen)) {
642 ImGui::TextDisabled("Entrance ID: --");
643 ImGui::TextDisabled("Target Room: --");
644 }
645}
646
648 DrawPropertyHeader(ICON_MD_EXIT_TO_APP, "Overworld Exit");
649
650 if (ImGui::CollapsingHeader("Exit Point", ImGuiTreeNodeFlags_DefaultOpen)) {
651 ImGui::TextDisabled("Exit ID: --");
652 int x = 0, y = 0;
653 if (DrawPositionEditor("Position", &x, &y)) {
654 NotifyChange();
655 }
656 }
657
658 if (ImGui::CollapsingHeader("Target Map", ImGuiTreeNodeFlags_DefaultOpen)) {
659 ImGui::TextDisabled("Room ID: --");
660 ImGui::TextDisabled("Target Map: --");
661 }
662}
663
665 DrawPropertyHeader(ICON_MD_STAR, "Overworld Item");
666
667 if (ImGui::CollapsingHeader("Position", ImGuiTreeNodeFlags_DefaultOpen)) {
668 int x = 0, y = 0;
669 if (DrawPositionEditor("Position", &x, &y)) {
670 NotifyChange();
671 }
672 }
673
674 if (ImGui::CollapsingHeader("Item Data", ImGuiTreeNodeFlags_DefaultOpen)) {
675 ImGui::TextDisabled("Item ID: --");
676 ImGui::TextDisabled("Map ID: --");
677 }
678}
679
681 DrawPropertyHeader(ICON_MD_IMAGE, "Graphics Sheet");
682
683 if (ImGui::CollapsingHeader("Sheet Info", ImGuiTreeNodeFlags_DefaultOpen)) {
684 DrawReadOnlyHex("Sheet ID", selection_.id, 2);
685 ImGui::TextDisabled("Size: 128x32");
686 ImGui::TextDisabled("BPP: 4");
687 }
688
689 if (show_advanced_ &&
690 ImGui::CollapsingHeader("ROM Location", ImGuiTreeNodeFlags_None)) {
691 ImGui::TextDisabled("Address: --");
692 ImGui::TextDisabled("Compressed: Yes");
693 ImGui::TextDisabled("Original Size: --");
694 }
695}
696
699
700 if (ImGui::CollapsingHeader("Palette Info", ImGuiTreeNodeFlags_DefaultOpen)) {
701 DrawReadOnlyHex("Palette ID", selection_.id, 2);
702 ImGui::TextDisabled("Colors: 16");
703 }
704
705 if (ImGui::CollapsingHeader("Colors", ImGuiTreeNodeFlags_DefaultOpen)) {
706 // Would show color swatches in actual implementation
707 ImGui::TextDisabled("Color editing not yet implemented");
708 }
709}
710
711} // namespace editor
712} // namespace yaze
bool DrawByteProperty(const char *label, uint8_t *value, const char *tooltip=nullptr)
bool DrawFlagsProperty(const char *label, uint8_t *flags, const char *const flag_names[], int flag_count)
std::string BuildAgentPrompt(const char *intent) const
bool DrawPositionEditor(const char *label, int *x, int *y, int min_val=0, int max_val=512)
std::function< void(const std::string &) send_to_agent_)
void DrawReadOnlyText(const char *label, const char *value)
bool DrawSizeEditor(const char *label, int *width, int *height)
bool DrawWordProperty(const char *label, uint16_t *value, const char *tooltip=nullptr)
bool DrawComboProperty(const char *label, int *current_item, const char *const items[], int items_count)
void ClearSelection()
Clear the current selection.
void Draw()
Draw the properties panel content.
void DrawReadOnlyHex(const char *label, uint32_t value, int digits=4)
void DrawPropertyHeader(const char *icon, const char *title)
void SetSelection(const SelectionContext &context)
Set the current selection to display/edit.
const Theme & GetCurrentTheme() const
static ThemeManager & Get()
#define ICON_MD_GRID_VIEW
Definition icons.h:897
#define ICON_MD_EXIT_TO_APP
Definition icons.h:699
#define ICON_MD_INFO
Definition icons.h:993
#define ICON_MD_STAR
Definition icons.h:1848
#define ICON_MD_MAP
Definition icons.h:1173
#define ICON_MD_GRID_ON
Definition icons.h:896
#define ICON_MD_DOOR_FRONT
Definition icons.h:613
#define ICON_MD_IMAGE
Definition icons.h:982
#define ICON_MD_PEST_CONTROL
Definition icons.h:1429
#define ICON_MD_TOUCH_APP
Definition icons.h:2000
#define ICON_MD_PALETTE
Definition icons.h:1370
#define ICON_MD_CATEGORY
Definition icons.h:382
#define ICON_MD_SMART_TOY
Definition icons.h:1781
const char * GetSelectionTypeName(SelectionType type)
Get a human-readable name for a selection type.
SelectionType
Types of entities that can be selected and edited.
void ColoredText(const char *text, const ImVec4 &color)
bool ThemedButton(const char *label, const ImVec2 &size, const char *panel_id, const char *anim_id)
Draw a standard text button with theme colors.
ImVec4 GetPrimaryVec4()
void SectionHeader(const char *icon, const char *label, const ImVec4 &color)
ImVec4 GetTextDisabledVec4()
void ColoredTextF(const ImVec4 &color, const char *fmt,...)
std::string GetRoomTagLabel(int id)
Convenience function to get a room tag label.
Holds information about the current selection.
static const std::vector< std::string > & GetRoomEffectNames()
static const std::vector< std::string > & GetRoomTagNames()