8#include "absl/strings/str_format.h"
18#include "imgui/imgui.h"
26 "Nothing",
"Green Rupee",
"Rock",
"Bee",
"Heart (4)",
27 "Bomb (4)",
"Heart",
"Blue Rupee",
"Key",
"Arrow (5)",
28 "Bomb (1)",
"Heart",
"Magic (Small)",
"Full Magic",
"Cucco",
29 "Green Soldier",
"Bush Stal",
"Blue Soldier",
"Landmine",
"Heart",
30 "Fairy",
"Heart",
"Nothing (22)",
"Hole",
"Warp",
31 "Staircase",
"Bombable",
"Switch"};
34 ImGui::TableNextRow();
35 ImGui::TableNextColumn();
36 ImGui::AlignTextToFramePadding();
37 ImGui::TextUnformatted(label);
38 ImGui::TableNextColumn();
39 ImGui::SetNextItemWidth(-1);
43 if (!ImGui::BeginTable(
44 "##EntityProperties", 2,
45 ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_NoPadOuterX)) {
48 ImGui::TableSetupColumn(
"Property", ImGuiTableColumnFlags_WidthFixed, 68.0f);
49 ImGui::TableSetupColumn(
"Value", ImGuiTableColumnFlags_WidthStretch);
57 bool hexadecimal =
false) {
60 const float button_width = ImGui::GetFrameHeight();
61 const float spacing = ImGui::GetStyle().ItemInnerSpacing.x;
62 ImGui::SetNextItemWidth(std::max(
63 1.0f, ImGui::GetContentRegionAvail().x - 2 * (button_width + spacing)));
65 "##Value", ImGuiDataType_S32, &value, hexadecimal ?
"%02X" :
"%d",
66 hexadecimal ? ImGuiInputTextFlags_CharsHexadecimal
67 : ImGuiInputTextFlags_CharsDecimal,
70 ImGui::SameLine(0, spacing);
71 ImGui::BeginDisabled(value <= 0);
72 if (ImGui::Button(
"-", ImVec2(button_width, button_width))) {
78 ImGui::SameLine(0, spacing);
79 ImGui::BeginDisabled(value >= maximum);
80 if (ImGui::Button(
"+", ImVec2(button_width, button_width))) {
88 value = std::clamp(value, 0, maximum);
89 value -= value % step;
94bool BeginChoice(
const char* label,
const char*
id,
const char* preview) {
97 ImGui::BeginCombo((std::string(
"##") +
id).c_str(), preview);
104 const std::function<
void(
int,
size_t)>& jump_to_reciprocal) {
106 if (index >= doors.size()) {
109 auto door = doors[index];
110 ImGui::TextWrapped(
"Door #%zu · %s", index,
112 bool changed =
false;
120 absl::StrFormat(
"%02X %s",
static_cast<int>(type),
122 if (ImGui::Selectable(label.c_str(), door.type == type)) {
132 for (
int i = 0; i < 4; ++i) {
134 if (ImGui::Selectable(
136 door.direction == direction)) {
137 door.direction = direction;
143 auto position_label = [&](uint8_t position) {
146 position, door.direction);
147 return absl::StrFormat(
"%02X · tile %d, %d", position, x, y);
150 position_label(door.position).c_str())) {
155 if (ImGui::Selectable(position_label(i).c_str(), door.position == i)) {
156 door.position =
static_cast<uint8_t
>(i);
164 ImGui::EndDisabled();
167 index, door.type, door.direction, door.position);
177 const auto& sprites =
179 if (index >= sprites.size()) {
184 const auto& sprite = sprites[index];
188 ImGui::TextWrapped(
"Sprite #%zu · %02X %s", index, sprite.id(), name.c_str());
189 if (sprite.IsOverlord()) {
190 ImGui::TextDisabled(
"Overlord");
192 int id = sprite.id(), x = sprite.x(), y = sprite.y();
193 int subtype = sprite.subtype(), layer = sprite.layer(),
194 key = sprite.key_drop();
195 const ImGuiID error_id = ImGui::GetID(
"SpriteEditRejected");
196 bool changed =
false;
204 constexpr const char* kLayers[] = {
"Upper (0)",
"Lower (1)"};
206 layer >= 0 && layer <= 1 ? kLayers[layer] :
"Unknown")) {
207 for (
int i = 0; i < 2; ++i) {
208 if (ImGui::Selectable(kLayers[i], layer == i)) {
213 "selectable", i == 0 ?
"SpriteLayer/Upper" :
"SpriteLayer/Lower");
217 constexpr const char* kKeys[] = {
"None",
"Small key",
"Big key"};
219 key >= 0 && key <= 2 ? kKeys[key] :
"Unknown")) {
220 for (
int i = 0; i < 3; ++i) {
221 if (ImGui::Selectable(kKeys[i], key == i)) {
232 static_cast<uint8_t
>(
id), x, y, subtype, layer, key);
233 ImGui::GetStateStorage()->SetBool(error_id, !validation.ok());
234 if (validation.ok()) {
238 .
UpdateSprite(index,
static_cast<uint8_t
>(
id), x, y, subtype, layer,
242 if (ImGui::GetStateStorage()->GetBool(error_id)) {
244 "Edit not applied: those fields encode a reserved sprite-list marker. "
245 "Change the position, subtype, or layer before trying again.");
248 "Position uses 16-pixel tiles. Drag in the canvas to move.");
254 if (index >= items.size()) {
257 const auto item = items[index];
258 auto type = item.item;
259 int x = item.GetPixelX(), y = item.GetPixelY();
262 ImGui::TextWrapped(
"Pot item #%zu · %02X %s", index, type, name);
264 bool changed =
false;
265 bool position_changed =
false;
268 absl::StrFormat(
"%02X %s", type, name).c_str())) {
270 const auto label = absl::StrFormat(
"%02X %s", i,
kPotItemNames[i]);
271 if (ImGui::Selectable(label.c_str(), type == i)) {
272 type =
static_cast<uint8_t
>(i);
276 absl::StrFormat(
"ItemType/%zu", i));
286 if (position_changed) {
289 }
else if (changed) {
296 "Position snaps to an 8-pixel grid; layer and control flags are "
309 "Pot-item saving is off. Enable Save Pot Items in File > Save Scope > "
310 "Dungeon before saving.");
315 const std::function<
void(
int,
size_t)>& jump_to_reciprocal) {
320 const auto* context =
324 if (!context || context->current_room_id != viewer.
current_room_id()) {
326 "Open this room in the room canvas to edit its entities.");
331 if (selection.HasMixedSelection()) {
332 ImGui::TextWrapped(
"%s. Select one entity to edit its properties.",
336 const auto entity = selection.entity;
338 ImGui::PushID(
"DungeonEntityInspector");
340 ImGui::PushID(
static_cast<int>(entity.index));
342 reinterpret_cast<uintptr_t
>(&viewer),
344 static_cast<uint64_t
>(entity.type)};
345 switch (entity.type) {
346 case EntityType::Door:
347 DrawDoor(viewer, entity.index, jump_to_reciprocal);
349 case EntityType::Sprite:
352 DrawSprite(viewer, entity.index, identity);
353 ImGui::EndDisabled();
355 case EntityType::Item:
358 DrawItem(viewer, entity.index, identity);
359 ImGui::EndDisabled();
InteractionContext * context() const
Get the interaction context.
bool UpdateDoor(size_t index, zelda3::DoorType type, zelda3::DoorDirection direction, uint8_t position)
DungeonRoomStore * rooms() const
int current_room_id() const
DungeonObjectInteraction & object_interaction()
bool IsObjectInteractionEnabled() const
bool header_read_only() const
const project::YazeProject * project() const
InteractionCoordinator & entity_coordinator()
Get the interaction coordinator for entity handling.
ItemInteractionHandler & item_handler()
SpriteInteractionHandler & sprite_handler()
TileObjectHandler & tile_handler()
DoorInteractionHandler & door_handler()
bool MutateItemType(size_t index, uint8_t new_type)
Change one pot item's raw type byte in place.
bool UpdateItem(size_t index, uint8_t type, int pixel_x, int pixel_y)
bool UpdateSprite(size_t index, uint8_t id, int x, int y, int subtype, int layer, int key_drop)
static absl::Status ValidateSpriteProperties(uint8_t id, int x, int y, int subtype, int layer, int key_drop)
RAII guard for ImGui style colors.
static constexpr int kMaxDoorPositions
static bool IsValidPosition(uint8_t position, DoorDirection direction)
Check if a position is valid for door placement.
static std::pair< int, int > PositionToRenderTileCoords(uint8_t position, DoorDirection direction)
Convert encoded position to the top-left tile of the visible door.
const AgentUITheme & GetTheme()
bool BeginChoice(const char *label, const char *id, const char *preview)
constexpr std::array< const char *, 28 > kPotItemNames
void DrawItem(DungeonCanvasViewer &viewer, size_t index, gui::InputScalarTargetIdentity identity)
bool IntegerProperty(const char *label, const char *id, int &value, int maximum, int step, gui::InputScalarTargetIdentity identity, bool hexadecimal=false)
void DrawDoor(DungeonCanvasViewer &viewer, size_t index, const std::function< void(int, size_t)> &jump_to_reciprocal)
void PropertyLabel(const char *label)
void DrawSprite(DungeonCanvasViewer &viewer, size_t index, gui::InputScalarTargetIdentity identity)
zelda3::ResourceLabelProvider LabelsForProject(const project::YazeProject *project)
Editors are the view controllers for the application.
void DrawDungeonEntityInspector(DungeonCanvasViewer &viewer, const std::function< void(int, size_t)> &jump_to_reciprocal)
DungeonSelectionSnapshot BuildDungeonSelectionSnapshot(const DungeonObjectInteraction &interaction, const DungeonRoomStore *rooms, int room_id)
void DrawDungeonPotItemSaveNotice()
std::string GetDungeonSelectionSummaryText(const DungeonSelectionSnapshot &snapshot)
void DrawDungeonConnectionEditor(DungeonCanvasViewer &viewer, size_t door_index, const std::function< void(int, size_t)> &jump_to_reciprocal)
void AutoRegisterLastItem(const std::string &widget_type, const std::string &explicit_label, const std::string &description)
Automatically register the last ImGui item.
bool InputScalarDeferred(const char *label, ImGuiDataType data_type, void *data, const char *format, ImGuiInputTextFlags flags, InputScalarTargetIdentity target_identity)
constexpr std::array< DoorType, 32 > GetPlaceableDoorTypes()
Get supported door types suitable for editor placement controls.
constexpr std::string_view GetDoorDirectionName(DoorDirection dir)
Get human-readable name for door direction.
constexpr std::string_view GetDoorTypeName(DoorType type)
Get human-readable name for door type.
DoorDirection
Door direction on room walls.