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);
103template <
typename PlaceFn,
typename DeleteFn>
105 bool deleted =
false;
106 if (ImGui::BeginTable(
107 "##EntityActions", 2,
108 ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_NoPadOuterX)) {
109 ImGui::TableNextColumn();
110 if (ImGui::Button(
"Place another", ImVec2(-1, 0))) {
114 ImGui::TableNextColumn();
115 if (ImGui::Button(
"Delete", ImVec2(-1, 0))) {
126 const std::function<
void(
int,
size_t)>& jump_to_reciprocal) {
128 if (index >= doors.size()) {
131 auto door = doors[index];
132 ImGui::TextWrapped(
"Door #%zu · %s", index,
134 bool changed =
false;
135 bool deleted =
false;
143 absl::StrFormat(
"%02X %s",
static_cast<int>(type),
145 if (ImGui::Selectable(label.c_str(), door.type == type)) {
155 for (
int i = 0; i < 4; ++i) {
157 if (ImGui::Selectable(
159 door.direction == direction)) {
160 door.direction = direction;
166 auto position_label = [&](uint8_t position) {
169 position, door.direction);
170 return absl::StrFormat(
"%02X · tile %d, %d", position, x, y);
173 position_label(door.position).c_str())) {
178 if (ImGui::Selectable(position_label(i).c_str(), door.position == i)) {
179 door.position =
static_cast<uint8_t
>(i);
196 ImGui::EndDisabled();
202 index, door.type, door.direction, door.position);
206 const bool normal_door =
211 if (ImGui::Button(
"Complete stone surround", ImVec2(-1, 0))) {
215 if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
218 ?
"Add missing stone trim on the selected side and select it "
220 "Existing matching trim is reused. One Undo step."
221 :
"Stone surrounds are available for ordinary doors.");
223 ImGui::EndDisabled();
224 const auto& trim_status =
226 if (!trim_status.ok()) {
227 ImGui::TextWrapped(
"%s", std::string(trim_status.message()).c_str());
237 const auto& sprites =
239 if (index >= sprites.size()) {
244 const auto& sprite = sprites[index];
248 ImGui::TextWrapped(
"Sprite #%zu · %02X %s", index, sprite.id(), name.c_str());
249 if (sprite.IsOverlord()) {
250 ImGui::TextDisabled(
"Overlord");
252 int id = sprite.id(), x = sprite.x(), y = sprite.y();
253 int subtype = sprite.subtype(), layer = sprite.layer(),
254 key = sprite.key_drop();
255 const ImGuiID error_id = ImGui::GetID(
"SpriteEditRejected");
256 bool changed =
false;
264 constexpr const char* kLayers[] = {
"Upper (0)",
"Lower (1)"};
266 layer >= 0 && layer <= 1 ? kLayers[layer] :
"Unknown")) {
267 for (
int i = 0; i < 2; ++i) {
268 if (ImGui::Selectable(kLayers[i], layer == i)) {
273 "selectable", i == 0 ?
"SpriteLayer/Upper" :
"SpriteLayer/Lower");
277 constexpr const char* kKeys[] = {
"None",
"Small key",
"Big key"};
279 key >= 0 && key <= 2 ? kKeys[key] :
"Unknown")) {
280 for (
int i = 0; i < 3; ++i) {
281 if (ImGui::Selectable(kKeys[i], key == i)) {
292 static_cast<uint8_t
>(
id), x, y, subtype, layer, key);
293 ImGui::GetStateStorage()->SetBool(error_id, !validation.ok());
294 if (validation.ok()) {
298 .
UpdateSprite(index,
static_cast<uint8_t
>(
id), x, y, subtype, layer,
302 if (ImGui::GetStateStorage()->GetBool(error_id)) {
304 "Edit not applied: those fields encode a reserved sprite-list marker. "
305 "Change the position, subtype, or layer before trying again.");
308 "Position uses 16-pixel tiles. Drag in the canvas to move.");
312 true,
static_cast<uint8_t
>(
id));
324 if (index >= items.size()) {
327 const auto item = items[index];
328 auto type = item.item;
329 int x = item.GetPixelX(), y = item.GetPixelY();
331 ImGui::TextWrapped(
"Pot item #%zu · %02X %s", index, type, name);
333 bool changed =
false;
334 bool position_changed =
false;
337 absl::StrFormat(
"%02X %s", type, name).c_str())) {
339 const auto label = absl::StrFormat(
"%02X %s", i,
kPotItemNames[i]);
340 if (ImGui::Selectable(label.c_str(), type == i)) {
341 type =
static_cast<uint8_t
>(i);
345 absl::StrFormat(
"ItemType/%zu", i));
355 if (position_changed) {
358 }
else if (changed) {
365 "Position snaps to an 8-pixel grid; layer and control flags are "
379 return item_id < kPotItemNames.size() ? kPotItemNames[item_id] :
"Unknown";
389 "Pot-item saving is off. Enable Save Pot Items in File > Save Scope > "
390 "Dungeon before saving.");
395 const std::function<
void(
int,
size_t)>& jump_to_reciprocal) {
400 const auto* context =
404 if (!context || context->current_room_id != viewer.
current_room_id()) {
406 "Open this room in the room canvas to edit its entities.");
411 if (selection.HasMixedSelection()) {
412 ImGui::TextWrapped(
"%s. Select one entity to edit its properties.",
416 const auto entity = selection.entity;
418 ImGui::PushID(
"DungeonEntityInspector");
420 ImGui::PushID(
static_cast<int>(entity.index));
422 reinterpret_cast<uintptr_t
>(&viewer),
424 static_cast<uint64_t
>(entity.type)};
425 switch (entity.type) {
426 case EntityType::Door:
427 DrawDoor(viewer, entity.index, jump_to_reciprocal);
429 case EntityType::Sprite:
432 DrawSprite(viewer, entity.index, identity);
433 ImGui::EndDisabled();
435 case EntityType::Item:
438 DrawItem(viewer, entity.index, identity);
439 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
void SetItemPlacementMode(bool enabled, uint8_t item_id=0)
void SetSpritePlacementMode(bool enabled, uint8_t sprite_id=0)
InteractionCoordinator & entity_coordinator()
Get the interaction coordinator for entity handling.
ItemInteractionHandler & item_handler()
absl::Status CompleteDoorTrim(size_t door_index)
SpriteInteractionHandler & sprite_handler()
TileObjectHandler & tile_handler()
const absl::Status & selection_edit_status() const
DoorInteractionHandler & door_handler()
absl::Status DeleteSelectedEntity()
Delete currently selected entity.
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 DrawEntityActionRow(PlaceFn &&on_place_another, DeleteFn &&on_delete)
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.
const char * GetDungeonPotItemName(uint8_t item_id)
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)
@ NormalDoor
Normal door (upper layer)
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.