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; }
53 std::string GetWorkflowGroup() const override {
54 return "Editors";
55 } // After sprite editor
56
57 // ==========================================================================
58 // WindowContent Drawing
59 // ==========================================================================
60
61 void Draw(bool* p_open) override {
62 if (!current_room_id_ || !rooms_) {
63 ImGui::TextDisabled(tr("No room data available"));
64 return;
65 }
66
67 if (*current_room_id_ < 0 ||
68 *current_room_id_ >= static_cast<int>(rooms_->size())) {
69 ImGui::TextDisabled(tr("No room selected"));
70 return;
71 }
72
74 ImGui::Separator();
76 }
77
78 // ==========================================================================
79 // Panel-Specific Methods
80 // ==========================================================================
81
83
84 void SetChestModifiedCallback(std::function<void(int, int)> callback) {
85 chest_modified_callback_ = std::move(callback);
86 }
87
88 private:
90 const auto& theme = AgentUI::GetTheme();
91 auto& room = (*rooms_)[*current_room_id_];
92 auto& chests = room.GetChests();
93
94 // Header with count and limit warning
95 int chest_count = static_cast<int>(chests.size());
96 ImVec4 count_color =
97 chest_count > 6 ? theme.text_error_red : theme.text_primary;
98 ImGui::TextColored(count_color, ICON_MD_INVENTORY_2 " Chests: %d/6",
99 chest_count);
100
101 if (chest_count > 6) {
102 ImGui::SameLine();
103 ImGui::TextColored(theme.text_warning_yellow, ICON_MD_WARNING);
104 if (ImGui::IsItemHovered()) {
105 ImGui::SetTooltip(
106 tr("Room exceeds chest limit (6 max)!\n"
107 "This may cause game crashes."));
108 }
109 }
110
111 // Add chest button
112 ImGui::SameLine();
113 if (ImGui::SmallButton(ICON_MD_ADD " Add")) {
114 // Add new chest with default values
115 zelda3::chest_data new_chest;
116 new_chest.id = 0; // Default item: nothing
117 new_chest.size = false; // Small chest
118 chests.push_back(new_chest);
119 room.MarkChestsDirty();
120 selected_chest_index_ = static_cast<int>(chests.size()) - 1;
123 }
124 }
125 if (ImGui::IsItemHovered()) {
126 ImGui::SetTooltip(tr("Add new chest to room"));
127 }
128
129 // Chest list
130 if (chests.empty()) {
131 ImGui::TextColored(theme.text_secondary_gray,
132 ICON_MD_INFO " No chests in this room");
133 return;
134 }
135
136 const auto& item_names = zelda3::Zelda3Labels::GetItemNames();
137 float list_height =
138 std::min(200.0f, ImGui::GetContentRegionAvail().y * 0.5f);
139 ImGui::BeginChild("##ChestList", ImVec2(0, list_height), true);
140
141 for (size_t i = 0; i < chests.size(); ++i) {
142 const auto& chest = chests[i];
143 bool is_selected = (selected_chest_index_ == static_cast<int>(i));
144
145 ImGui::PushID(static_cast<int>(i));
146
147 // Chest icon based on size
148 const char* size_icon =
150 const char* size_label = chest.size ? "Big" : "Small";
151
152 // Get item name
153 std::string item_name =
154 (chest.id < item_names.size())
155 ? item_names[chest.id]
156 : absl::StrFormat("Unknown (0x%02X)", chest.id);
157
158 // Selectable list item
159 std::string label = absl::StrFormat("%s [%zu] %s: %s", size_icon, i + 1,
160 size_label, item_name.c_str());
161
162 if (ImGui::Selectable(label.c_str(), is_selected)) {
163 selected_chest_index_ = static_cast<int>(i);
164 }
165
166 // Highlight with theme color
167 if (is_selected) {
168 ImVec2 min = ImGui::GetItemRectMin();
169 ImVec2 max = ImGui::GetItemRectMax();
170 ImU32 sel_color =
171 ImGui::ColorConvertFloat4ToU32(theme.dungeon_selection_primary);
172 ImGui::GetWindowDrawList()->AddRect(min, max, sel_color, 0.0f, 0, 2.0f);
173 }
174
175 ImGui::PopID();
176 }
177
178 ImGui::EndChild();
179 }
180
182 const auto& theme = AgentUI::GetTheme();
183 auto& room = (*rooms_)[*current_room_id_];
184 auto& chests = room.GetChests();
185
186 if (selected_chest_index_ < 0 ||
187 selected_chest_index_ >= static_cast<int>(chests.size())) {
188 ImGui::TextColored(theme.text_secondary_gray,
189 ICON_MD_INFO " Select a chest to edit");
190 return;
191 }
192
193 auto& chest = chests[selected_chest_index_];
194 const auto& item_names = zelda3::Zelda3Labels::GetItemNames();
195
196 ImGui::Text(ICON_MD_EDIT " Editing Chest #%d", selected_chest_index_ + 1);
197
198 // Chest type (size)
199 ImGui::Text(tr("Type:"));
200 ImGui::SameLine();
201 bool is_big = chest.size;
202 if (ImGui::RadioButton(tr("Small"), !is_big)) {
203 chest.size = false;
204 room.MarkChestsDirty();
207 }
208 }
209 ImGui::SameLine();
210 if (ImGui::RadioButton(tr("Big"), is_big)) {
211 chest.size = true;
212 room.MarkChestsDirty();
215 }
216 }
217
218 // Item selector dropdown
219 ImGui::Text(tr("Item:"));
220 ImGui::SameLine();
221
222 std::string current_item =
223 (chest.id < item_names.size())
224 ? absl::StrFormat("[%02X] %s", chest.id,
225 item_names[chest.id].c_str())
226 : absl::StrFormat("[%02X] Unknown", chest.id);
227
228 ImGui::SetNextItemWidth(-1);
229 if (ImGui::BeginCombo("##ItemSelect", current_item.c_str())) {
230 // Search filter
231 static char search_buf[64] = "";
232 ImGui::InputTextWithHint("##Search", "Search items...", search_buf,
233 sizeof(search_buf));
234 ImGui::Separator();
235
236 for (size_t i = 0; i < item_names.size(); ++i) {
237 // Apply search filter
238 if (search_buf[0] != '\0') {
239 std::string name_lower = item_names[i];
240 std::string filter_lower = search_buf;
241 for (auto& c : name_lower) {
242 c = static_cast<char>(tolower(static_cast<unsigned char>(c)));
243 }
244 for (auto& c : filter_lower) {
245 c = static_cast<char>(tolower(static_cast<unsigned char>(c)));
246 }
247 if (name_lower.find(filter_lower) == std::string::npos) {
248 continue;
249 }
250 }
251
252 std::string item_label = absl::StrFormat(
253 "[%02X] %s", static_cast<int>(i), item_names[i].c_str());
254 bool is_selected = (chest.id == static_cast<uint8_t>(i));
255
256 if (ImGui::Selectable(item_label.c_str(), is_selected)) {
257 chest.id = static_cast<uint8_t>(i);
258 room.MarkChestsDirty();
261 }
262 }
263
264 if (is_selected) {
265 ImGui::SetItemDefaultFocus();
266 }
267 }
268
269 ImGui::EndCombo();
270 }
271
272 // Delete button
273 ImGui::Spacing();
274 {
275 gui::StyleColorGuard del_guard(ImGuiCol_Button, theme.status_error);
276 if (ImGui::Button(ICON_MD_DELETE " Delete Chest")) {
277 chests.erase(chests.begin() + selected_chest_index_);
278 room.MarkChestsDirty();
282 }
283 }
284 }
285 }
286
287 int* current_room_id_ = nullptr;
290
291 // Selection state
293
294 std::function<void(int, int)> chest_modified_callback_;
295};
296
297} // namespace editor
298} // namespace yaze
299
300#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 GetWorkflowGroup() const override
Optional workflow group for hack-centric actions.
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()