yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
item_editor_panel.h
Go to the documentation of this file.
1#ifndef YAZE_APP_EDITOR_DUNGEON_PANELS_ITEM_EDITOR_PANEL_H_
2#define YAZE_APP_EDITOR_DUNGEON_PANELS_ITEM_EDITOR_PANEL_H_
3
4#include <array>
5#include <cstdint>
6#include <functional>
7#include <string>
8#include "util/i18n/tr.h"
9
10#include "absl/strings/str_format.h"
16#include "app/gui/core/icons.h"
17#include "app/gui/core/input.h"
19#include "imgui/imgui.h"
20#include "zelda3/dungeon/room.h"
21
22namespace yaze {
23namespace editor {
24
37 public:
38 ItemEditorPanel(int* current_room_id, DungeonRoomStore* rooms,
39 DungeonCanvasViewer* canvas_viewer = nullptr)
40 : current_room_id_(current_room_id),
41 rooms_(rooms),
42 canvas_viewer_(canvas_viewer) {}
43
44 // ==========================================================================
45 // WindowContent Identity
46 // ==========================================================================
47
48 std::string GetId() const override { return "dungeon.item_editor"; }
49 std::string GetDisplayName() const override { return "Item Editor"; }
50 std::string GetIcon() const override { return ICON_MD_INVENTORY; }
51 std::string GetEditorCategory() const override { return "Dungeon"; }
52 int GetPriority() const override { return 66; }
53 std::string GetWorkflowGroup() const override { return "Editors"; }
54
55 // ==========================================================================
56 // WindowContent Drawing
57 // ==========================================================================
58
59 void Draw(bool* p_open) override {
60 gui::AutoWidgetScope automation_scope("Dungeon/ItemEditor");
61 if (!current_room_id_ || !rooms_) {
62 ImGui::TextDisabled(tr("No room data available"));
63 return;
64 }
65
66 if (*current_room_id_ < 0 ||
67 *current_room_id_ >= static_cast<int>(rooms_->size())) {
68 ImGui::TextDisabled(tr("No room selected"));
69 return;
70 }
71
73 ImGui::Separator();
75 ImGui::Separator();
77 }
78
79 // ==========================================================================
80 // Panel-Specific Methods
81 // ==========================================================================
82
84
86 std::function<void(const zelda3::PotItem&)> callback) {
87 item_placed_callback_ = std::move(callback);
88 }
89 void SetOpenSelectionInspectorCallback(std::function<void()> callback) {
90 open_selection_inspector_callback_ = std::move(callback);
91 }
92
93 private:
94 // Pot item names from ZELDA3_DUNGEON_SPEC.md Section 7.2
95 static constexpr const char* kPotItemNames[] = {
96 "Nothing", // 0
97 "Green Rupee", // 1
98 "Rock", // 2
99 "Bee", // 3
100 "Health", // 4
101 "Bomb", // 5
102 "Heart", // 6
103 "Blue Rupee", // 7
104 "Key", // 8
105 "Arrow", // 9
106 "Bomb", // 10
107 "Heart", // 11
108 "Magic", // 12
109 "Full Magic", // 13
110 "Cucco", // 14
111 "Green Soldier", // 15
112 "Bush Stal", // 16
113 "Blue Soldier", // 17
114 "Landmine", // 18
115 "Heart", // 19
116 "Fairy", // 20
117 "Heart", // 21
118 "Nothing", // 22
119 "Hole", // 23
120 "Warp", // 24
121 "Staircase", // 25
122 "Bombable", // 26
123 "Switch" // 27
124 };
125 static constexpr size_t kPotItemCount =
126 sizeof(kPotItemNames) / sizeof(kPotItemNames[0]);
127
129 const auto& theme = AgentUI::GetTheme();
130 // Placement mode indicator
131 if (placement_mode_) {
132 const char* item_name = (selected_item_id_ < kPotItemCount)
134 : "Unknown";
135 ImGui::TextColored(theme.status_warning,
136 ICON_MD_PLACE " Placing: %s (0x%02X)", item_name,
138 if (ImGui::SmallButton(ICON_MD_CANCEL " Cancel")) {
139 placement_mode_ = false;
140 if (canvas_viewer_) {
142 }
143 }
144 } else {
145 ImGui::TextColored(theme.text_secondary_gray,
146 ICON_MD_INFO " Select an item to place");
147 }
148 }
149
151 const auto& theme = AgentUI::GetTheme();
152 ImGui::Text(ICON_MD_INVENTORY " Select Item:");
153
154 // Item grid with responsive sizing
155 float available_height = ImGui::GetContentRegionAvail().y;
156 // Reserve space for room items section (header + list + some margin)
157 float reserved_height = 120.0f;
158 // Calculate grid height: at least 150px, but responsive to available space
159 float grid_height =
160 std::max(150.0f, std::min(400.0f, available_height - reserved_height));
161
162 // Responsive item size based on panel width
163 float panel_width = ImGui::GetContentRegionAvail().x;
164 float item_size =
165 std::max(36.0f, std::min(48.0f, (panel_width - 40.0f) / 6.0f));
166 int items_per_row =
167 std::max(1, static_cast<int>(panel_width / (item_size + 8)));
168
169 ImGui::BeginChild("##ItemGrid", ImVec2(0, grid_height), true,
170 ImGuiWindowFlags_HorizontalScrollbar);
171
172 int col = 0;
173 for (size_t i = 0; i < kPotItemCount; ++i) {
174 bool is_selected = (selected_item_id_ == static_cast<int>(i));
175
176 ImGui::PushID(static_cast<int>(i));
177
178 // Color-coded button based on item type using theme colors
179 ImVec4 button_color = GetItemTypeColor(static_cast<int>(i), theme);
180 if (is_selected) {
181 button_color.x = std::min(1.0f, button_color.x + 0.2f);
182 button_color.y = std::min(1.0f, button_color.y + 0.2f);
183 button_color.z = std::min(1.0f, button_color.z + 0.2f);
184 }
185
186 {
187 gui::StyleColorGuard btn_colors({
188 {ImGuiCol_Button, button_color},
189 {ImGuiCol_ButtonHovered,
190 ImVec4(std::min(1.0f, button_color.x + 0.1f),
191 std::min(1.0f, button_color.y + 0.1f),
192 std::min(1.0f, button_color.z + 0.1f), 1.0f)},
193 {ImGuiCol_ButtonActive,
194 ImVec4(std::min(1.0f, button_color.x + 0.2f),
195 std::min(1.0f, button_color.y + 0.2f),
196 std::min(1.0f, button_color.z + 0.2f), 1.0f)},
197 });
198
199 // Get icon and short name for item
200 const char* icon = GetItemTypeIcon(static_cast<int>(i));
201 std::string label =
202 absl::StrFormat("%s\n%02X", icon, static_cast<int>(i));
203 if (ImGui::Button(label.c_str(), ImVec2(item_size, item_size))) {
204 selected_item_id_ = static_cast<int>(i);
205 placement_mode_ = true;
206 if (canvas_viewer_) {
208 true, static_cast<uint8_t>(i));
209 }
210 }
211 }
212
213 if (ImGui::IsItemHovered()) {
214 ImGui::SetTooltip(tr("%s (0x%02X)\nClick to select for placement"),
215 kPotItemNames[i], static_cast<int>(i));
216 }
217
218 // Selection highlight using theme color
219 if (is_selected) {
220 ImVec2 min = ImGui::GetItemRectMin();
221 ImVec2 max = ImGui::GetItemRectMax();
222 ImU32 sel_color =
223 ImGui::ColorConvertFloat4ToU32(theme.dungeon_selection_primary);
224 ImGui::GetWindowDrawList()->AddRect(min, max, sel_color, 0.0f, 0, 2.0f);
225 }
226
227 ImGui::PopID();
228
229 col++;
230 if (col < items_per_row) {
231 ImGui::SameLine();
232 } else {
233 col = 0;
234 }
235 }
236
237 ImGui::EndChild();
238 }
239
241 const auto& theme = AgentUI::GetTheme();
242 auto& room = (*rooms_)[*current_room_id_];
243 const auto& items = room.GetPotItems();
244 int selected_item_index = -1;
245 if (canvas_viewer_ &&
247 const auto selected_entity =
249 if (selected_entity.type == EntityType::Item) {
250 selected_item_index = static_cast<int>(selected_entity.index);
251 }
252 }
253
254 ImGui::Text(ICON_MD_LIST " Room Items (%zu):", items.size());
255 if (selected_item_index >= 0 && open_selection_inspector_callback_) {
256 ImGui::SameLine();
257 if (ImGui::SmallButton(ICON_MD_OPEN_IN_NEW " Inspect Selected")) {
259 }
260 }
261
262 if (items.empty()) {
263 ImGui::TextColored(theme.text_secondary_gray,
264 ICON_MD_INFO " No items in this room");
265 return;
266 }
267
268 if (selected_item_index >= 0 &&
269 selected_item_index < static_cast<int>(items.size()) &&
271 uint8_t raw_item_type = items[selected_item_index].item;
272 ImGui::AlignTextToFramePadding();
273 ImGui::TextUnformatted(tr("Selected raw type"));
274 ImGui::SameLine();
275 ImGui::SetNextItemWidth(72.0f);
276 const bool type_changed = gui::InputScalarDeferred(
277 "##SelectedItemRawType", ImGuiDataType_U8, &raw_item_type, "%02X",
278 ImGuiInputTextFlags_CharsHexadecimal,
279 {reinterpret_cast<uintptr_t>(rooms_),
280 static_cast<uint64_t>(*current_room_id_),
281 static_cast<uint64_t>(selected_item_index)});
282 gui::AutoRegisterLastItem("input_scalar", "selected_item_raw_type",
283 "Selected pot item raw type byte");
284 if (type_changed) {
287 .item_handler()
288 .MutateItemType(selected_item_index, raw_item_type);
289 }
290 }
291
292 float list_height = std::max(100.0f, ImGui::GetContentRegionAvail().y);
293 ImGui::BeginChild("##ItemList", ImVec2(0, list_height), true);
294 for (size_t i = 0; i < items.size(); ++i) {
295 const auto& item = items[i];
296
297 ImGui::PushID(static_cast<int>(i));
298
299 const char* item_name =
300 (item.item < kPotItemCount) ? kPotItemNames[item.item] : "Unknown";
301
302 const std::string row_label =
303 absl::StrFormat("[%zu] %s (0x%02X)", i, item_name, item.item);
304 const bool row_selected = ImGui::Selectable(
305 row_label.c_str(), selected_item_index == static_cast<int>(i));
307 "selectable", absl::StrFormat("item_row_%zu", i),
308 "Select a pot item in the current dungeon room");
309 ImGui::SameLine();
310 ImGui::TextColored(theme.text_secondary_gray, "@ (%d,%d)",
311 item.GetTileX(), item.GetTileY());
312
313 if (row_selected && canvas_viewer_) {
315 }
316
317 ImGui::PopID();
318 }
319 ImGui::EndChild();
320 }
321
322 ImVec4 GetItemTypeColor(int item_id, const AgentUITheme& theme) {
323 // Color-code based on item type using theme colors
324 if (item_id == 0 || item_id == 22) {
325 return theme.dungeon_object_default; // Gray for "Nothing"
326 } else if (item_id >= 1 && item_id <= 7) {
327 return theme.dungeon_sprite_layer0; // Green for rupees/items
328 } else if (item_id == 8) {
329 return theme.dungeon_object_chest; // Gold for key
330 } else if (item_id >= 15 && item_id <= 17) {
331 return theme.status_error; // Red for enemies
332 } else if (item_id >= 23 && item_id <= 27) {
333 return theme.dungeon_object_stairs; // Yellow for special
334 }
335 return theme.dungeon_object_pot; // Pot color for default
336 }
337
338 const char* GetItemTypeIcon(int item_id) {
339 // Return item-type-appropriate icons
340 if (item_id == 0 || item_id == 22) {
341 return ICON_MD_BLOCK; // Nothing
342 } else if (item_id == 1 || item_id == 7) {
343 return ICON_MD_MONETIZATION_ON; // Rupees (green/blue)
344 } else if (item_id == 4 || item_id == 6 || item_id == 11 || item_id == 19 ||
345 item_id == 21) {
346 return ICON_MD_FAVORITE; // Hearts
347 } else if (item_id == 8) {
348 return ICON_MD_KEY; // Key
349 } else if (item_id == 5 || item_id == 10) {
350 return ICON_MD_CIRCLE; // Bombs
351 } else if (item_id == 9) {
352 return ICON_MD_ARROW_UPWARD; // Arrows
353 } else if (item_id == 12 || item_id == 13) {
354 return ICON_MD_AUTO_AWESOME; // Magic
355 } else if (item_id == 14) {
356 return ICON_MD_EGG; // Cucco
357 } else if (item_id >= 15 && item_id <= 17) {
358 return ICON_MD_PERSON; // Soldiers
359 } else if (item_id == 18) {
360 return ICON_MD_WARNING; // Landmine
361 } else if (item_id == 20) {
362 return ICON_MD_FLUTTER_DASH; // Fairy
363 } else if (item_id == 23) {
364 return ICON_MD_TERRAIN; // Hole
365 } else if (item_id == 24) {
366 return ICON_MD_SWAP_HORIZ; // Warp
367 } else if (item_id == 25) {
368 return ICON_MD_STAIRS; // Staircase
369 } else if (item_id == 26) {
370 return ICON_MD_BROKEN_IMAGE; // Bombable
371 } else if (item_id == 27) {
372 return ICON_MD_TOGGLE_ON; // Switch
373 } else if (item_id == 2) {
374 return ICON_MD_LANDSCAPE; // Rock
375 } else if (item_id == 3) {
376 return ICON_MD_BUG_REPORT; // Bee
377 }
378 return ICON_MD_HELP; // Unknown
379 }
380
381 int* current_room_id_ = nullptr;
384
385 // Selection state
387 bool placement_mode_ = false;
388
389 std::function<void(const zelda3::PotItem&)> item_placed_callback_;
391};
392
393} // namespace editor
394} // namespace yaze
395
396#endif // YAZE_APP_EDITOR_DUNGEON_PANELS_ITEM_EDITOR_PANEL_H_
DungeonObjectInteraction & object_interaction()
void SetItemPlacementMode(bool enabled, uint8_t item_id=0)
InteractionCoordinator & entity_coordinator()
Get the interaction coordinator for entity handling.
void SelectEntity(EntityType type, size_t index)
WindowContent for placing and managing dungeon pot items.
std::string GetIcon() const override
Material Design icon for this panel.
static constexpr size_t kPotItemCount
static constexpr const char * kPotItemNames[]
int GetPriority() const override
Get display priority for menu ordering.
ImVec4 GetItemTypeColor(int item_id, const AgentUITheme &theme)
void SetCanvasViewer(DungeonCanvasViewer *viewer)
void SetOpenSelectionInspectorCallback(std::function< void()> callback)
std::function< void(const zelda3::PotItem &) item_placed_callback_)
std::string GetEditorCategory() const override
Editor category this panel belongs to.
std::string GetId() const override
Unique identifier for this panel.
DungeonCanvasViewer * canvas_viewer_
void SetItemPlacedCallback(std::function< void(const zelda3::PotItem &)> callback)
std::string GetDisplayName() const override
Human-readable name shown in menus and title bars.
const char * GetItemTypeIcon(int item_id)
std::function< void()> open_selection_inspector_callback_
std::string GetWorkflowGroup() const override
Optional workflow group for hack-centric actions.
ItemEditorPanel(int *current_room_id, DungeonRoomStore *rooms, DungeonCanvasViewer *canvas_viewer=nullptr)
void Draw(bool *p_open) override
Draw the panel content.
bool MutateItemType(size_t index, uint8_t new_type)
Change one pot item's raw type byte in place.
Base interface for all logical window content components.
RAII scope that enables automatic widget registration.
RAII guard for ImGui style colors.
Definition style_guard.h:27
#define ICON_MD_BLOCK
Definition icons.h:269
#define ICON_MD_FLUTTER_DASH
Definition icons.h:805
#define ICON_MD_INFO
Definition icons.h:993
#define ICON_MD_CANCEL
Definition icons.h:364
#define ICON_MD_LANDSCAPE
Definition icons.h:1059
#define ICON_MD_WARNING
Definition icons.h:2123
#define ICON_MD_TERRAIN
Definition icons.h:1952
#define ICON_MD_PLACE
Definition icons.h:1477
#define ICON_MD_SWAP_HORIZ
Definition icons.h:1896
#define ICON_MD_CIRCLE
Definition icons.h:411
#define ICON_MD_STAIRS
Definition icons.h:1847
#define ICON_MD_AUTO_AWESOME
Definition icons.h:214
#define ICON_MD_BUG_REPORT
Definition icons.h:327
#define ICON_MD_TOGGLE_ON
Definition icons.h:1994
#define ICON_MD_LIST
Definition icons.h:1094
#define ICON_MD_BROKEN_IMAGE
Definition icons.h:320
#define ICON_MD_EGG
Definition icons.h:654
#define ICON_MD_INVENTORY
Definition icons.h:1011
#define ICON_MD_ARROW_UPWARD
Definition icons.h:189
#define ICON_MD_PERSON
Definition icons.h:1415
#define ICON_MD_FAVORITE
Definition icons.h:727
#define ICON_MD_KEY
Definition icons.h:1026
#define ICON_MD_OPEN_IN_NEW
Definition icons.h:1354
#define ICON_MD_HELP
Definition icons.h:933
#define ICON_MD_MONETIZATION_ON
Definition icons.h:1229
const AgentUITheme & GetTheme()
void AutoRegisterLastItem(const std::string &widget_type, const std::string &explicit_label, const std::string &description)
Automatically register the last ImGui item.
bool InputScalarDeferred(const char *label, ImGuiDataType data_type, void *data, const char *format, ImGuiInputTextFlags flags, InputScalarTargetIdentity target_identity)
Definition input.cc:392
Centralized theme colors for Agent UI components.
Definition agent_theme.h:19
Automatic widget registration helpers for ImGui Test Engine integration.