7#include "absl/strings/str_format.h"
16#include "imgui/imgui.h"
27 : current_room_id_(current_room_id),
29 canvas_viewer_(canvas_viewer) {}
32 return "dungeon.sprite_editor";
36 return "Sprite Editor";
67 ImGui::TextDisabled(
"No room selected");
78 if (selected.size() == 1 && selected.front().type == EntityType::Sprite) {
83 if (show_room_sprites) {
99 std::function<
void()> callback) {
114 if (ImGui::IsItemHovered())
117 const bool done = ImGui::SmallButton(
"Done (Esc)");
122 ImGui::TextDisabled(
"Click a sprite, then the canvas.");
127 ImGui::SetNextItemWidth(-1);
128 ImGui::InputTextWithHint(
"##Search",
"Search sprites by name or hex ID...",
132 const float available = std::max(1.0f, ImGui::GetContentRegionAvail().y);
133 const float list_height =
134 show_room_sprites ? std::max(1.0f, available * 0.55f) : available;
135 const bool can_place =
140 if (ImGui::BeginChild(
"##SpriteLibrary", ImVec2(0, list_height),
true)) {
141 ImGui::BeginDisabled(!can_place);
142 for (
int id = 0;
id < 256; ++id) {
146 const std::string label = absl::StrFormat(
"%02X %s",
id, name);
148 const bool selected =
151 absl::StrFormat(
"place_sprite_%02X",
id));
156 if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled))
157 ImGui::SetTooltip(
"%s (%02X)\n%s", name.c_str(),
id,
159 ?
"Click, then click the room to place."
160 :
"Select an editable room to place sprites.");
163 ImGui::EndDisabled();
171 auto& sprites = room.GetSprites();
172 int selected_sprite_list_index = -1;
177 if (selected_entities.size() == 1 &&
178 selected_entities.front().type == EntityType::Sprite) {
179 selected_sprite_list_index =
180 static_cast<int>(selected_entities.front().index);
185 int sprite_count =
static_cast<int>(sprites.size());
187 sprite_count > 16 ? theme.text_error_red : theme.text_primary;
188 ImGui::TextColored(count_color,
ICON_MD_LIST " Room Sprites: %d/16",
197 if (sprite_count > 16) {
200 if (ImGui::IsItemHovered()) {
202 tr(
"Room exceeds sprite limit (16 max)!\n"
203 "This may cause game crashes."));
207 if (sprites.empty()) {
208 ImGui::TextColored(theme.text_secondary_gray,
214 float available = ImGui::GetContentRegionAvail().y;
215 float list_height = std::max(100.0f, available * 0.4f);
217 ImGui::BeginChild(
"##SpriteList", ImVec2(0, list_height),
true);
218 for (
size_t i = 0; i < sprites.size(); ++i) {
219 const auto& sprite = sprites[i];
220 bool is_selected = (selected_sprite_list_index ==
static_cast<int>(i));
222 ImGui::PushID(
static_cast<int>(i));
226 absl::StrFormat(
"[%02X] %s", sprite.id(),
SpriteName(sprite.id()));
229 if (sprite.key_drop() == 1) {
231 }
else if (sprite.key_drop() == 2) {
236 if (sprite.IsOverlord()) {
240 const bool row_selected = ImGui::Selectable(label.c_str(), is_selected);
242 absl::StrFormat(
"sprite_row_%zu", i),
243 "Select a sprite in the current dungeon room");
252 ImGui::SameLine(ImGui::GetContentRegionAvail().x - 80);
253 ImGui::TextColored(theme.text_secondary_gray, tr(
"(%d,%d) L%d"), sprite.x(),
254 sprite.y(), sprite.layer());
271 absl::StrFormat(
"%02X %s", sprite_id,
SpriteName(sprite_id));
273 const auto lower = [](
unsigned char c) {
274 return static_cast<char>(tolower(c));
276 std::transform(name.begin(), name.end(), name.begin(), lower);
277 std::transform(filter.begin(), filter.end(), filter.begin(), lower);
278 return name.find(filter) != std::string::npos;
int current_room_id() const
DungeonObjectInteraction & object_interaction()
bool IsObjectInteractionEnabled() const
bool header_read_only() const
const project::YazeProject * project() const
void SetSpritePlacementMode(bool enabled, uint8_t sprite_id=0)
bool IsSpritePlacementActive() const
InteractionCoordinator & entity_coordinator()
Get the interaction coordinator for entity handling.
void SelectEntity(EntityType type, size_t index)
const std::vector< SelectedEntity > & GetSelectedEntities() const
SpriteInteractionHandler & sprite_handler()
std::function< void(const zelda3::Sprite &) sprite_placed_callback_)
std::string SpriteName(int sprite_id) const
void SetOpenSelectionInspectorCallback(std::function< void()> callback)
void DrawContent(bool show_room_sprites)
void SetCanvasViewer(DungeonCanvasViewer *viewer)
std::string GetEditorCategory() const override
Editor category this panel belongs to.
DungeonRoomStore * rooms_
std::string GetDisplayName() const override
Human-readable name shown in menus and title bars.
void Draw(bool *p_open) override
Draw the panel content.
std::string GetWorkflowGroup() const override
Optional workflow group for hack-centric actions.
bool MatchesFilter(int sprite_id) const
DungeonCanvasViewer * canvas_viewer_
void DrawPlacementControls()
std::function< void()> open_selection_inspector_callback_
void DrawSpriteSelector(bool show_room_sprites)
void SetSpritePlacedCallback(std::function< void(const zelda3::Sprite &)> callback)
std::string GetId() const override
Unique identifier for this panel.
int GetPriority() const override
Get display priority for menu ordering.
SpriteEditorPanel(int *current_room_id, DungeonRoomStore *rooms, DungeonCanvasViewer *canvas_viewer=nullptr)
std::string GetIcon() const override
Material Design icon for this panel.
uint8_t GetSpriteId() const
Get current sprite ID for placement.
std::string GetLabel(ResourceType type, int id) const
Get a label for a resource by type and ID.
A class for managing sprites in the overworld and underworld.
#define ICON_MD_OPEN_IN_NEW
const AgentUITheme & GetTheme()
zelda3::ResourceLabelProvider LabelsForProject(const project::YazeProject *project)
void DrawDungeonEntityInspector(DungeonCanvasViewer &viewer, 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.