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 DrawContent(true);
175}
176
180
182 if (!canvas_viewer_ || !rooms_ || canvas_viewer_->rooms() != rooms_ ||
183 current_room_id_ < 0 ||
184 current_room_id_ >= static_cast<int>(rooms_->size()) ||
186 return false;
187 }
188 const auto* context = canvas_viewer_->object_interaction()
190 .tile_handler()
191 .context();
192 return context && context->current_room_id == current_room_id_;
193}
194
199
200void DoorEditorContent::DrawContent(bool show_room_doors) {
202 const bool can_edit = CanEditRoom();
209 .door_handler()
210 .GetDoorType();
211 }
212 gui::AutoWidgetScope automation_scope("Dungeon/DoorEditor");
213
214 const auto& theme = AgentUI::GetTheme();
215 static constexpr auto kDoorTypes = zelda3::GetPlaceableDoorTypes();
216
217 if (show_room_doors && HasMatchingViewer() &&
220 EntityType::Door) {
221 gui::SectionHeader(ICON_MD_SELECT_ALL, "Selected Door", theme.text_info);
223 ImGui::Button(ICON_MD_OPEN_IN_NEW " Inspect Selected", ImVec2(-1, 0))) {
225 }
226 ImGui::Spacing();
227 }
228
229 gui::SectionHeader(ICON_MD_DOOR_FRONT, "Door Styles", theme.text_info);
230 ImGui::TextWrapped("Select a door style, then click a wall to place it.");
231
233 ImGui::TextColored(
234 theme.status_warning, ICON_MD_PLACE " Active: %s",
235 std::string(zelda3::GetDoorTypeName(selected_door_type_)).c_str());
236 ImGui::SameLine();
237 if (ImGui::SmallButton(ICON_MD_CANCEL " Cancel")) {
239 }
240 }
241
242 constexpr float kDoorCardHeight = 48.0f;
243 constexpr float kDoorCardSpacing = 6.0f;
244 constexpr float kMinDoorCardWidth = 92.0f;
245
246 const float grid_height =
247 show_room_doors ? 150.0f
248 : std::max(1.0f, ImGui::GetContentRegionAvail().y);
249 ImGui::BeginChild("##DoorTypeGrid", ImVec2(0, grid_height), true,
250 ImGuiWindowFlags_AlwaysVerticalScrollbar);
251 ImGui::BeginDisabled(!can_edit);
252 const float panel_width = ImGui::GetContentRegionAvail().x;
253 const int items_per_row =
254 std::max(1, static_cast<int>((panel_width + kDoorCardSpacing) /
255 (kMinDoorCardWidth + kDoorCardSpacing)));
256 const float card_width = std::max(
257 kMinDoorCardWidth,
258 (panel_width - (items_per_row - 1) * kDoorCardSpacing) / items_per_row);
259
260 int col = 0;
261 for (size_t i = 0; i < kDoorTypes.size(); ++i) {
262 const auto door_type = kDoorTypes[i];
263 const bool is_selected = selected_door_type_ == door_type;
264 const int type_val = static_cast<int>(door_type);
265 ImGui::PushID(static_cast<int>(i));
266
267 ImVec4 button_color = DoorTypeAccent(theme, door_type);
268 if (is_selected) {
269 button_color.x = std::min(1.0f, button_color.x + 0.2f);
270 button_color.y = std::min(1.0f, button_color.y + 0.2f);
271 button_color.z = std::min(1.0f, button_color.z + 0.2f);
272 }
273
274 gui::StyleColorGuard btn_colors({
275 {ImGuiCol_Button, button_color},
276 {ImGuiCol_ButtonHovered,
277 ImVec4(button_color.x + 0.1f, button_color.y + 0.1f,
278 button_color.z + 0.1f, 1.0f)},
279 {ImGuiCol_ButtonActive,
280 ImVec4(button_color.x + 0.2f, button_color.y + 0.2f,
281 button_color.z + 0.2f, 1.0f)},
282 });
283
284 const std::string label =
285 absl::StrFormat("%s %02X\n%s", DoorTypeIcon(door_type), type_val,
286 ShortDoorTypeLabel(door_type).c_str());
287 const bool picked =
288 ImGui::Button(label.c_str(), ImVec2(card_width, kDoorCardHeight));
290 absl::StrFormat("place_door_%02X", type_val));
291 if (picked && can_edit) {
292 selected_door_type_ = door_type;
294 if (ResolveCanvasViewer()) {
296 true, selected_door_type_);
297 }
298 }
299
300 if (ImGui::IsItemHovered()) {
301 ImGui::SetTooltip(tr("%s (0x%02X)\nClick to select for placement"),
302 std::string(zelda3::GetDoorTypeName(door_type)).c_str(),
303 type_val);
304 }
305
306 if (is_selected) {
307 ImVec2 min = ImGui::GetItemRectMin();
308 ImVec2 max = ImGui::GetItemRectMax();
309 ImGui::GetWindowDrawList()->AddRect(min, max, IM_COL32(255, 255, 0, 255),
310 0.0f, 0, 2.0f);
311 }
312
313 ImGui::PopID();
314 col++;
315 if (col < items_per_row && i < kDoorTypes.size() - 1) {
316 ImGui::SameLine(0, kDoorCardSpacing);
317 } else {
318 col = 0;
319 }
320 }
321
322 ImGui::EndDisabled();
323 ImGui::EndChild();
324
325 if (!show_room_doors || !rooms_ || current_room_id_ < 0 ||
326 current_room_id_ >= static_cast<int>(rooms_->size())) {
327 return;
328 }
329
330 const auto& room = (*rooms_)[current_room_id_];
331 const auto& doors = room.GetDoors();
332
333 ImGui::Spacing();
334 gui::SectionHeader(ICON_MD_LIST, "Doors In Room", theme.text_info);
335 if (doors.empty()) {
336 ImGui::TextColored(theme.text_secondary_gray,
337 ICON_MD_INFO " No doors in this room");
338 return;
339 }
340
341 int selected_door_index = -1;
342 if (HasMatchingViewer() &&
344 const auto selected_entity =
346 if (selected_entity.type == EntityType::Door) {
347 selected_door_index = static_cast<int>(selected_entity.index);
348 }
349 }
350
351 if (selected_door_index >= 0 &&
352 selected_door_index < static_cast<int>(doors.size())) {
353 ImGui::BeginDisabled(!can_edit);
354 uint8_t raw_type = static_cast<uint8_t>(doors[selected_door_index].type);
355 ImGui::AlignTextToFramePadding();
356 ImGui::TextUnformatted(tr("Selected raw type"));
357 ImGui::SameLine();
358 ImGui::SetNextItemWidth(72.0f);
359 const bool type_changed = gui::InputScalarDeferred(
360 "##SelectedDoorRawType", ImGuiDataType_U8, &raw_type, "%02X",
361 ImGuiInputTextFlags_CharsHexadecimal,
362 {reinterpret_cast<uintptr_t>(rooms_),
363 static_cast<uint64_t>(current_room_id_),
364 static_cast<uint64_t>(selected_door_index)});
365 gui::AutoRegisterLastItem("input_scalar", "selected_door_raw_type",
366 "Selected door raw type byte");
367 ImGui::EndDisabled();
368 if (type_changed && can_edit) {
371 .door_handler()
372 .MutateDoorType(selected_door_index,
373 static_cast<zelda3::DoorType>(raw_type));
374 }
375 }
376
377 ImGui::BeginChild("##DoorList", ImVec2(0, 0), true);
378 for (size_t i = 0; i < doors.size(); ++i) {
379 const auto& door = doors[i];
380 const auto [tile_x, tile_y] = door.GetTileCoords();
381 const std::string type_name(zelda3::GetDoorTypeName(door.type));
382 const std::string dir_name(zelda3::GetDoorDirectionName(door.direction));
383
384 ImGui::PushID(static_cast<int>(i));
385 const std::string row_label = absl::StrFormat("[%zu] %s", i, type_name);
386 const bool row_selected = ImGui::Selectable(
387 row_label.c_str(), selected_door_index == static_cast<int>(i));
388 gui::AutoRegisterLastItem("selectable", absl::StrFormat("door_row_%zu", i),
389 "Select a door in the current dungeon room");
390 if (row_selected) {
391 if (HasMatchingViewer()) {
392 canvas_viewer_->object_interaction().SelectEntity(EntityType::Door, i);
393 }
394 }
395 ImGui::SameLine();
396 ImGui::TextColored(theme.text_secondary_gray, "(%s @ %d,%d)",
397 dir_name.c_str(), tile_x, tile_y);
398 ImGui::PopID();
399 }
400 ImGui::EndChild();
401}
402
403} // namespace yaze::editor
InteractionContext * context() const
Get the interaction context.
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_
void DrawContent(bool show_room_doors)
zelda3::DoorType GetDoorType() const
Get current door type for placement.
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.