9#include "absl/strings/str_format.h"
10#include "imgui/imgui.h"
22 const size_t current_sprite_count = room ? room->GetSprites().size() : 0;
45 if (sprite_index.has_value()) {
49 ImVec2(
static_cast<float>(canvas_x),
static_cast<float>(canvas_y));
77 auto [tile_x, tile_y] =
85 auto& sprites = room->GetSprites();
104 if (!pointer_screen_pos.has_value())
108 const auto [canvas_x, canvas_y] =
118 const size_t current_sprite_count = room ? room->GetSprites().size() : 0;
123 ImVec2(
static_cast<float>(snapped_x),
static_cast<float>(snapped_y)));
126 const ImVec2 rect_max(rect_min.x + rect_size.x, rect_min.y + rect_size.y);
129 const ImVec4 base_color =
131 ImVec4 fill_color = base_color;
132 fill_color.w = 0.40f;
133 ImVec4 outline_color = base_color;
134 outline_color.w = 0.85f;
136 canvas->draw_list()->AddRectFilled(rect_min, rect_max,
137 ImGui::GetColorU32(fill_color));
138 canvas->draw_list()->AddRect(
139 rect_min, rect_max, ImGui::GetColorU32(outline_color), 0.0f, 0, 2.0f);
143 canvas->draw_list()->AddText(rect_min, ImGui::GetColorU32(theme.text_primary),
147 if (capacity_state != GhostCapacityState::kNormal &&
148 ImGui::IsMouseHoveringRect(rect_min, rect_max)) {
149 ImGui::SetTooltip(tr(
"Sprites: %zu/%zu\n%s"), current_sprite_count,
154 const std::string badge_text = absl::StrFormat(
157 ImVec2(rect_min.x, rect_max.y + 6.0f), theme,
158 capacity_state, badge_text);
169 const auto& sprites = room->GetSprites();
181 auto [tile_x, tile_y] =
190 ImDrawList* draw_list = ImGui::GetWindowDrawList();
192 const float scale = transform.
scale();
194 ImVec2(
static_cast<float>(pixel_x),
static_cast<float>(pixel_y)));
199 static float pulse = 0.0f;
200 pulse += ImGui::GetIO().DeltaTime * 3.0f;
201 float alpha = 0.5f + 0.3f * sinf(pulse);
203 ImU32 color = IM_COL32(0, 255, 0, 180);
205 (color & 0x00FFFFFF) | (
static_cast<ImU32
>(alpha * 100) << 24);
207 draw_list->AddRectFilled(pos, ImVec2(pos.x + size.x, pos.y + size.y),
209 draw_list->AddRect(pos, ImVec2(pos.x + size.x, pos.y + size.y), color, 0.0f,
213 ImVec2 text_pos(pos.x, pos.y - 14 * scale);
214 draw_list->AddText(text_pos, IM_COL32(255, 255, 255, 220),
"Sprite");
218 int canvas_x,
int canvas_y)
const {
228 for (
size_t i = 0; i < sprites.size(); ++i) {
229 const auto& sprite = sprites[i];
236 if (canvas_x >= sprite_x &&
238 canvas_y >= sprite_y &&
265 auto& sprites = room->GetSprites();
270 sprites.erase(sprites.begin() +
272 room->MarkSpritesDirty();
284 if (!room || room->GetSprites().empty()) {
289 room->GetSprites().clear();
290 room->MarkSpritesDirty();
306 auto& sprites = room->GetSprites();
312 const int next_x = std::clamp(
static_cast<int>(sprite.x()) + delta_x, 0,
314 const int next_y = std::clamp(
static_cast<int>(sprite.y()) + delta_y, 0,
316 if (next_x == sprite.x() && next_y == sprite.y()) {
321 sprite.set_x(next_x);
322 sprite.set_y(next_y);
323 room->MarkSpritesDirty();
360 static_cast<uint8_t
>(sprite_y), 0, 0);
363 room->GetSprites().push_back(new_sprite);
364 room->MarkSpritesDirty();
371 int canvas_x,
int canvas_y)
const {
std::optional< ImVec2 > GetPointerScreenPosition() const
DungeonCanvasTransform GetCanvasTransform() const
void TriggerSuccessToast()
InteractionContext * ctx_
bool IsWithinBounds(int canvas_x, int canvas_y) const
Check if coordinates are within room bounds.
zelda3::Room * GetCurrentRoom() const
Get current room (convenience method)
bool HasValidContext() const
Check if context is valid.
GhostCapacityState GetPlacementGhostCapacityState() const
bool NudgeSelected(int delta_x, int delta_y)
void PlaceSpriteAtPosition(int canvas_x, int canvas_y)
Place sprite at position.
void ClearSelection()
Clear sprite selection.
PlacementCapacityState GhostCapacityState
void HandleRelease() override
Handle mouse release.
void SelectSprite(size_t index)
Select sprite at index.
std::optional< size_t > selected_sprite_index_
std::optional< size_t > GetEntityAtPosition(int canvas_x, int canvas_y) const override
Get entity at canvas position.
void DrawGhostPreview() override
Draw ghost preview during placement.
uint8_t preview_sprite_id_
bool sprite_placement_mode_
void DrawSelectionHighlight() override
Draw selection highlight for selected entities.
void HandleDrag(ImVec2 current_pos, ImVec2 delta) override
Handle mouse drag.
bool HandleClick(int canvas_x, int canvas_y) override
Handle mouse click at canvas position.
std::pair< int, int > CanvasToSpriteCoords(int canvas_x, int canvas_y) const
Convert canvas to sprite coordinates (16-pixel grid)
void BeginPlacement() override
Begin placement mode.
PlacementBlockReason placement_block_reason_
void DeleteSelected()
Delete selected sprite.
const std::vector< zelda3::Sprite > & GetSprites() const
A class for managing sprites in the overworld and underworld.
const AgentUITheme & GetTheme()
constexpr int kSpriteGridMax
constexpr int kSpriteTileSize
Editors are the view controllers for the application.
void DrawPlacementCapacityBadge(ImDrawList *draw_list, const ImVec2 &badge_min, const AgentUITheme &theme, PlacementCapacityState state, std::string_view primary_text)
ImVec4 GetPlacementAccentColor(const AgentUITheme &theme, PlacementCapacityState state, const ImVec4 &normal_color)
std::string_view GetPlacementCapacityTooltipSuffix(PlacementCapacityState state)
PlacementCapacityState GetPlacementCapacityState(size_t current_count, size_t max_count)
constexpr size_t kMaxTotalSprites
const zelda3::Room * GetCurrentRoomConst() const
Get const pointer to current room.
void NotifyEntityChanged() const
Notify that entity has changed.
void NotifyInvalidateCache(MutationDomain domain=MutationDomain::kUnknown) const
Notify that cache invalidation is needed.
void NotifyMutation(MutationDomain domain=MutationDomain::kUnknown) const
Notify that a mutation is about to happen.