yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
chest_editor_panel.h
Go to the documentation of this file.
1#ifndef YAZE_APP_EDITOR_DUNGEON_PANELS_CHEST_EDITOR_PANEL_H_
2#define YAZE_APP_EDITOR_DUNGEON_PANELS_CHEST_EDITOR_PANEL_H_
3
4#include <array>
5#include <cctype>
6#include <cstdint>
7#include <functional>
8#include <string>
9#include "util/i18n/tr.h"
10
11#include "absl/strings/str_format.h"
16#include "app/gui/core/icons.h"
18#include "imgui/imgui.h"
19#include "zelda3/dungeon/room.h"
21
22namespace yaze {
23namespace editor {
24
37 public:
38 ChestEditorPanel(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.chest_editor"; }
49 std::string GetDisplayName() const override { return "Chest Editor"; }
50 std::string GetIcon() const override { return ICON_MD_INVENTORY_2; }
51 std::string GetEditorCategory() const override { return "Dungeon"; }
52 int GetPriority() const override { return 70; } // After sprite editor
53
54 // ==========================================================================
55 // WindowContent Drawing
56 // ==========================================================================
57
58 void Draw(bool* p_open) override {
59 if (!current_room_id_ || !rooms_) {
60 ImGui::TextDisabled(tr("No room data available"));
61 return;
62 }
63
64 if (*current_room_id_ < 0 ||
65 *current_room_id_ >= static_cast<int>(rooms_->size())) {
66 ImGui::TextDisabled(tr("No room selected"));
67 return;
68 }
69
71 ImGui::Separator();
73 }
74
75 // ==========================================================================
76 // Panel-Specific Methods
77 // ==========================================================================
78
80
81 void SetChestModifiedCallback(std::function<void(int, int)> callback) {
82 chest_modified_callback_ = std::move(callback);
83 }
84
85 private:
87 const auto& theme = AgentUI::GetTheme();
88 auto& room = (*rooms_)[*current_room_id_];
89 auto& chests = room.GetChests();
90
91 // Header with count and limit warning
92 int chest_count = static_cast<int>(chests.size());
93 ImVec4 count_color =
94 chest_count > 6 ? theme.text_error_red : theme.text_primary;
95 ImGui::TextColored(count_color, ICON_MD_INVENTORY_2 " Chests: %d/6",
96 chest_count);
97
98 if (chest_count > 6) {
99 ImGui::SameLine();
100 ImGui::TextColored(theme.text_warning_yellow, ICON_MD_WARNING);
101 if (ImGui::IsItemHovered()) {
102 ImGui::SetTooltip(
103 tr("Room exceeds chest limit (6 max)!\n"
104 "This may cause game crashes."));
105 }
106 }
107
108 // Add chest button
109 ImGui::SameLine();
110 if (ImGui::SmallButton(ICON_MD_ADD " Add")) {
111 // Add new chest with default values
112 zelda3::chest_data new_chest;
113 new_chest.id = 0; // Default item: nothing
114 new_chest.size = false; // Small chest
115 chests.push_back(new_chest);
116 room.MarkChestsDirty();
117 selected_chest_index_ = static_cast<int>(chests.size()) - 1;
120 }
121 }
122 if (ImGui::IsItemHovered()) {
123 ImGui::SetTooltip(tr("Add new chest to room"));
124 }
125
126 // Chest list
127 if (chests.empty()) {
128 ImGui::TextColored(theme.text_secondary_gray,
129 ICON_MD_INFO " No chests in this room");
130 return;
131 }
132
133 const auto& item_names = zelda3::Zelda3Labels::GetItemNames();
134 float list_height =
135 std::min(200.0f, ImGui::GetContentRegionAvail().y * 0.5f);
136 ImGui::BeginChild("##ChestList", ImVec2(0, list_height), true);
137
138 for (size_t i = 0; i < chests.size(); ++i) {
139 const auto& chest = chests[i];
140 bool is_selected = (selected_chest_index_ == static_cast<int>(i));
141
142 ImGui::PushID(static_cast<int>(i));
143
144 // Chest icon based on size
145 const char* size_icon =
147 const char* size_label = chest.size ? "Big" : "Small";
148
149 // Get item name
150 std::string item_name =
151 (chest.id < item_names.size())
152 ? item_names[chest.id]
153 : absl::StrFormat("Unknown (0x%02X)", chest.id);
154
155 // Selectable list item
156 std::string label = absl::StrFormat("%s [%zu] %s: %s", size_icon, i + 1,
157 size_label, item_name.c_str());
158
159 if (ImGui::Selectable(label.c_str(), is_selected)) {
160 selected_chest_index_ = static_cast<int>(i);
161 }
162
163 // Highlight with theme color
164 if (is_selected) {
165 ImVec2 min = ImGui::GetItemRectMin();
166 ImVec2 max = ImGui::GetItemRectMax();
167 ImU32 sel_color =
168 ImGui::ColorConvertFloat4ToU32(theme.dungeon_selection_primary);
169 ImGui::GetWindowDrawList()->AddRect(min, max, sel_color, 0.0f, 0, 2.0f);
170 }
171
172 ImGui::PopID();
173 }
174
175 ImGui::EndChild();
176 }
177
179 const auto& theme = AgentUI::GetTheme();
180 auto& room = (*rooms_)[*current_room_id_];
181 auto& chests = room.GetChests();
182
183 if (selected_chest_index_ < 0 ||
184 selected_chest_index_ >= static_cast<int>(chests.size())) {
185 ImGui::TextColored(theme.text_secondary_gray,
186 ICON_MD_INFO " Select a chest to edit");
187 return;
188 }
189
190 auto& chest = chests[selected_chest_index_];
191 const auto& item_names = zelda3::Zelda3Labels::GetItemNames();
192
193 ImGui::Text(ICON_MD_EDIT " Editing Chest #%d", selected_chest_index_ + 1);
194
195 // Chest type (size)
196 ImGui::Text(tr("Type:"));
197 ImGui::SameLine();
198 bool is_big = chest.size;
199 if (ImGui::RadioButton(tr("Small"), !is_big)) {
200 chest.size = false;
201 room.MarkChestsDirty();
204 }
205 }
206 ImGui::SameLine();
207 if (ImGui::RadioButton(tr("Big"), is_big)) {
208 chest.size = true;
209 room.MarkChestsDirty();
212 }
213 }
214
215 // Item selector dropdown
216 ImGui::Text(tr("Item:"));
217 ImGui::SameLine();
218
219 std::string current_item =
220 (chest.id < item_names.size())
221 ? absl::StrFormat("[%02X] %s", chest.id,
222 item_names[chest.id].c_str())
223 : absl::StrFormat("[%02X] Unknown", chest.id);
224
225 ImGui::SetNextItemWidth(-1);
226 if (ImGui::BeginCombo("##ItemSelect", current_item.c_str())) {
227 // Search filter
228 static char search_buf[64] = "";
229 ImGui::InputTextWithHint("##Search", "Search items...", search_buf,
230 sizeof(search_buf));
231 ImGui::Separator();
232
233 for (size_t i = 0; i < item_names.size(); ++i) {
234 // Apply search filter
235 if (search_buf[0] != '\0') {
236 std::string name_lower = item_names[i];
237 std::string filter_lower = search_buf;
238 for (auto& c : name_lower) {
239 c = static_cast<char>(tolower(static_cast<unsigned char>(c)));
240 }
241 for (auto& c : filter_lower) {
242 c = static_cast<char>(tolower(static_cast<unsigned char>(c)));
243 }
244 if (name_lower.find(filter_lower) == std::string::npos) {
245 continue;
246 }
247 }
248
249 std::string item_label = absl::StrFormat(
250 "[%02X] %s", static_cast<int>(i), item_names[i].c_str());
251 bool is_selected = (chest.id == static_cast<uint8_t>(i));
252
253 if (ImGui::Selectable(item_label.c_str(), is_selected)) {
254 chest.id = static_cast<uint8_t>(i);
255 room.MarkChestsDirty();
258 }
259 }
260
261 if (is_selected) {
262 ImGui::SetItemDefaultFocus();
263 }
264 }
265
266 ImGui::EndCombo();
267 }
268
269 // Delete button
270 ImGui::Spacing();
271 {
272 gui::StyleColorGuard del_guard(ImGuiCol_Button, theme.status_error);
273 if (ImGui::Button(ICON_MD_DELETE " Delete Chest")) {
274 chests.erase(chests.begin() + selected_chest_index_);
275 room.MarkChestsDirty();
279 }
280 }
281 }
282 }
283
284 int* current_room_id_ = nullptr;
287
288 // Selection state
290
291 std::function<void(int, int)> chest_modified_callback_;
292};
293
294} // namespace editor
295} // namespace yaze
296
297#endif // YAZE_APP_EDITOR_DUNGEON_PANELS_CHEST_EDITOR_PANEL_H_
WindowContent for managing chest contents in dungeon rooms.
void Draw(bool *p_open) override
Draw the panel content.
void SetCanvasViewer(DungeonCanvasViewer *viewer)
DungeonCanvasViewer * canvas_viewer_
std::function< void(int, int)> chest_modified_callback_
std::string GetDisplayName() const override
Human-readable name shown in menus and title bars.
void SetChestModifiedCallback(std::function< void(int, int)> callback)
std::string GetIcon() const override
Material Design icon for this panel.
std::string GetId() const override
Unique identifier for this panel.
int GetPriority() const override
Get display priority for menu ordering.
ChestEditorPanel(int *current_room_id, DungeonRoomStore *rooms, DungeonCanvasViewer *canvas_viewer=nullptr)
std::string GetEditorCategory() const override
Editor category this panel belongs to.
Base interface for all logical window content components.
RAII guard for ImGui style colors.
Definition style_guard.h:27
#define ICON_MD_INFO
Definition icons.h:993
#define ICON_MD_WARNING
Definition icons.h:2123
#define ICON_MD_EDIT
Definition icons.h:645
#define ICON_MD_ADD
Definition icons.h:86
#define ICON_MD_INVENTORY
Definition icons.h:1011
#define ICON_MD_DELETE
Definition icons.h:530
#define ICON_MD_INVENTORY_2
Definition icons.h:1012
const AgentUITheme & GetTheme()
Treasure chest.
Definition zelda.h:425
static const std::vector< std::string > & GetItemNames()