8#include "absl/strings/str_format.h"
9#include "imgui/imgui.h"
36 if (item_index.has_value()) {
40 ImVec2(
static_cast<float>(canvas_x),
static_cast<float>(canvas_y));
72 auto& pot_items = room->GetPotItems();
75 pixel_x / 8 * 8, pixel_y / 8 * 8,
81 const uint16_t next_position = *encoded;
89 room->MarkPotItemsDirty();
103 if (!pointer_screen_pos.has_value())
107 const auto [canvas_x, canvas_y] =
118 ImVec2(
static_cast<float>(snapped_x),
static_cast<float>(snapped_y)));
120 const ImVec2 rect_max(rect_min.x + rect_size.x, rect_min.y + rect_size.y);
123 ImVec4 fill_color = theme.dungeon_selection_primary;
124 fill_color.w = 0.35f;
125 ImVec4 outline_color = theme.dungeon_selection_primary;
126 outline_color.w = 0.85f;
128 canvas->draw_list()->AddRectFilled(rect_min, rect_max,
129 ImGui::GetColorU32(fill_color));
130 canvas->draw_list()->AddRect(
131 rect_min, rect_max, ImGui::GetColorU32(outline_color), 0.0f, 0, 2.0f);
135 canvas->draw_list()->AddText(rect_min, ImGui::GetColorU32(theme.text_primary),
147 const auto& pot_items = room->GetPotItems();
152 int pixel_x = pot_item.GetPixelX();
153 int pixel_y = pot_item.GetPixelY();
164 ImDrawList* draw_list = ImGui::GetWindowDrawList();
166 const float scale = transform.
scale();
168 ImVec2(
static_cast<float>(pixel_x),
static_cast<float>(pixel_y)));
172 static float pulse = 0.0f;
173 pulse += ImGui::GetIO().DeltaTime * 3.0f;
174 float alpha = 0.5f + 0.3f * sinf(pulse);
176 ImU32 color = IM_COL32(255, 255, 0, 180);
178 (color & 0x00FFFFFF) | (
static_cast<ImU32
>(alpha * 100) << 24);
180 draw_list->AddRectFilled(pos, ImVec2(pos.x + size.x, pos.y + size.y),
182 draw_list->AddRect(pos, ImVec2(pos.x + size.x, pos.y + size.y), color, 0.0f,
186 ImVec2 text_pos(pos.x, pos.y - 14 * scale);
187 draw_list->AddText(text_pos, IM_COL32(255, 255, 255, 220),
"Item");
191 int canvas_x,
int canvas_y)
const {
201 for (
size_t i = 0; i < pot_items.size(); ++i) {
202 const auto& pot_item = pot_items[i];
204 int item_x = pot_item.GetPixelX();
205 int item_y = pot_item.GetPixelY();
208 if (canvas_x >= item_x && canvas_x < item_x + 16 && canvas_y >= item_y &&
209 canvas_y < item_y + 16) {
235 auto& pot_items = room->GetPotItems();
240 pot_items.erase(pot_items.begin() +
242 room->MarkPotItemsDirty();
254 if (!room || room->GetPotItems().empty()) {
259 room->GetPotItems().clear();
260 room->MarkPotItemsDirty();
277 auto& pot_items = room->GetPotItems();
283 constexpr int kRoomPixelMax = 511;
284 const int next_pixel_x =
285 std::clamp(pot_item.GetPixelX() + delta_pixel_x, 0, kRoomPixelMax);
286 const int next_pixel_y =
287 std::clamp(pot_item.GetPixelY() + delta_pixel_y, 0, kRoomPixelMax);
289 next_pixel_x / 8 * 8, next_pixel_y / 8 * 8, pot_item.position);
292 const uint16_t next_position = *encoded;
293 if (next_position == pot_item.position) {
298 pot_item.position = next_position;
299 room->MarkPotItemsDirty();
310 if (!room || index >= room->GetPotItems().size()) {
313 auto& item = room->GetPotItems()[index];
314 if (item.item == new_type || item.position == 0xFFFF) {
321 item.item = new_type;
322 room->MarkPotItemsDirty();
334 if (!room || index >= room->GetPotItems().size()) {
337 auto& item = room->GetPotItems()[index];
338 if (pixel_x == item.GetPixelX() && pixel_y == item.GetPixelY()) {
345 const uint16_t position = *encoded;
346 if (item.item == type && item.position == position) {
351 item.position = position;
352 room->MarkPotItemsDirty();
359 const std::vector<zelda3::PotItem>& candidate) {
363 const auto* source_context =
ctx_;
371 "Pot-item room changed during placement preflight; repeat the "
391 std::clamp(canvas_y, 0, 511) / 8 * 8);
398 auto candidate = room->GetPotItems();
399 candidate.push_back(new_item);
405 room->GetPotItems() = std::move(candidate);
406 room->MarkPotItemsDirty();
std::optional< ImVec2 > GetPointerScreenPosition() const
DungeonCanvasTransform GetCanvasTransform() const
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.
void DrawSelectionHighlight() override
Draw selection highlight for selected entities.
std::function< void(const absl::Status &) mutation_error_callback_)
std::optional< size_t > selected_item_index_
ItemPlacementPreflight item_placement_preflight_
bool HandleClick(int canvas_x, int canvas_y) override
Handle mouse click at canvas position.
void DrawGhostPreview() override
Draw ghost preview during placement.
bool PreflightPlacement(const std::vector< zelda3::PotItem > &candidate)
void BeginPlacement() override
Begin placement mode.
void PlaceItemAtPosition(int canvas_x, int canvas_y)
Place item at position.
void HandleDrag(ImVec2 current_pos, ImVec2 delta) override
Handle mouse drag.
void ClearSelection()
Clear item selection.
absl::Status mutation_status_
std::optional< size_t > GetEntityAtPosition(int canvas_x, int canvas_y) const override
Get entity at canvas position.
bool item_placement_mode_
void DeleteSelected()
Delete selected item.
bool NudgeSelected(int delta_pixel_x, int delta_pixel_y)
bool MutateItemType(size_t index, uint8_t new_type)
Change one pot item's raw type byte in place.
void HandleRelease() override
Handle mouse release.
void SelectItem(size_t index)
Select item at index.
bool UpdateItem(size_t index, uint8_t type, int pixel_x, int pixel_y)
const std::vector< PotItem > & GetPotItems() const
const AgentUITheme & GetTheme()
constexpr int kRoomPixelHeight
constexpr int kRoomPixelWidth
Editors are the view controllers for the application.
constexpr std::optional< uint16_t > EncodePotItemPosition(int x, int y, uint16_t original=0)
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.