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 "rom/rom.h"
14#include "imgui/imgui.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) ImGui::SameLine();
186 if (gui::ThemedButton("Copy Hex")) {
187 char buffer[32];
188 std::snprintf(buffer, sizeof(buffer), "0x%X", selection_.id);
189 ImGui::SetClipboardText(buffer);
190 }
191 wrote_action = true;
192 }
193 if (!selection_.display_name.empty()) {
194 if (wrote_action) ImGui::SameLine();
195 if (gui::ThemedButton("Copy Name")) {
196 ImGui::SetClipboardText(selection_.display_name.c_str());
197 }
198 wrote_action = true;
199 }
200
202
203 if (show_raw_data_) {
204 ImGui::Spacing();
205 ImGui::TextDisabled("Data Ptr: %p", selection_.data);
206 }
207}
208
210 if (!send_to_agent_) {
211 return;
212 }
213
214 ImGui::Spacing();
215 ImGui::Separator();
216 ImGui::Spacing();
217
218 ImGui::TextDisabled("%s Agent Actions", ICON_MD_SMART_TOY);
219 bool no_selection = (selection_.type == SelectionType::kNone);
220 ImGui::BeginDisabled(no_selection);
221 if (gui::ThemedButton("Explain")) {
222 SendAgentPrompt("Explain this selection and how to edit it safely.");
223 }
224 ImGui::SameLine();
225 if (gui::ThemedButton("Suggest Fixes")) {
226 SendAgentPrompt("Suggest improvements or checks for this selection.");
227 }
228 ImGui::SameLine();
229 if (gui::ThemedButton("Audit")) {
230 SendAgentPrompt("Audit this selection for possible issues or conflicts.");
231 }
232 ImGui::EndDisabled();
233 if (no_selection &&
234 ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
235 ImGui::SetTooltip("Select an object first to use agent actions");
236 }
237}
238
240 std::string context = absl::StrFormat("Selection Type: %s",
242 if (!selection_.display_name.empty()) {
243 context += absl::StrFormat("\nName: %s", selection_.display_name);
244 }
245 if (selection_.id >= 0) {
246 context += absl::StrFormat("\nID: 0x%X", selection_.id);
247 }
248 if (selection_.secondary_id >= 0) {
249 context += absl::StrFormat("\nSecondary ID: 0x%X",
251 }
252 if (selection_.read_only) {
253 context += "\nRead Only: true";
254 }
255 return context;
256}
257
258std::string SelectionPropertiesPanel::BuildAgentPrompt(const char* intent) const {
259 std::string prompt = intent ? intent : "Review this selection.";
260 prompt += "\n\n";
261 prompt += BuildSelectionContext();
262 prompt += "\n\nProvide actionable steps inside Yaze where possible.";
263 return prompt;
264}
265
267 if (!send_to_agent_) {
268 return;
269 }
271 if (focus_agent_panel_) {
273 }
274}
275
276bool SelectionPropertiesPanel::DrawPositionEditor(const char* label, int* x,
277 int* y, int min_val,
278 int max_val) {
279 bool changed = false;
280 ImGui::PushID(label);
281
282 ImGui::Text("%s", label);
283 ImGui::PushItemWidth(80);
284
285 ImGui::Text("X:");
286 ImGui::SameLine();
287 if (ImGui::InputInt("##X", x, 1, 8)) {
288 *x = std::clamp(*x, min_val, max_val);
289 changed = true;
290 }
291
292 ImGui::SameLine();
293 ImGui::Text("Y:");
294 ImGui::SameLine();
295 if (ImGui::InputInt("##Y", y, 1, 8)) {
296 *y = std::clamp(*y, min_val, max_val);
297 changed = true;
298 }
299
300 ImGui::PopItemWidth();
301 ImGui::PopID();
302
303 return changed;
304}
305
306bool SelectionPropertiesPanel::DrawSizeEditor(const char* label, int* width,
307 int* height) {
308 bool changed = false;
309 ImGui::PushID(label);
310
311 ImGui::Text("%s", label);
312 ImGui::PushItemWidth(80);
313
314 ImGui::Text("W:");
315 ImGui::SameLine();
316 if (ImGui::InputInt("##W", width, 1, 8)) {
317 *width = std::max(1, *width);
318 changed = true;
319 }
320
321 ImGui::SameLine();
322 ImGui::Text("H:");
323 ImGui::SameLine();
324 if (ImGui::InputInt("##H", height, 1, 8)) {
325 *height = std::max(1, *height);
326 changed = true;
327 }
328
329 ImGui::PopItemWidth();
330 ImGui::PopID();
331
332 return changed;
333}
334
336 uint8_t* value,
337 const char* tooltip) {
338 bool changed = false;
339 int val = *value;
340
341 ImGui::PushItemWidth(80);
342 if (ImGui::InputInt(label, &val, 1, 16)) {
343 *value = static_cast<uint8_t>(std::clamp(val, 0, 255));
344 changed = true;
345 }
346 ImGui::PopItemWidth();
347
348 if (tooltip && ImGui::IsItemHovered()) {
349 ImGui::SetTooltip("%s", tooltip);
350 }
351
352 return changed;
353}
354
356 uint16_t* value,
357 const char* tooltip) {
358 bool changed = false;
359 int val = *value;
360
361 ImGui::PushItemWidth(100);
362 if (ImGui::InputInt(label, &val, 1, 256)) {
363 *value = static_cast<uint16_t>(std::clamp(val, 0, 65535));
364 changed = true;
365 }
366 ImGui::PopItemWidth();
367
368 if (tooltip && ImGui::IsItemHovered()) {
369 ImGui::SetTooltip("%s", tooltip);
370 }
371
372 return changed;
373}
374
376 int* current_item,
377 const char* const items[],
378 int items_count) {
379 return ImGui::Combo(label, current_item, items, items_count);
380}
381
383 uint8_t* flags,
384 const char* const flag_names[],
385 int flag_count) {
386 bool changed = false;
387
388 if (ImGui::TreeNode(label)) {
389 for (int i = 0; i < flag_count && i < 8; ++i) {
390 bool bit_set = (*flags >> i) & 1;
391 if (ImGui::Checkbox(flag_names[i], &bit_set)) {
392 if (bit_set) {
393 *flags |= (1 << i);
394 } else {
395 *flags &= ~(1 << i);
396 }
397 changed = true;
398 }
399 }
400 ImGui::TreePop();
401 }
402
403 return changed;
404}
405
407 const char* value) {
408 ImGui::Text("%s:", label);
409 ImGui::SameLine();
410 ImGui::TextDisabled("%s", value);
411}
412
414 uint32_t value, int digits) {
415 ImGui::Text("%s:", label);
416 ImGui::SameLine();
417 char fmt[16];
418 snprintf(fmt, sizeof(fmt), "0x%%0%dX", digits);
419 ImGui::TextDisabled(fmt, value);
420}
421
427
428// ============================================================================
429// Type-specific property editors
430// ============================================================================
431
433 DrawPropertyHeader(ICON_MD_GRID_VIEW, "Dungeon Room");
434
435 if (!selection_.data) {
436 ImGui::TextDisabled("No room data available.");
437 return;
438 }
439
440 auto* room = static_cast<zelda3::Room*>(selection_.data);
441
442 if (ImGui::CollapsingHeader("Identity", ImGuiTreeNodeFlags_DefaultOpen)) {
443 DrawReadOnlyHex("Room ID", selection_.id, 4);
445 }
446
447 if (ImGui::CollapsingHeader("Graphics & Layout", ImGuiTreeNodeFlags_DefaultOpen)) {
448 uint8_t blockset = room->blockset();
449 if (DrawByteProperty("Blockset", &blockset, "Tile graphics and layout definition")) {
450 room->SetBlockset(blockset);
451 NotifyChange();
452 }
453
454 uint8_t palette = room->palette();
455 if (DrawByteProperty("Palette", &palette, "Room color scheme")) {
456 room->SetPalette(palette);
457 NotifyChange();
458 }
459
460 uint8_t spriteset = room->spriteset();
461 if (DrawByteProperty("Spriteset", &spriteset, "Enemy graphics and behavior")) {
462 room->SetSpriteset(spriteset);
463 NotifyChange();
464 }
465 }
466
467 if (show_advanced_ &&
468 ImGui::CollapsingHeader("Advanced Settings", ImGuiTreeNodeFlags_DefaultOpen)) {
469
470 // Room Tags with manifest integration
471 auto draw_tag_combo = [&](const char* label, zelda3::TagKey current, std::function<void(zelda3::TagKey)> setter) {
472 std::string current_label = zelda3::GetRoomTagLabel(static_cast<int>(current));
473 if (ImGui::BeginCombo(label, current_label.c_str())) {
474 const auto& vanilla_tags = zelda3::Zelda3Labels::GetRoomTagNames();
475 for (int i = 0; i < static_cast<int>(vanilla_tags.size()); ++i) {
476 std::string item_label = zelda3::GetRoomTagLabel(i);
477 if (ImGui::Selectable(item_label.c_str(), static_cast<int>(current) == i)) {
478 setter(static_cast<zelda3::TagKey>(i));
479 NotifyChange();
480 }
481 }
482 ImGui::EndCombo();
483 }
484 };
485
486 draw_tag_combo("Tag 1", room->tag1(), [&](zelda3::TagKey val) { room->SetTag1(val); });
487 draw_tag_combo("Tag 2", room->tag2(), [&](zelda3::TagKey val) { room->SetTag2(val); });
488
489 // Room Effect
490 int effect = static_cast<int>(room->effect());
491 const auto& effects = zelda3::Zelda3Labels::GetRoomEffectNames();
492 std::vector<const char*> effect_ptrs;
493 for (const auto& s : effects) effect_ptrs.push_back(s.c_str());
494
495 if (ImGui::Combo("Effect", &effect, effect_ptrs.data(), static_cast<int>(effect_ptrs.size()))) {
496 room->SetEffect(static_cast<zelda3::EffectKey>(effect));
497 NotifyChange();
498 }
499 }
500}
501
503 DrawPropertyHeader(ICON_MD_CATEGORY, "Dungeon Object");
504
505 if (ImGui::CollapsingHeader("Transform", ImGuiTreeNodeFlags_DefaultOpen)) {
506 // Placeholder for actual object data
507 int x = 0, y = 0;
508 if (DrawPositionEditor("Position", &x, &y, 0, 63)) {
509 NotifyChange();
510 }
511
512 int w = 1, h = 1;
513 if (DrawSizeEditor("Size", &w, &h)) {
514 NotifyChange();
515 }
516 }
517
518 if (ImGui::CollapsingHeader("Object Type", ImGuiTreeNodeFlags_DefaultOpen)) {
519 ImGui::TextDisabled("Object ID: --");
520 ImGui::TextDisabled("Subtype: --");
521 ImGui::TextDisabled("Layer: --");
522 }
523
524 if (show_raw_data_ &&
525 ImGui::CollapsingHeader("Raw Data", ImGuiTreeNodeFlags_None)) {
526 ImGui::TextDisabled("Byte 1: 0x00");
527 ImGui::TextDisabled("Byte 2: 0x00");
528 ImGui::TextDisabled("Byte 3: 0x00");
529 }
530}
531
533 DrawPropertyHeader(ICON_MD_PEST_CONTROL, "Dungeon Sprite");
534
535 if (ImGui::CollapsingHeader("Position", ImGuiTreeNodeFlags_DefaultOpen)) {
536 int x = 0, y = 0;
537 if (DrawPositionEditor("Position", &x, &y, 0, 255)) {
538 NotifyChange();
539 }
540 }
541
542 if (ImGui::CollapsingHeader("Sprite Data", ImGuiTreeNodeFlags_DefaultOpen)) {
543 ImGui::TextDisabled("Sprite ID: --");
544 ImGui::TextDisabled("Subtype: --");
545 ImGui::TextDisabled("Overlord: No");
546 }
547}
548
550 DrawPropertyHeader(ICON_MD_DOOR_FRONT, "Dungeon Entrance");
551
552 if (ImGui::CollapsingHeader("Target", ImGuiTreeNodeFlags_DefaultOpen)) {
553 ImGui::TextDisabled("Target Room: --");
554 ImGui::TextDisabled("Entry Position: --");
555 }
556
557 if (ImGui::CollapsingHeader("Properties", ImGuiTreeNodeFlags_DefaultOpen)) {
558 ImGui::TextDisabled("Door Type: --");
559 ImGui::TextDisabled("Direction: --");
560 }
561}
562
564 DrawPropertyHeader(ICON_MD_MAP, "Overworld Map");
565
566 if (ImGui::CollapsingHeader("Identity", ImGuiTreeNodeFlags_DefaultOpen)) {
567 DrawReadOnlyHex("Map ID", selection_.id, 2);
569 }
570
571 if (ImGui::CollapsingHeader("Graphics", ImGuiTreeNodeFlags_DefaultOpen)) {
572 ImGui::TextDisabled("GFX Set: --");
573 ImGui::TextDisabled("Palette: --");
574 ImGui::TextDisabled("Sprite GFX: --");
575 ImGui::TextDisabled("Sprite Palette: --");
576 }
577
578 if (ImGui::CollapsingHeader("Properties", ImGuiTreeNodeFlags_DefaultOpen)) {
579 ImGui::TextDisabled("Large Map: No");
580 ImGui::TextDisabled("Area Size: 1x1");
581 ImGui::TextDisabled("Parent ID: --");
582 }
583}
584
586 DrawPropertyHeader(ICON_MD_GRID_ON, "Overworld Tile");
587
588 if (ImGui::CollapsingHeader("Tile Info", ImGuiTreeNodeFlags_DefaultOpen)) {
589 DrawReadOnlyHex("Tile16 ID", selection_.id, 4);
590 ImGui::TextDisabled("Position: --");
591 }
592
593 if (show_advanced_ &&
594 ImGui::CollapsingHeader("Tile16 Data", ImGuiTreeNodeFlags_None)) {
595 ImGui::TextDisabled("TL: 0x0000");
596 ImGui::TextDisabled("TR: 0x0000");
597 ImGui::TextDisabled("BL: 0x0000");
598 ImGui::TextDisabled("BR: 0x0000");
599 }
600}
601
603 DrawPropertyHeader(ICON_MD_PEST_CONTROL, "Overworld Sprite");
604
605 if (ImGui::CollapsingHeader("Position", ImGuiTreeNodeFlags_DefaultOpen)) {
606 int x = 0, y = 0;
607 if (DrawPositionEditor("Position", &x, &y, 0, 8191)) {
608 NotifyChange();
609 }
610 }
611
612 if (ImGui::CollapsingHeader("Sprite", ImGuiTreeNodeFlags_DefaultOpen)) {
613 ImGui::TextDisabled("Sprite ID: --");
614 ImGui::TextDisabled("Map ID: --");
615 }
616}
617
619 DrawPropertyHeader(ICON_MD_DOOR_FRONT, "Overworld Entrance");
620
621 if (ImGui::CollapsingHeader("Position", ImGuiTreeNodeFlags_DefaultOpen)) {
622 int x = 0, y = 0;
623 if (DrawPositionEditor("Position", &x, &y)) {
624 NotifyChange();
625 }
626 }
627
628 if (ImGui::CollapsingHeader("Target", ImGuiTreeNodeFlags_DefaultOpen)) {
629 ImGui::TextDisabled("Entrance ID: --");
630 ImGui::TextDisabled("Target Room: --");
631 }
632}
633
635 DrawPropertyHeader(ICON_MD_EXIT_TO_APP, "Overworld Exit");
636
637 if (ImGui::CollapsingHeader("Exit Point", ImGuiTreeNodeFlags_DefaultOpen)) {
638 ImGui::TextDisabled("Exit ID: --");
639 int x = 0, y = 0;
640 if (DrawPositionEditor("Position", &x, &y)) {
641 NotifyChange();
642 }
643 }
644
645 if (ImGui::CollapsingHeader("Target Map", ImGuiTreeNodeFlags_DefaultOpen)) {
646 ImGui::TextDisabled("Room ID: --");
647 ImGui::TextDisabled("Target Map: --");
648 }
649}
650
652 DrawPropertyHeader(ICON_MD_STAR, "Overworld Item");
653
654 if (ImGui::CollapsingHeader("Position", ImGuiTreeNodeFlags_DefaultOpen)) {
655 int x = 0, y = 0;
656 if (DrawPositionEditor("Position", &x, &y)) {
657 NotifyChange();
658 }
659 }
660
661 if (ImGui::CollapsingHeader("Item Data", ImGuiTreeNodeFlags_DefaultOpen)) {
662 ImGui::TextDisabled("Item ID: --");
663 ImGui::TextDisabled("Map ID: --");
664 }
665}
666
668 DrawPropertyHeader(ICON_MD_IMAGE, "Graphics Sheet");
669
670 if (ImGui::CollapsingHeader("Sheet Info", ImGuiTreeNodeFlags_DefaultOpen)) {
671 DrawReadOnlyHex("Sheet ID", selection_.id, 2);
672 ImGui::TextDisabled("Size: 128x32");
673 ImGui::TextDisabled("BPP: 4");
674 }
675
676 if (show_advanced_ &&
677 ImGui::CollapsingHeader("ROM Location", ImGuiTreeNodeFlags_None)) {
678 ImGui::TextDisabled("Address: --");
679 ImGui::TextDisabled("Compressed: Yes");
680 ImGui::TextDisabled("Original Size: --");
681 }
682}
683
686
687 if (ImGui::CollapsingHeader("Palette Info", ImGuiTreeNodeFlags_DefaultOpen)) {
688 DrawReadOnlyHex("Palette ID", selection_.id, 2);
689 ImGui::TextDisabled("Colors: 16");
690 }
691
692 if (ImGui::CollapsingHeader("Colors", ImGuiTreeNodeFlags_DefaultOpen)) {
693 // Would show color swatches in actual implementation
694 ImGui::TextDisabled("Color editing not yet implemented");
695 }
696}
697
698} // namespace editor
699} // 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()