yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
door_editor_content.cc
Go to the documentation of this file.
2#include "util/i18n/tr.h"
3
4#include <algorithm>
5#include <string>
6
7#include "absl/strings/str_format.h"
10#include "app/gui/core/input.h"
13#include "imgui/imgui.h"
14
15namespace yaze::editor {
16
17namespace {
18
19ImVec4 DoorTypeAccent(const AgentUITheme& theme, zelda3::DoorType type) {
20 const int type_val = static_cast<int>(type);
21 if (type_val <= 0x12) {
22 return theme.dungeon_object_door;
23 }
24 if (type_val <= 0x1E) {
25 return theme.status_warning;
26 }
27 return theme.status_success;
28}
29
30// Semantic icon prefix used by both the placement picker and the "Selected
31// Door" swap combo. Keeps supported types scannable by category.
32const char* DoorTypeIcon(zelda3::DoorType type) {
33 switch (type) {
38 return ICON_MD_DOOR_FRONT;
52 return ICON_MD_VIEW_DAY;
54 return ICON_MD_VISIBILITY;
58 return ICON_MD_KEY;
63 return ICON_MD_STAIRS;
65 return ICON_MD_BOLT;
69 return ICON_MD_WHATSHOT;
71 return ICON_MD_WATER_DROP;
73 return ICON_MD_FLAG;
76 return ICON_MD_SWAP_HORIZ;
77 default:
79 }
80}
81
83 switch (type) {
85 return "Normal";
87 return "Lower";
89 return "Exit Low";
91 return "Cave";
93 return "2-Side";
95 return "Eye";
97 return "S-Key";
99 return "B-Key";
101 return "Up";
103 return "Down";
105 return "Up Low";
107 return "Down Low";
109 return "Dash";
111 return "Bomb";
113 return "Bomb Exit";
115 return "B-Key Lock";
117 return "Blast";
119 return "Curtain";
121 return "Bottom";
123 return "Top";
125 return "Exit";
127 return "Exit Low";
129 return "Lit Low";
131 return "Water";
133 return "Marker";
135 return "Layer";
137 return "Swap";
139 return "1-Side";
141 return "2-Side L";
143 return "Explicit";
145 return "Bottom L";
147 return "Top L";
148 }
149 return "Door";
150}
151
152} // namespace
153
160
168
172
173void DoorEditorContent::Draw(bool* p_open) {
174 (void)p_open;
176 gui::AutoWidgetScope automation_scope("Dungeon/DoorEditor");
177
178 const auto& theme = AgentUI::GetTheme();
179 static constexpr auto kDoorTypes = zelda3::GetPlaceableDoorTypes();
180
181 if (ResolveCanvasViewer() &&
185 gui::SectionHeader(ICON_MD_SELECT_ALL, "Selected Door", theme.text_info);
187 ImGui::Button(ICON_MD_OPEN_IN_NEW " Inspect Selected", ImVec2(-1, 0))) {
189 }
190 ImGui::Spacing();
191 }
192
193 gui::SectionHeader(ICON_MD_DOOR_FRONT, "Door Styles", theme.text_info);
194 ImGui::TextColored(theme.text_secondary_gray,
195 tr("Select a door style, then click a wall in the room "
196 "canvas to place it."));
197
199 ImGui::TextColored(
200 theme.status_warning, ICON_MD_PLACE " Active: %s",
201 std::string(zelda3::GetDoorTypeName(selected_door_type_)).c_str());
202 ImGui::SameLine();
203 if (ImGui::SmallButton(ICON_MD_CANCEL " Cancel")) {
205 }
206 }
207
208 constexpr float kDoorCardHeight = 48.0f;
209 constexpr float kDoorCardSpacing = 6.0f;
210 constexpr float kMinDoorCardWidth = 92.0f;
211 const float panel_width = ImGui::GetContentRegionAvail().x;
212 const int items_per_row =
213 std::max(2, static_cast<int>((panel_width + kDoorCardSpacing) /
214 (kMinDoorCardWidth + kDoorCardSpacing)));
215 const float card_width = std::max(
216 kMinDoorCardWidth,
217 (panel_width - (items_per_row - 1) * kDoorCardSpacing) / items_per_row);
218
219 ImGui::BeginChild("##DoorTypeGrid", ImVec2(0, 150), true,
220 ImGuiWindowFlags_AlwaysVerticalScrollbar);
221
222 int col = 0;
223 for (size_t i = 0; i < kDoorTypes.size(); ++i) {
224 const auto door_type = kDoorTypes[i];
225 const bool is_selected = selected_door_type_ == door_type;
226 const int type_val = static_cast<int>(door_type);
227 ImGui::PushID(static_cast<int>(i));
228
229 ImVec4 button_color = DoorTypeAccent(theme, door_type);
230 if (is_selected) {
231 button_color.x = std::min(1.0f, button_color.x + 0.2f);
232 button_color.y = std::min(1.0f, button_color.y + 0.2f);
233 button_color.z = std::min(1.0f, button_color.z + 0.2f);
234 }
235
236 gui::StyleColorGuard btn_colors({
237 {ImGuiCol_Button, button_color},
238 {ImGuiCol_ButtonHovered,
239 ImVec4(button_color.x + 0.1f, button_color.y + 0.1f,
240 button_color.z + 0.1f, 1.0f)},
241 {ImGuiCol_ButtonActive,
242 ImVec4(button_color.x + 0.2f, button_color.y + 0.2f,
243 button_color.z + 0.2f, 1.0f)},
244 });
245
246 const std::string label =
247 absl::StrFormat("%s %02X\n%s", DoorTypeIcon(door_type), type_val,
248 ShortDoorTypeLabel(door_type).c_str());
249 if (ImGui::Button(label.c_str(), ImVec2(card_width, kDoorCardHeight))) {
250 selected_door_type_ = door_type;
252 if (ResolveCanvasViewer()) {
254 true, selected_door_type_);
255 }
256 }
257
258 if (ImGui::IsItemHovered()) {
259 ImGui::SetTooltip(tr("%s (0x%02X)\nClick to select for placement"),
260 std::string(zelda3::GetDoorTypeName(door_type)).c_str(),
261 type_val);
262 }
263
264 if (is_selected) {
265 ImVec2 min = ImGui::GetItemRectMin();
266 ImVec2 max = ImGui::GetItemRectMax();
267 ImGui::GetWindowDrawList()->AddRect(min, max, IM_COL32(255, 255, 0, 255),
268 0.0f, 0, 2.0f);
269 }
270
271 ImGui::PopID();
272 col++;
273 if (col < items_per_row && i < kDoorTypes.size() - 1) {
274 ImGui::SameLine();
275 } else {
276 col = 0;
277 }
278 }
279
280 ImGui::EndChild();
281
282 if (!rooms_ || current_room_id_ < 0 || current_room_id_ >= 296) {
283 return;
284 }
285
286 const auto& room = (*rooms_)[current_room_id_];
287 const auto& doors = room.GetDoors();
288
289 ImGui::Spacing();
290 gui::SectionHeader(ICON_MD_LIST, "Doors In Room", theme.text_info);
291 if (doors.empty()) {
292 ImGui::TextColored(theme.text_secondary_gray,
293 ICON_MD_INFO " No doors in this room");
294 return;
295 }
296
297 int selected_door_index = -1;
298 if (ResolveCanvasViewer() &&
300 const auto selected_entity =
302 if (selected_entity.type == EntityType::Door) {
303 selected_door_index = static_cast<int>(selected_entity.index);
304 }
305 }
306
307 if (selected_door_index >= 0 &&
308 selected_door_index < static_cast<int>(doors.size())) {
309 uint8_t raw_type = static_cast<uint8_t>(doors[selected_door_index].type);
310 ImGui::AlignTextToFramePadding();
311 ImGui::TextUnformatted(tr("Selected raw type"));
312 ImGui::SameLine();
313 ImGui::SetNextItemWidth(72.0f);
314 const bool type_changed = gui::InputScalarDeferred(
315 "##SelectedDoorRawType", ImGuiDataType_U8, &raw_type, "%02X",
316 ImGuiInputTextFlags_CharsHexadecimal,
317 {reinterpret_cast<uintptr_t>(rooms_),
318 static_cast<uint64_t>(current_room_id_),
319 static_cast<uint64_t>(selected_door_index)});
320 gui::AutoRegisterLastItem("input_scalar", "selected_door_raw_type",
321 "Selected door raw type byte");
322 if (type_changed && canvas_viewer_) {
325 .door_handler()
326 .MutateDoorType(selected_door_index,
327 static_cast<zelda3::DoorType>(raw_type));
328 }
329 }
330
331 ImGui::BeginChild("##DoorList", ImVec2(0, 0), true);
332 for (size_t i = 0; i < doors.size(); ++i) {
333 const auto& door = doors[i];
334 const auto [tile_x, tile_y] = door.GetTileCoords();
335 const std::string type_name(zelda3::GetDoorTypeName(door.type));
336 const std::string dir_name(zelda3::GetDoorDirectionName(door.direction));
337
338 ImGui::PushID(static_cast<int>(i));
339 const std::string row_label = absl::StrFormat("[%zu] %s", i, type_name);
340 const bool row_selected = ImGui::Selectable(
341 row_label.c_str(), selected_door_index == static_cast<int>(i));
342 gui::AutoRegisterLastItem("selectable", absl::StrFormat("door_row_%zu", i),
343 "Select a door in the current dungeon room");
344 if (row_selected) {
345 if (canvas_viewer_) {
347 }
348 }
349 ImGui::SameLine();
350 ImGui::TextColored(theme.text_secondary_gray, "(%s @ %d,%d)",
351 dir_name.c_str(), tile_x, tile_y);
352 ImGui::PopID();
353 }
354 ImGui::EndChild();
355}
356
357} // namespace yaze::editor
DungeonCanvasViewer * ResolveCanvasViewer()
std::function< DungeonCanvasViewer *()> canvas_viewer_provider_
void OnClose() override
Called when panel is hidden.
void Draw(bool *p_open) override
Draw the panel content.
std::function< void()> open_selection_inspector_callback_
bool MutateDoorType(size_t index, zelda3::DoorType new_type)
Change the type of a door in place, re-encoding ROM bytes.
DungeonObjectInteraction & object_interaction()
InteractionCoordinator & entity_coordinator()
Get the interaction coordinator for entity handling.
void SelectEntity(EntityType type, size_t index)
void SetDoorPlacementMode(bool enabled, zelda3::DoorType type=zelda3::DoorType::NormalDoor)
RAII scope that enables automatic widget registration.
RAII guard for ImGui style colors.
Definition style_guard.h:27
#define ICON_MD_VIEW_DAY
Definition icons.h:2088
#define ICON_MD_EXIT_TO_APP
Definition icons.h:699
#define ICON_MD_INFO
Definition icons.h:993
#define ICON_MD_CANCEL
Definition icons.h:364
#define ICON_MD_DOOR_SLIDING
Definition icons.h:614
#define ICON_MD_PLACE
Definition icons.h:1477
#define ICON_MD_SWAP_HORIZ
Definition icons.h:1896
#define ICON_MD_STAIRS
Definition icons.h:1847
#define ICON_MD_VISIBILITY
Definition icons.h:2101
#define ICON_MD_WHATSHOT
Definition icons.h:2153
#define ICON_MD_LIST
Definition icons.h:1094
#define ICON_MD_DOOR_FRONT
Definition icons.h:613
#define ICON_MD_BOLT
Definition icons.h:282
#define ICON_MD_FLAG
Definition icons.h:784
#define ICON_MD_SELECT_ALL
Definition icons.h:1680
#define ICON_MD_KEY
Definition icons.h:1026
#define ICON_MD_OPEN_IN_NEW
Definition icons.h:1354
#define ICON_MD_WATER_DROP
Definition icons.h:2131
const AgentUITheme & GetTheme()
ImVec4 DoorTypeAccent(const AgentUITheme &theme, zelda3::DoorType type)
Editors are the view controllers for the application.
void AutoRegisterLastItem(const std::string &widget_type, const std::string &explicit_label, const std::string &description)
Automatically register the last ImGui item.
void SectionHeader(const char *icon, const char *label, const ImVec4 &color)
bool InputScalarDeferred(const char *label, ImGuiDataType data_type, void *data, const char *format, ImGuiInputTextFlags flags, InputScalarTargetIdentity target_identity)
Definition input.cc:392
DoorType
Door types from ALTTP.
Definition door_types.h:33
@ NormalDoorOneSidedShutter
Normal door (lower layer; with one-sided shutters)
@ TopShutterLower
Top-sided shutter door (lower layer)
@ FancyDungeonExitLower
Fancy dungeon exit (lower layer)
@ FancyDungeonExit
Fancy dungeon exit.
@ SmallKeyDoor
Small key door.
@ ExitLower
Exit (lower layer)
@ SmallKeyStairsDown
Small key stairs (downwards)
@ BombableCaveExit
Bombable cave exit.
@ SmallKeyStairsUp
Small key stairs (upwards)
@ DungeonSwapMarker
Dungeon swap marker.
@ NormalDoor
Normal door (upper layer)
@ BombableDoor
Bombable door.
@ LayerSwapMarker
Layer swap marker.
@ ExplicitRoomDoor
Explicit room door.
@ BottomShutterLower
Bottom-sided shutter door (lower layer)
@ ExplodingWall
Exploding wall.
@ TopSidedShutter
Top-sided shutter door.
@ LitCaveExitLower
Lit cave exit (lower layer)
@ DoubleSidedShutterLower
Double-sided shutter (lower layer)
@ UnopenableBigKeyDoor
Unopenable, double-sided big key door.
@ NormalDoorLower
Normal door (lower layer)
@ BottomSidedShutter
Bottom-sided shutter door.
@ SmallKeyStairsDownLower
Small key stairs (lower layer; downwards)
@ CurtainDoor
Curtain door.
@ WaterfallDoor
Waterfall door.
@ BigKeyDoor
Big key door.
@ EyeWatchDoor
Eye watch door.
@ SmallKeyStairsUpLower
Small key stairs (lower layer; upwards)
@ ExitMarker
Exit marker.
@ DoubleSidedShutter
Double sided shutter door.
constexpr std::array< DoorType, 32 > GetPlaceableDoorTypes()
Get supported door types suitable for editor placement controls.
Definition door_types.h:364
constexpr std::string_view GetDoorDirectionName(DoorDirection dir)
Get human-readable name for door direction.
Definition door_types.h:204
constexpr std::string_view GetDoorTypeName(DoorType type)
Get human-readable name for door type.
Definition door_types.h:110
Centralized theme colors for Agent UI components.
Definition agent_theme.h:19
Automatic widget registration helpers for ImGui Test Engine integration.