yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
overworld_item_list_view.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <cctype>
5#include <string>
6#include <vector>
7
8#include "absl/strings/str_format.h"
11#include "imgui/imgui.h"
13
14namespace yaze::editor {
15namespace {
16
17bool ItemIdentityMatches(const zelda3::OverworldItem& lhs,
18 const zelda3::OverworldItem& rhs) {
19 return lhs.id_ == rhs.id_ && lhs.room_map_id_ == rhs.room_map_id_ &&
20 lhs.x_ == rhs.x_ && lhs.y_ == rhs.y_ && lhs.game_x_ == rhs.game_x_ &&
21 lhs.game_y_ == rhs.game_y_ && lhs.bg2_ == rhs.bg2_;
22}
23
24std::string ToLower(std::string value) {
25 std::transform(
26 value.begin(), value.end(), value.begin(),
27 [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
28 return value;
29}
30
31std::string ItemName(const zelda3::OverworldItem& item) {
32 if (item.id_ < zelda3::kSecretItemNames.size()) {
33 return zelda3::kSecretItemNames[item.id_];
34 }
35 return absl::StrFormat("Item 0x%02X", static_cast<int>(item.id_));
36}
37
38bool MatchesItemFilter(const zelda3::OverworldItem& item,
39 const std::string& lowered_filter) {
40 if (lowered_filter.empty()) {
41 return true;
42 }
43
44 const std::string name = ToLower(ItemName(item));
45 const std::string id_hex =
46 absl::StrFormat("%02x", static_cast<int>(item.id_));
47 const std::string map_hex =
48 absl::StrFormat("%02x", static_cast<int>(item.room_map_id_));
49 const std::string pos = absl::StrFormat("%d,%d", item.x_, item.y_);
50
51 return name.find(lowered_filter) != std::string::npos ||
52 id_hex.find(lowered_filter) != std::string::npos ||
53 map_hex.find(lowered_filter) != std::string::npos ||
54 pos.find(lowered_filter) != std::string::npos;
55}
56
57} // namespace
58
59void OverworldItemListView::Draw(bool* p_open) {
60 (void)p_open;
61 const auto ctx = CurrentOverworldWindowContext();
62 if (!ctx) {
63 return;
64 }
65 auto* ow_editor = ctx.editor;
66
67 auto* items = ow_editor->overworld().mutable_all_items();
68 if (!items) {
69 ImGui::TextDisabled("No item list available.");
70 return;
71 }
72
73 static char filter_buffer[64] = "";
74 static bool current_world_only = true;
75 static bool current_map_only = false;
76 static int sort_mode = 0; // 0=Map+Pos, 1=ID, 2=Name
77
78 ImGui::InputTextWithHint("##ItemFilter", ICON_MD_FILTER_ALT " Filter items",
79 filter_buffer, sizeof(filter_buffer));
80 ImGui::SameLine();
81 ImGui::Checkbox("World", &current_world_only);
82 ImGui::SameLine();
83 ImGui::Checkbox("Map", &current_map_only);
84
85 const char* sort_modes[] = {"Map + Position", "Item ID", "Name"};
86 ImGui::SetNextItemWidth(180.0f);
87 ImGui::Combo("Sort", &sort_mode, sort_modes, IM_ARRAYSIZE(sort_modes));
88
89 const std::string lowered_filter = ToLower(std::string(filter_buffer));
90 std::vector<size_t> filtered_indices;
91 filtered_indices.reserve(items->size());
92
93 const int current_map = ow_editor->current_map_id();
94 const int current_world = ow_editor->current_world_id();
95
96 for (size_t i = 0; i < items->size(); ++i) {
97 const auto& item = items->at(i);
98 if (item.deleted) {
99 continue;
100 }
101 if (current_world_only && (item.room_map_id_ / 0x40) != current_world) {
102 continue;
103 }
104 if (current_map_only && item.room_map_id_ != current_map) {
105 continue;
106 }
107 if (!MatchesItemFilter(item, lowered_filter)) {
108 continue;
109 }
110 filtered_indices.push_back(i);
111 }
112
113 auto sort_by_map_pos = [&](size_t lhs, size_t rhs) {
114 const auto& a = items->at(lhs);
115 const auto& b = items->at(rhs);
116 if (a.room_map_id_ != b.room_map_id_) {
117 return a.room_map_id_ < b.room_map_id_;
118 }
119 if (a.y_ != b.y_) {
120 return a.y_ < b.y_;
121 }
122 if (a.x_ != b.x_) {
123 return a.x_ < b.x_;
124 }
125 return a.id_ < b.id_;
126 };
127 auto sort_by_id = [&](size_t lhs, size_t rhs) {
128 const auto& a = items->at(lhs);
129 const auto& b = items->at(rhs);
130 if (a.id_ != b.id_) {
131 return a.id_ < b.id_;
132 }
133 return sort_by_map_pos(lhs, rhs);
134 };
135 auto sort_by_name = [&](size_t lhs, size_t rhs) {
136 const auto& a = items->at(lhs);
137 const auto& b = items->at(rhs);
138 const std::string name_a = ItemName(a);
139 const std::string name_b = ItemName(b);
140 if (name_a != name_b) {
141 return name_a < name_b;
142 }
143 return sort_by_map_pos(lhs, rhs);
144 };
145
146 switch (sort_mode) {
147 case 1:
148 std::sort(filtered_indices.begin(), filtered_indices.end(), sort_by_id);
149 break;
150 case 2:
151 std::sort(filtered_indices.begin(), filtered_indices.end(), sort_by_name);
152 break;
153 case 0:
154 default:
155 std::sort(filtered_indices.begin(), filtered_indices.end(),
156 sort_by_map_pos);
157 break;
158 }
159
160 const zelda3::OverworldItem* selected_item = ow_editor->GetSelectedItem();
161 const bool has_selection = selected_item != nullptr;
162
163 if (!has_selection) {
164 ImGui::BeginDisabled();
165 }
166 if (ImGui::Button(ICON_MD_CONTENT_COPY " Duplicate", ImVec2(120.0f, 0.0f))) {
167 ow_editor->DuplicateSelectedItem();
168 selected_item = ow_editor->GetSelectedItem();
169 }
170 ImGui::SameLine();
171 if (ImGui::Button(ICON_MD_DELETE " Delete", ImVec2(96.0f, 0.0f))) {
172 ow_editor->DeleteSelectedItem();
173 selected_item = ow_editor->GetSelectedItem();
174 }
175 if (!has_selection) {
176 ImGui::EndDisabled();
177 }
178
179 ImGui::SameLine();
180 if (selected_item) {
181 ImGui::TextDisabled("Selected: 0x%02X @ (%d,%d)",
182 static_cast<int>(selected_item->id_), selected_item->x_,
183 selected_item->y_);
184 } else {
185 ImGui::TextDisabled("No item selected");
186 }
187
188 ImGui::TextDisabled(
189 "Shortcuts: Ctrl+D duplicate, arrows nudge, Shift+arrows nudge by 16px");
190 ImGui::TextDisabled("Total: %zu | Visible: %zu", items->size(),
191 filtered_indices.size());
192
193 if (ImGui::BeginTable("##OverworldItemListTable", 6,
194 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg |
195 ImGuiTableFlags_Resizable |
196 ImGuiTableFlags_SizingStretchProp |
197 ImGuiTableFlags_ScrollY,
198 ImVec2(0.0f, 0.0f))) {
199 ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 30.0f);
200 ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed, 42.0f);
201 ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch, 1.0f);
202 ImGui::TableSetupColumn("Map", ImGuiTableColumnFlags_WidthFixed, 54.0f);
203 ImGui::TableSetupColumn("World XY", ImGuiTableColumnFlags_WidthFixed,
204 118.0f);
205 ImGui::TableSetupColumn("Game XY", ImGuiTableColumnFlags_WidthFixed, 86.0f);
206 ImGui::TableHeadersRow();
207
208 for (size_t visible_row = 0; visible_row < filtered_indices.size();
209 ++visible_row) {
210 const size_t index = filtered_indices[visible_row];
211 const auto& item = items->at(index);
212 const bool is_selected =
213 selected_item && ItemIdentityMatches(item, *selected_item);
214
215 ImGui::TableNextRow();
216
217 ImGui::TableNextColumn();
218 if (ImGui::SmallButton(
219 absl::StrFormat("%s##select_item_%zu",
220 is_selected ? ICON_MD_RADIO_BUTTON_CHECKED
222 index)
223 .c_str())) {
224 ow_editor->SelectItemByIdentity(item);
225 selected_item = ow_editor->GetSelectedItem();
226 }
227
228 ImGui::TableNextColumn();
229 ImGui::Text("0x%02X", static_cast<int>(item.id_));
230
231 ImGui::TableNextColumn();
232 ImGui::TextUnformatted(ItemName(item).c_str());
233
234 ImGui::TableNextColumn();
235 if (item.room_map_id_ == current_map) {
236 ImGui::TextColored(ImVec4(0.45f, 0.85f, 0.50f, 1.0f), "%02X",
237 static_cast<int>(item.room_map_id_));
238 } else {
239 ImGui::Text("%02X", static_cast<int>(item.room_map_id_));
240 }
241
242 ImGui::TableNextColumn();
243 ImGui::Text("%4d, %4d", item.x_, item.y_);
244
245 ImGui::TableNextColumn();
246 ImGui::Text("%2d, %2d", item.game_x_, item.game_y_);
247 }
248
249 ImGui::EndTable();
250 }
251}
252
254
255} // namespace yaze::editor
Filterable list view for overworld items with quick selection/actions.
void Draw(bool *p_open) override
Draw the panel content.
#define ICON_MD_RADIO_BUTTON_CHECKED
Definition icons.h:1548
#define ICON_MD_DELETE
Definition icons.h:530
#define ICON_MD_CONTENT_COPY
Definition icons.h:465
#define ICON_MD_RADIO_BUTTON_UNCHECKED
Definition icons.h:1551
#define ICON_MD_FILTER_ALT
Definition icons.h:761
Editors are the view controllers for the application.
OverworldWindowContext CurrentOverworldWindowContext()
const std::vector< std::string > kSecretItemNames
#define REGISTER_PANEL(PanelClass)
Auto-registration macro for panels with default constructors.