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"
15#include "app/gui/core/icons.h"
17#include "imgui/imgui.h"
18#include "zelda3/dungeon/room.h"
19
20namespace yaze {
21namespace editor {
22
35 public:
36 ItemEditorPanel(int* current_room_id, DungeonRoomStore* rooms,
37 DungeonCanvasViewer* canvas_viewer = nullptr)
38 : current_room_id_(current_room_id),
39 rooms_(rooms),
40 canvas_viewer_(canvas_viewer) {}
41
42 // ==========================================================================
43 // WindowContent Identity
44 // ==========================================================================
45
46 std::string GetId() const override { return "dungeon.item_editor"; }
47 std::string GetDisplayName() const override { return "Item Editor"; }
48 std::string GetIcon() const override { return ICON_MD_INVENTORY; }
49 std::string GetEditorCategory() const override { return "Dungeon"; }
50 int GetPriority() const override { return 66; }
51
52 // ==========================================================================
53 // WindowContent Drawing
54 // ==========================================================================
55
56 void Draw(bool* p_open) override {
57 if (!current_room_id_ || !rooms_) {
58 ImGui::TextDisabled(tr("No room data available"));
59 return;
60 }
61
62 if (*current_room_id_ < 0 ||
63 *current_room_id_ >= static_cast<int>(rooms_->size())) {
64 ImGui::TextDisabled(tr("No room selected"));
65 return;
66 }
67
69 ImGui::Separator();
71 ImGui::Separator();
73 }
74
75 // ==========================================================================
76 // Panel-Specific Methods
77 // ==========================================================================
78
80
82 std::function<void(const zelda3::PotItem&)> callback) {
83 item_placed_callback_ = std::move(callback);
84 }
85 void SetOpenSelectionInspectorCallback(std::function<void()> callback) {
86 open_selection_inspector_callback_ = std::move(callback);
87 }
88
89 private:
90 // Pot item names from ZELDA3_DUNGEON_SPEC.md Section 7.2
91 static constexpr const char* kPotItemNames[] = {
92 "Nothing", // 0
93 "Green Rupee", // 1
94 "Rock", // 2
95 "Bee", // 3
96 "Health", // 4
97 "Bomb", // 5
98 "Heart", // 6
99 "Blue Rupee", // 7
100 "Key", // 8
101 "Arrow", // 9
102 "Bomb", // 10
103 "Heart", // 11
104 "Magic", // 12
105 "Full Magic", // 13
106 "Cucco", // 14
107 "Green Soldier", // 15
108 "Bush Stal", // 16
109 "Blue Soldier", // 17
110 "Landmine", // 18
111 "Heart", // 19
112 "Fairy", // 20
113 "Heart", // 21
114 "Nothing", // 22
115 "Hole", // 23
116 "Warp", // 24
117 "Staircase", // 25
118 "Bombable", // 26
119 "Switch" // 27
120 };
121 static constexpr size_t kPotItemCount =
122 sizeof(kPotItemNames) / sizeof(kPotItemNames[0]);
123
125 const auto& theme = AgentUI::GetTheme();
126 // Placement mode indicator
127 if (placement_mode_) {
128 const char* item_name = (selected_item_id_ < kPotItemCount)
130 : "Unknown";
131 ImGui::TextColored(theme.status_warning,
132 ICON_MD_PLACE " Placing: %s (0x%02X)", item_name,
134 if (ImGui::SmallButton(ICON_MD_CANCEL " Cancel")) {
135 placement_mode_ = false;
136 if (canvas_viewer_) {
138 }
139 }
140 } else {
141 ImGui::TextColored(theme.text_secondary_gray,
142 ICON_MD_INFO " Select an item to place");
143 }
144 }
145
147 const auto& theme = AgentUI::GetTheme();
148 ImGui::Text(ICON_MD_INVENTORY " Select Item:");
149
150 // Item grid with responsive sizing
151 float available_height = ImGui::GetContentRegionAvail().y;
152 // Reserve space for room items section (header + list + some margin)
153 float reserved_height = 120.0f;
154 // Calculate grid height: at least 150px, but responsive to available space
155 float grid_height =
156 std::max(150.0f, std::min(400.0f, available_height - reserved_height));
157
158 // Responsive item size based on panel width
159 float panel_width = ImGui::GetContentRegionAvail().x;
160 float item_size =
161 std::max(36.0f, std::min(48.0f, (panel_width - 40.0f) / 6.0f));
162 int items_per_row =
163 std::max(1, static_cast<int>(panel_width / (item_size + 8)));
164
165 ImGui::BeginChild("##ItemGrid", ImVec2(0, grid_height), true,
166 ImGuiWindowFlags_HorizontalScrollbar);
167
168 int col = 0;
169 for (size_t i = 0; i < kPotItemCount; ++i) {
170 bool is_selected = (selected_item_id_ == static_cast<int>(i));
171
172 ImGui::PushID(static_cast<int>(i));
173
174 // Color-coded button based on item type using theme colors
175 ImVec4 button_color = GetItemTypeColor(static_cast<int>(i), theme);
176 if (is_selected) {
177 button_color.x = std::min(1.0f, button_color.x + 0.2f);
178 button_color.y = std::min(1.0f, button_color.y + 0.2f);
179 button_color.z = std::min(1.0f, button_color.z + 0.2f);
180 }
181
182 {
183 gui::StyleColorGuard btn_colors({
184 {ImGuiCol_Button, button_color},
185 {ImGuiCol_ButtonHovered,
186 ImVec4(std::min(1.0f, button_color.x + 0.1f),
187 std::min(1.0f, button_color.y + 0.1f),
188 std::min(1.0f, button_color.z + 0.1f), 1.0f)},
189 {ImGuiCol_ButtonActive,
190 ImVec4(std::min(1.0f, button_color.x + 0.2f),
191 std::min(1.0f, button_color.y + 0.2f),
192 std::min(1.0f, button_color.z + 0.2f), 1.0f)},
193 });
194
195 // Get icon and short name for item
196 const char* icon = GetItemTypeIcon(static_cast<int>(i));
197 std::string label =
198 absl::StrFormat("%s\n%02X", icon, static_cast<int>(i));
199 if (ImGui::Button(label.c_str(), ImVec2(item_size, item_size))) {
200 selected_item_id_ = static_cast<int>(i);
201 placement_mode_ = true;
202 if (canvas_viewer_) {
204 true, static_cast<uint8_t>(i));
205 }
206 }
207 }
208
209 if (ImGui::IsItemHovered()) {
210 ImGui::SetTooltip(tr("%s (0x%02X)\nClick to select for placement"),
211 kPotItemNames[i], static_cast<int>(i));
212 }
213
214 // Selection highlight using theme color
215 if (is_selected) {
216 ImVec2 min = ImGui::GetItemRectMin();
217 ImVec2 max = ImGui::GetItemRectMax();
218 ImU32 sel_color =
219 ImGui::ColorConvertFloat4ToU32(theme.dungeon_selection_primary);
220 ImGui::GetWindowDrawList()->AddRect(min, max, sel_color, 0.0f, 0, 2.0f);
221 }
222
223 ImGui::PopID();
224
225 col++;
226 if (col < items_per_row) {
227 ImGui::SameLine();
228 } else {
229 col = 0;
230 }
231 }
232
233 ImGui::EndChild();
234 }
235
237 const auto& theme = AgentUI::GetTheme();
238 auto& room = (*rooms_)[*current_room_id_];
239 const auto& items = room.GetPotItems();
240 int selected_item_index = -1;
241 if (canvas_viewer_ &&
243 const auto selected_entity =
245 if (selected_entity.type == EntityType::Item) {
246 selected_item_index = static_cast<int>(selected_entity.index);
247 }
248 }
249
250 ImGui::Text(ICON_MD_LIST " Room Items (%zu):", items.size());
251 if (selected_item_index >= 0 && open_selection_inspector_callback_) {
252 ImGui::SameLine();
253 if (ImGui::SmallButton(ICON_MD_OPEN_IN_NEW " Inspect Selected")) {
255 }
256 }
257
258 if (items.empty()) {
259 ImGui::TextColored(theme.text_secondary_gray,
260 ICON_MD_INFO " No items in this room");
261 return;
262 }
263
264 // Responsive list height - use remaining available space
265 float list_height =
266 std::max(120.0f, ImGui::GetContentRegionAvail().y - 10.0f);
267 ImGui::BeginChild("##ItemList", ImVec2(0, list_height), true);
268 for (size_t i = 0; i < items.size(); ++i) {
269 const auto& item = items[i];
270
271 ImGui::PushID(static_cast<int>(i));
272
273 const char* item_name =
274 (item.item < kPotItemCount) ? kPotItemNames[item.item] : "Unknown";
275
276 ImGui::Text(tr("[%zu] %s (0x%02X)"), i, item_name, item.item);
277 ImGui::SameLine();
278 ImGui::TextColored(theme.text_secondary_gray, "@ (%d,%d)",
279 item.GetTileX(), item.GetTileY());
280
281 if (ImGui::IsItemClicked() && canvas_viewer_) {
283 }
284 if (selected_item_index == static_cast<int>(i)) {
285 ImGui::SameLine();
286 ImGui::TextColored(theme.status_success, ICON_MD_CHECK_CIRCLE);
287 }
288
289 ImGui::PopID();
290 }
291 ImGui::EndChild();
292 }
293
294 ImVec4 GetItemTypeColor(int item_id, const AgentUITheme& theme) {
295 // Color-code based on item type using theme colors
296 if (item_id == 0 || item_id == 22) {
297 return theme.dungeon_object_default; // Gray for "Nothing"
298 } else if (item_id >= 1 && item_id <= 7) {
299 return theme.dungeon_sprite_layer0; // Green for rupees/items
300 } else if (item_id == 8) {
301 return theme.dungeon_object_chest; // Gold for key
302 } else if (item_id >= 15 && item_id <= 17) {
303 return theme.status_error; // Red for enemies
304 } else if (item_id >= 23 && item_id <= 27) {
305 return theme.dungeon_object_stairs; // Yellow for special
306 }
307 return theme.dungeon_object_pot; // Pot color for default
308 }
309
310 const char* GetItemTypeIcon(int item_id) {
311 // Return item-type-appropriate icons
312 if (item_id == 0 || item_id == 22) {
313 return ICON_MD_BLOCK; // Nothing
314 } else if (item_id == 1 || item_id == 7) {
315 return ICON_MD_MONETIZATION_ON; // Rupees (green/blue)
316 } else if (item_id == 4 || item_id == 6 || item_id == 11 || item_id == 19 ||
317 item_id == 21) {
318 return ICON_MD_FAVORITE; // Hearts
319 } else if (item_id == 8) {
320 return ICON_MD_KEY; // Key
321 } else if (item_id == 5 || item_id == 10) {
322 return ICON_MD_CIRCLE; // Bombs
323 } else if (item_id == 9) {
324 return ICON_MD_ARROW_UPWARD; // Arrows
325 } else if (item_id == 12 || item_id == 13) {
326 return ICON_MD_AUTO_AWESOME; // Magic
327 } else if (item_id == 14) {
328 return ICON_MD_EGG; // Cucco
329 } else if (item_id >= 15 && item_id <= 17) {
330 return ICON_MD_PERSON; // Soldiers
331 } else if (item_id == 18) {
332 return ICON_MD_WARNING; // Landmine
333 } else if (item_id == 20) {
334 return ICON_MD_FLUTTER_DASH; // Fairy
335 } else if (item_id == 23) {
336 return ICON_MD_TERRAIN; // Hole
337 } else if (item_id == 24) {
338 return ICON_MD_SWAP_HORIZ; // Warp
339 } else if (item_id == 25) {
340 return ICON_MD_STAIRS; // Staircase
341 } else if (item_id == 26) {
342 return ICON_MD_BROKEN_IMAGE; // Bombable
343 } else if (item_id == 27) {
344 return ICON_MD_TOGGLE_ON; // Switch
345 } else if (item_id == 2) {
346 return ICON_MD_LANDSCAPE; // Rock
347 } else if (item_id == 3) {
348 return ICON_MD_BUG_REPORT; // Bee
349 }
350 return ICON_MD_HELP; // Unknown
351 }
352
353 int* current_room_id_ = nullptr;
356
357 // Selection state
359 bool placement_mode_ = false;
360
361 std::function<void(const zelda3::PotItem&)> item_placed_callback_;
363};
364
365} // namespace editor
366} // namespace yaze
367
368#endif // YAZE_APP_EDITOR_DUNGEON_PANELS_ITEM_EDITOR_PANEL_H_
DungeonObjectInteraction & object_interaction()
void SetItemPlacementMode(bool enabled, uint8_t item_id=0)
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.
Base interface for all logical window content components.
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_CHECK_CIRCLE
Definition icons.h:400
#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()
Centralized theme colors for Agent UI components.
Definition agent_theme.h:19