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
54 // ==========================================================================
55 // WindowContent Drawing
56 // ==========================================================================
57
58 void Draw(bool* p_open) override {
59 gui::AutoWidgetScope automation_scope("Dungeon/ItemEditor");
60 if (!current_room_id_ || !rooms_) {
61 ImGui::TextDisabled(tr("No room data available"));
62 return;
63 }
64
65 if (*current_room_id_ < 0 ||
66 *current_room_id_ >= static_cast<int>(rooms_->size())) {
67 ImGui::TextDisabled(tr("No room selected"));
68 return;
69 }
70
72 ImGui::Separator();
74 ImGui::Separator();
76 }
77
78 // ==========================================================================
79 // Panel-Specific Methods
80 // ==========================================================================
81
83
85 std::function<void(const zelda3::PotItem&)> callback) {
86 item_placed_callback_ = std::move(callback);
87 }
88 void SetOpenSelectionInspectorCallback(std::function<void()> callback) {
89 open_selection_inspector_callback_ = std::move(callback);
90 }
91
92 private:
93 // Pot item names from ZELDA3_DUNGEON_SPEC.md Section 7.2
94 static constexpr const char* kPotItemNames[] = {
95 "Nothing", // 0
96 "Green Rupee", // 1
97 "Rock", // 2
98 "Bee", // 3
99 "Health", // 4
100 "Bomb", // 5
101 "Heart", // 6
102 "Blue Rupee", // 7
103 "Key", // 8
104 "Arrow", // 9
105 "Bomb", // 10
106 "Heart", // 11
107 "Magic", // 12
108 "Full Magic", // 13
109 "Cucco", // 14
110 "Green Soldier", // 15
111 "Bush Stal", // 16
112 "Blue Soldier", // 17
113 "Landmine", // 18
114 "Heart", // 19
115 "Fairy", // 20
116 "Heart", // 21
117 "Nothing", // 22
118 "Hole", // 23
119 "Warp", // 24
120 "Staircase", // 25
121 "Bombable", // 26
122 "Switch" // 27
123 };
124 static constexpr size_t kPotItemCount =
125 sizeof(kPotItemNames) / sizeof(kPotItemNames[0]);
126
128 const auto& theme = AgentUI::GetTheme();
129 // Placement mode indicator
130 if (placement_mode_) {
131 const char* item_name = (selected_item_id_ < kPotItemCount)
133 : "Unknown";
134 ImGui::TextColored(theme.status_warning,
135 ICON_MD_PLACE " Placing: %s (0x%02X)", item_name,
137 if (ImGui::SmallButton(ICON_MD_CANCEL " Cancel")) {
138 placement_mode_ = false;
139 if (canvas_viewer_) {
141 }
142 }
143 } else {
144 ImGui::TextColored(theme.text_secondary_gray,
145 ICON_MD_INFO " Select an item to place");
146 }
147 }
148
150 const auto& theme = AgentUI::GetTheme();
151 ImGui::Text(ICON_MD_INVENTORY " Select Item:");
152
153 // Item grid with responsive sizing
154 float available_height = ImGui::GetContentRegionAvail().y;
155 // Reserve space for room items section (header + list + some margin)
156 float reserved_height = 120.0f;
157 // Calculate grid height: at least 150px, but responsive to available space
158 float grid_height =
159 std::max(150.0f, std::min(400.0f, available_height - reserved_height));
160
161 // Responsive item size based on panel width
162 float panel_width = ImGui::GetContentRegionAvail().x;
163 float item_size =
164 std::max(36.0f, std::min(48.0f, (panel_width - 40.0f) / 6.0f));
165 int items_per_row =
166 std::max(1, static_cast<int>(panel_width / (item_size + 8)));
167
168 ImGui::BeginChild("##ItemGrid", ImVec2(0, grid_height), true,
169 ImGuiWindowFlags_HorizontalScrollbar);
170
171 int col = 0;
172 for (size_t i = 0; i < kPotItemCount; ++i) {
173 bool is_selected = (selected_item_id_ == static_cast<int>(i));
174
175 ImGui::PushID(static_cast<int>(i));
176
177 // Color-coded button based on item type using theme colors
178 ImVec4 button_color = GetItemTypeColor(static_cast<int>(i), theme);
179 if (is_selected) {
180 button_color.x = std::min(1.0f, button_color.x + 0.2f);
181 button_color.y = std::min(1.0f, button_color.y + 0.2f);
182 button_color.z = std::min(1.0f, button_color.z + 0.2f);
183 }
184
185 {
186 gui::StyleColorGuard btn_colors({
187 {ImGuiCol_Button, button_color},
188 {ImGuiCol_ButtonHovered,
189 ImVec4(std::min(1.0f, button_color.x + 0.1f),
190 std::min(1.0f, button_color.y + 0.1f),
191 std::min(1.0f, button_color.z + 0.1f), 1.0f)},
192 {ImGuiCol_ButtonActive,
193 ImVec4(std::min(1.0f, button_color.x + 0.2f),
194 std::min(1.0f, button_color.y + 0.2f),
195 std::min(1.0f, button_color.z + 0.2f), 1.0f)},
196 });
197
198 // Get icon and short name for item
199 const char* icon = GetItemTypeIcon(static_cast<int>(i));
200 std::string label =
201 absl::StrFormat("%s\n%02X", icon, static_cast<int>(i));
202 if (ImGui::Button(label.c_str(), ImVec2(item_size, item_size))) {
203 selected_item_id_ = static_cast<int>(i);
204 placement_mode_ = true;
205 if (canvas_viewer_) {
207 true, static_cast<uint8_t>(i));
208 }
209 }
210 }
211
212 if (ImGui::IsItemHovered()) {
213 ImGui::SetTooltip(tr("%s (0x%02X)\nClick to select for placement"),
214 kPotItemNames[i], static_cast<int>(i));
215 }
216
217 // Selection highlight using theme color
218 if (is_selected) {
219 ImVec2 min = ImGui::GetItemRectMin();
220 ImVec2 max = ImGui::GetItemRectMax();
221 ImU32 sel_color =
222 ImGui::ColorConvertFloat4ToU32(theme.dungeon_selection_primary);
223 ImGui::GetWindowDrawList()->AddRect(min, max, sel_color, 0.0f, 0, 2.0f);
224 }
225
226 ImGui::PopID();
227
228 col++;
229 if (col < items_per_row) {
230 ImGui::SameLine();
231 } else {
232 col = 0;
233 }
234 }
235
236 ImGui::EndChild();
237 }
238
240 const auto& theme = AgentUI::GetTheme();
241 auto& room = (*rooms_)[*current_room_id_];
242 const auto& items = room.GetPotItems();
243 int selected_item_index = -1;
244 if (canvas_viewer_ &&
246 const auto selected_entity =
248 if (selected_entity.type == EntityType::Item) {
249 selected_item_index = static_cast<int>(selected_entity.index);
250 }
251 }
252
253 ImGui::Text(ICON_MD_LIST " Room Items (%zu):", items.size());
254 if (selected_item_index >= 0 && open_selection_inspector_callback_) {
255 ImGui::SameLine();
256 if (ImGui::SmallButton(ICON_MD_OPEN_IN_NEW " Inspect Selected")) {
258 }
259 }
260
261 if (items.empty()) {
262 ImGui::TextColored(theme.text_secondary_gray,
263 ICON_MD_INFO " No items in this room");
264 return;
265 }
266
267 if (selected_item_index >= 0 &&
268 selected_item_index < static_cast<int>(items.size()) &&
270 uint8_t raw_item_type = items[selected_item_index].item;
271 ImGui::AlignTextToFramePadding();
272 ImGui::TextUnformatted(tr("Selected raw type"));
273 ImGui::SameLine();
274 ImGui::SetNextItemWidth(72.0f);
275 const bool type_changed = gui::InputScalarDeferred(
276 "##SelectedItemRawType", ImGuiDataType_U8, &raw_item_type, "%02X",
277 ImGuiInputTextFlags_CharsHexadecimal,
278 {reinterpret_cast<uintptr_t>(rooms_),
279 static_cast<uint64_t>(*current_room_id_),
280 static_cast<uint64_t>(selected_item_index)});
281 gui::AutoRegisterLastItem("input_scalar", "selected_item_raw_type",
282 "Selected pot item raw type byte");
283 if (type_changed) {
286 .item_handler()
287 .MutateItemType(selected_item_index, raw_item_type);
288 }
289 }
290
291 float list_height = std::max(100.0f, ImGui::GetContentRegionAvail().y);
292 ImGui::BeginChild("##ItemList", ImVec2(0, list_height), true);
293 for (size_t i = 0; i < items.size(); ++i) {
294 const auto& item = items[i];
295
296 ImGui::PushID(static_cast<int>(i));
297
298 const char* item_name =
299 (item.item < kPotItemCount) ? kPotItemNames[item.item] : "Unknown";
300
301 const std::string row_label =
302 absl::StrFormat("[%zu] %s (0x%02X)", i, item_name, item.item);
303 const bool row_selected = ImGui::Selectable(
304 row_label.c_str(), selected_item_index == static_cast<int>(i));
306 "selectable", absl::StrFormat("item_row_%zu", i),
307 "Select a pot item in the current dungeon room");
308 ImGui::SameLine();
309 ImGui::TextColored(theme.text_secondary_gray, "@ (%d,%d)",
310 item.GetTileX(), item.GetTileY());
311
312 if (row_selected && canvas_viewer_) {
314 }
315
316 ImGui::PopID();
317 }
318 ImGui::EndChild();
319 }
320
321 ImVec4 GetItemTypeColor(int item_id, const AgentUITheme& theme) {
322 // Color-code based on item type using theme colors
323 if (item_id == 0 || item_id == 22) {
324 return theme.dungeon_object_default; // Gray for "Nothing"
325 } else if (item_id >= 1 && item_id <= 7) {
326 return theme.dungeon_sprite_layer0; // Green for rupees/items
327 } else if (item_id == 8) {
328 return theme.dungeon_object_chest; // Gold for key
329 } else if (item_id >= 15 && item_id <= 17) {
330 return theme.status_error; // Red for enemies
331 } else if (item_id >= 23 && item_id <= 27) {
332 return theme.dungeon_object_stairs; // Yellow for special
333 }
334 return theme.dungeon_object_pot; // Pot color for default
335 }
336
337 const char* GetItemTypeIcon(int item_id) {
338 // Return item-type-appropriate icons
339 if (item_id == 0 || item_id == 22) {
340 return ICON_MD_BLOCK; // Nothing
341 } else if (item_id == 1 || item_id == 7) {
342 return ICON_MD_MONETIZATION_ON; // Rupees (green/blue)
343 } else if (item_id == 4 || item_id == 6 || item_id == 11 || item_id == 19 ||
344 item_id == 21) {
345 return ICON_MD_FAVORITE; // Hearts
346 } else if (item_id == 8) {
347 return ICON_MD_KEY; // Key
348 } else if (item_id == 5 || item_id == 10) {
349 return ICON_MD_CIRCLE; // Bombs
350 } else if (item_id == 9) {
351 return ICON_MD_ARROW_UPWARD; // Arrows
352 } else if (item_id == 12 || item_id == 13) {
353 return ICON_MD_AUTO_AWESOME; // Magic
354 } else if (item_id == 14) {
355 return ICON_MD_EGG; // Cucco
356 } else if (item_id >= 15 && item_id <= 17) {
357 return ICON_MD_PERSON; // Soldiers
358 } else if (item_id == 18) {
359 return ICON_MD_WARNING; // Landmine
360 } else if (item_id == 20) {
361 return ICON_MD_FLUTTER_DASH; // Fairy
362 } else if (item_id == 23) {
363 return ICON_MD_TERRAIN; // Hole
364 } else if (item_id == 24) {
365 return ICON_MD_SWAP_HORIZ; // Warp
366 } else if (item_id == 25) {
367 return ICON_MD_STAIRS; // Staircase
368 } else if (item_id == 26) {
369 return ICON_MD_BROKEN_IMAGE; // Bombable
370 } else if (item_id == 27) {
371 return ICON_MD_TOGGLE_ON; // Switch
372 } else if (item_id == 2) {
373 return ICON_MD_LANDSCAPE; // Rock
374 } else if (item_id == 3) {
375 return ICON_MD_BUG_REPORT; // Bee
376 }
377 return ICON_MD_HELP; // Unknown
378 }
379
380 int* current_room_id_ = nullptr;
383
384 // Selection state
386 bool placement_mode_ = false;
387
388 std::function<void(const zelda3::PotItem&)> item_placed_callback_;
390};
391
392} // namespace editor
393} // namespace yaze
394
395#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_
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:388
Centralized theme colors for Agent UI components.
Definition agent_theme.h:19
Automatic widget registration helpers for ImGui Test Engine integration.