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
10#include "absl/strings/str_format.h"
14#include "app/gui/core/icons.h"
16#include "imgui/imgui.h"
17#include "zelda3/dungeon/room.h"
19
20namespace yaze {
21namespace editor {
22
35 public:
36 ChestEditorPanel(int* current_room_id,
37 std::array<zelda3::Room, 0x128>* rooms,
38 DungeonCanvasViewer* canvas_viewer = nullptr)
39 : current_room_id_(current_room_id),
40 rooms_(rooms),
41 canvas_viewer_(canvas_viewer) {}
42
43 // ==========================================================================
44 // EditorPanel Identity
45 // ==========================================================================
46
47 std::string GetId() const override { return "dungeon.chest_editor"; }
48 std::string GetDisplayName() const override { return "Chest Editor"; }
49 std::string GetIcon() const override { return ICON_MD_INVENTORY_2; }
50 std::string GetEditorCategory() const override { return "Dungeon"; }
51 int GetPriority() const override { return 70; } // After sprite editor
52
53 // ==========================================================================
54 // EditorPanel Drawing
55 // ==========================================================================
56
57 void Draw(bool* p_open) override {
58 if (!current_room_id_ || !rooms_) {
59 ImGui::TextDisabled("No room data available");
60 return;
61 }
62
63 if (*current_room_id_ < 0 ||
64 *current_room_id_ >= static_cast<int>(rooms_->size())) {
65 ImGui::TextDisabled("No room selected");
66 return;
67 }
68
70 ImGui::Separator();
72 }
73
74 // ==========================================================================
75 // Panel-Specific Methods
76 // ==========================================================================
77
79 canvas_viewer_ = viewer;
80 }
81
82 void SetChestModifiedCallback(std::function<void(int, int)> callback) {
83 chest_modified_callback_ = std::move(callback);
84 }
85
86 private:
88 const auto& theme = AgentUI::GetTheme();
89 auto& room = (*rooms_)[*current_room_id_];
90 auto& chests = room.GetChests();
91
92 // Header with count and limit warning
93 int chest_count = static_cast<int>(chests.size());
94 ImVec4 count_color =
95 chest_count > 6 ? theme.text_error_red : theme.text_primary;
96 ImGui::TextColored(count_color, ICON_MD_INVENTORY_2 " Chests: %d/6",
97 chest_count);
98
99 if (chest_count > 6) {
100 ImGui::SameLine();
101 ImGui::TextColored(theme.text_warning_yellow, ICON_MD_WARNING);
102 if (ImGui::IsItemHovered()) {
103 ImGui::SetTooltip("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 selected_chest_index_ = static_cast<int>(chests.size()) - 1;
119 }
120 }
121 if (ImGui::IsItemHovered()) {
122 ImGui::SetTooltip("Add new chest to room");
123 }
124
125 // Chest list
126 if (chests.empty()) {
127 ImGui::TextColored(theme.text_secondary_gray,
128 ICON_MD_INFO " No chests in this room");
129 return;
130 }
131
132 const auto& item_names = zelda3::Zelda3Labels::GetItemNames();
133 float list_height = std::min(200.0f, ImGui::GetContentRegionAvail().y * 0.5f);
134 ImGui::BeginChild("##ChestList", ImVec2(0, list_height), true);
135
136 for (size_t i = 0; i < chests.size(); ++i) {
137 const auto& chest = chests[i];
138 bool is_selected = (selected_chest_index_ == static_cast<int>(i));
139
140 ImGui::PushID(static_cast<int>(i));
141
142 // Chest icon based on size
143 const char* size_icon = chest.size ? ICON_MD_INVENTORY_2 : ICON_MD_INVENTORY;
144 const char* size_label = chest.size ? "Big" : "Small";
145
146 // Get item name
147 std::string item_name = (chest.id < item_names.size())
148 ? item_names[chest.id]
149 : absl::StrFormat("Unknown (0x%02X)", chest.id);
150
151 // Selectable list item
152 std::string label = absl::StrFormat("%s [%zu] %s: %s",
153 size_icon, i + 1, size_label, item_name.c_str());
154
155 if (ImGui::Selectable(label.c_str(), is_selected)) {
156 selected_chest_index_ = static_cast<int>(i);
157 }
158
159 // Highlight with theme color
160 if (is_selected) {
161 ImVec2 min = ImGui::GetItemRectMin();
162 ImVec2 max = ImGui::GetItemRectMax();
163 ImU32 sel_color = ImGui::ColorConvertFloat4ToU32(
164 theme.dungeon_selection_primary);
165 ImGui::GetWindowDrawList()->AddRect(min, max, sel_color, 0.0f, 0, 2.0f);
166 }
167
168 ImGui::PopID();
169 }
170
171 ImGui::EndChild();
172 }
173
175 const auto& theme = AgentUI::GetTheme();
176 auto& room = (*rooms_)[*current_room_id_];
177 auto& chests = room.GetChests();
178
179 if (selected_chest_index_ < 0 ||
180 selected_chest_index_ >= static_cast<int>(chests.size())) {
181 ImGui::TextColored(theme.text_secondary_gray,
182 ICON_MD_INFO " Select a chest to edit");
183 return;
184 }
185
186 auto& chest = chests[selected_chest_index_];
187 const auto& item_names = zelda3::Zelda3Labels::GetItemNames();
188
189 ImGui::Text(ICON_MD_EDIT " Editing Chest #%d", selected_chest_index_ + 1);
190
191 // Chest type (size)
192 ImGui::Text("Type:");
193 ImGui::SameLine();
194 bool is_big = chest.size;
195 if (ImGui::RadioButton("Small", !is_big)) {
196 chest.size = false;
199 }
200 }
201 ImGui::SameLine();
202 if (ImGui::RadioButton("Big", is_big)) {
203 chest.size = true;
206 }
207 }
208
209 // Item selector dropdown
210 ImGui::Text("Item:");
211 ImGui::SameLine();
212
213 std::string current_item = (chest.id < item_names.size())
214 ? absl::StrFormat("[%02X] %s", chest.id, item_names[chest.id].c_str())
215 : absl::StrFormat("[%02X] Unknown", chest.id);
216
217 ImGui::SetNextItemWidth(-1);
218 if (ImGui::BeginCombo("##ItemSelect", current_item.c_str())) {
219 // Search filter
220 static char search_buf[64] = "";
221 ImGui::InputTextWithHint("##Search", "Search items...",
222 search_buf, sizeof(search_buf));
223 ImGui::Separator();
224
225 for (size_t i = 0; i < item_names.size(); ++i) {
226 // Apply search filter
227 if (search_buf[0] != '\0') {
228 std::string name_lower = item_names[i];
229 std::string filter_lower = search_buf;
230 for (auto& c : name_lower) {
231 c = static_cast<char>(tolower(static_cast<unsigned char>(c)));
232 }
233 for (auto& c : filter_lower) {
234 c = static_cast<char>(tolower(static_cast<unsigned char>(c)));
235 }
236 if (name_lower.find(filter_lower) == std::string::npos) {
237 continue;
238 }
239 }
240
241 std::string item_label = absl::StrFormat("[%02X] %s",
242 static_cast<int>(i), item_names[i].c_str());
243 bool is_selected = (chest.id == static_cast<uint8_t>(i));
244
245 if (ImGui::Selectable(item_label.c_str(), is_selected)) {
246 chest.id = static_cast<uint8_t>(i);
249 }
250 }
251
252 if (is_selected) {
253 ImGui::SetItemDefaultFocus();
254 }
255 }
256
257 ImGui::EndCombo();
258 }
259
260 // Delete button
261 ImGui::Spacing();
262 {
263 gui::StyleColorGuard del_guard(ImGuiCol_Button, theme.status_error);
264 if (ImGui::Button(ICON_MD_DELETE " Delete Chest")) {
265 chests.erase(chests.begin() + selected_chest_index_);
269 }
270 }
271 }
272 }
273
274 int* current_room_id_ = nullptr;
275 std::array<zelda3::Room, 0x128>* rooms_ = nullptr;
277
278 // Selection state
280
281 std::function<void(int, int)> chest_modified_callback_;
282};
283
284} // namespace editor
285} // namespace yaze
286
287#endif // YAZE_APP_EDITOR_DUNGEON_PANELS_CHEST_EDITOR_PANEL_H_
EditorPanel for managing chest contents in dungeon rooms.
void Draw(bool *p_open) override
Draw the panel content.
std::array< zelda3::Room, 0x128 > * rooms_
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, std::array< zelda3::Room, 0x128 > *rooms, DungeonCanvasViewer *canvas_viewer=nullptr)
std::string GetEditorCategory() const override
Editor category this panel belongs to.
Base interface for all logical panel 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()