yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
sprite_interaction_handler.cc
Go to the documentation of this file.
1// Related header
3
4// C++ standard library
5#include <algorithm>
6
7// Third-party library headers
8#include "absl/strings/str_format.h"
9#include "imgui/imgui.h"
10
11// Project headers
13
14namespace yaze::editor {
15
20
21bool SpriteInteractionHandler::HandleClick(int canvas_x, int canvas_y) {
22 if (!HasValidContext()) return false;
23
25 PlaceSpriteAtPosition(canvas_x, canvas_y);
26 return true;
27 }
28
29 // Try to select sprite at position
30 auto sprite_index = GetEntityAtPosition(canvas_x, canvas_y);
31 if (sprite_index.has_value()) {
32 SelectSprite(*sprite_index);
33 is_dragging_ = true;
34 drag_start_pos_ = ImVec2(static_cast<float>(canvas_x),
35 static_cast<float>(canvas_y));
37 return true;
38 }
39
41 return false;
42}
43
44void SpriteInteractionHandler::HandleDrag(ImVec2 current_pos, ImVec2 delta) {
45 if (!is_dragging_ || !selected_sprite_index_.has_value()) return;
46 drag_current_pos_ = current_pos;
47}
48
50 if (!is_dragging_ || !selected_sprite_index_.has_value()) {
51 is_dragging_ = false;
52 return;
53 }
54
55 auto* room = GetCurrentRoom();
56 if (!room) {
57 is_dragging_ = false;
58 return;
59 }
60
61 // Convert to sprite coordinates (16-pixel units)
62 auto [tile_x, tile_y] = CanvasToSpriteCoords(
63 static_cast<int>(drag_current_pos_.x),
64 static_cast<int>(drag_current_pos_.y));
65
66 // Clamp to valid range (sprites use 0-31 range)
67 tile_x = std::clamp(tile_x, 0, dungeon_coords::kSpriteGridMax);
68 tile_y = std::clamp(tile_y, 0, dungeon_coords::kSpriteGridMax);
69
70 auto& sprites = room->GetSprites();
71 if (*selected_sprite_index_ < sprites.size()) {
73
74 sprites[*selected_sprite_index_].set_x(tile_x);
75 sprites[*selected_sprite_index_].set_y(tile_y);
76
78 }
79
80 is_dragging_ = false;
81}
82
84 if (!sprite_placement_mode_ || !HasValidContext()) return;
85
86 auto* canvas = ctx_->canvas;
87 if (!canvas->IsMouseHovering()) return;
88
89 const ImGuiIO& io = ImGui::GetIO();
90 ImVec2 canvas_pos = canvas->zero_point();
91 float scale = GetCanvasScale();
92
93 // Convert to room coordinates (sprites use 16-pixel grid)
94 int canvas_x = static_cast<int>((io.MousePos.x - canvas_pos.x) / scale);
95 int canvas_y = static_cast<int>((io.MousePos.y - canvas_pos.y) / scale);
96
97 // Snap to 16-pixel grid
98 int snapped_x = (canvas_x / dungeon_coords::kSpriteTileSize) *
100 int snapped_y = (canvas_y / dungeon_coords::kSpriteTileSize) *
102
103 // Draw ghost rectangle for sprite preview
104 ImVec2 rect_min(canvas_pos.x + snapped_x * scale,
105 canvas_pos.y + snapped_y * scale);
106 ImVec2 rect_max(rect_min.x + dungeon_coords::kSpriteTileSize * scale,
107 rect_min.y + dungeon_coords::kSpriteTileSize * scale);
108
109 // Semi-transparent green for sprites
110 ImU32 fill_color = IM_COL32(50, 200, 50, 100);
111 ImU32 outline_color = IM_COL32(50, 255, 50, 200);
112
113 canvas->draw_list()->AddRectFilled(rect_min, rect_max, fill_color);
114 canvas->draw_list()->AddRect(rect_min, rect_max, outline_color, 0.0f, 0,
115 2.0f);
116
117 // Draw sprite ID label
118 std::string label = absl::StrFormat("%02X", preview_sprite_id_);
119 canvas->draw_list()->AddText(rect_min, IM_COL32(255, 255, 255, 255),
120 label.c_str());
121}
122
124 if (!selected_sprite_index_.has_value() || !HasValidContext()) return;
125
126 auto* room = GetCurrentRoom();
127 if (!room) return;
128
129 const auto& sprites = room->GetSprites();
130 if (*selected_sprite_index_ >= sprites.size()) return;
131
132 const auto& sprite = sprites[*selected_sprite_index_];
133
134 // Sprites use 16-pixel coordinate system
135 int pixel_x = sprite.x() * dungeon_coords::kSpriteTileSize;
136 int pixel_y = sprite.y() * dungeon_coords::kSpriteTileSize;
137
138 // If dragging, use current drag position (snapped to 16-pixel grid)
139 if (is_dragging_) {
140 auto [tile_x, tile_y] = CanvasToSpriteCoords(
141 static_cast<int>(drag_current_pos_.x),
142 static_cast<int>(drag_current_pos_.y));
143 tile_x = std::clamp(tile_x, 0, dungeon_coords::kSpriteGridMax);
144 tile_y = std::clamp(tile_y, 0, dungeon_coords::kSpriteGridMax);
145 pixel_x = tile_x * dungeon_coords::kSpriteTileSize;
146 pixel_y = tile_y * dungeon_coords::kSpriteTileSize;
147 }
148
149 ImDrawList* draw_list = ImGui::GetWindowDrawList();
150 ImVec2 canvas_pos = GetCanvasZeroPoint();
151 float scale = GetCanvasScale();
152
153 ImVec2 pos(canvas_pos.x + pixel_x * scale, canvas_pos.y + pixel_y * scale);
154 ImVec2 size(dungeon_coords::kSpriteTileSize * scale,
156
157 // Animated selection
158 static float pulse = 0.0f;
159 pulse += ImGui::GetIO().DeltaTime * 3.0f;
160 float alpha = 0.5f + 0.3f * sinf(pulse);
161
162 ImU32 color = IM_COL32(0, 255, 0, 180); // Green
163 ImU32 fill_color =
164 (color & 0x00FFFFFF) | (static_cast<ImU32>(alpha * 100) << 24);
165
166 draw_list->AddRectFilled(pos, ImVec2(pos.x + size.x, pos.y + size.y),
167 fill_color);
168 draw_list->AddRect(pos, ImVec2(pos.x + size.x, pos.y + size.y), color, 0.0f,
169 0, 2.0f);
170
171 // Draw label
172 ImVec2 text_pos(pos.x, pos.y - 14 * scale);
173 draw_list->AddText(text_pos, IM_COL32(255, 255, 255, 220), "Sprite");
174}
175
177 int canvas_x, int canvas_y) const {
178 if (!HasValidContext()) return std::nullopt;
179
180 auto* room = ctx_->GetCurrentRoomConst();
181 if (!room) return std::nullopt;
182
183 // Convert screen coordinates to room coordinates
184 float scale = GetCanvasScale();
185 int room_x = static_cast<int>(canvas_x / scale);
186 int room_y = static_cast<int>(canvas_y / scale);
187
188 // Check sprites (16x16 hitbox)
189 const auto& sprites = room->GetSprites();
190 for (size_t i = 0; i < sprites.size(); ++i) {
191 const auto& sprite = sprites[i];
192
193 // Sprites use 16-pixel coordinate system
194 int sprite_x = sprite.x() * dungeon_coords::kSpriteTileSize;
195 int sprite_y = sprite.y() * dungeon_coords::kSpriteTileSize;
196
197 // 16x16 hitbox
198 if (room_x >= sprite_x &&
199 room_x < sprite_x + dungeon_coords::kSpriteTileSize &&
200 room_y >= sprite_y &&
201 room_y < sprite_y + dungeon_coords::kSpriteTileSize) {
202 return i;
203 }
204 }
205
206 return std::nullopt;
207}
208
213
218
220 if (!selected_sprite_index_.has_value() || !HasValidContext()) return;
221
222 auto* room = GetCurrentRoom();
223 if (!room) return;
224
225 auto& sprites = room->GetSprites();
226 if (*selected_sprite_index_ >= sprites.size()) return;
227
229 sprites.erase(sprites.begin() +
230 static_cast<ptrdiff_t>(*selected_sprite_index_));
233}
234
236 int canvas_y) {
237 if (!HasValidContext()) return;
238
239 auto* room = GetCurrentRoom();
240 if (!room) return;
241
242 auto [sprite_x, sprite_y] = CanvasToSpriteCoords(canvas_x, canvas_y);
243
244 // Clamp to valid range
245 sprite_x = std::clamp(sprite_x, 0, dungeon_coords::kSpriteGridMax);
246 sprite_y = std::clamp(sprite_y, 0, dungeon_coords::kSpriteGridMax);
247
249
250 // Create the sprite
251 zelda3::Sprite new_sprite(preview_sprite_id_, static_cast<uint8_t>(sprite_x),
252 static_cast<uint8_t>(sprite_y), 0, 0);
253
254 // Add sprite to room
255 room->GetSprites().push_back(new_sprite);
256
258}
259
261 int canvas_x, int canvas_y) const {
262 float scale = GetCanvasScale();
263 // Convert to pixel coordinates, then to sprite tile coordinates
264 int pixel_x = static_cast<int>(canvas_x / scale);
265 int pixel_y = static_cast<int>(canvas_y / scale);
266 return {pixel_x / dungeon_coords::kSpriteTileSize,
268}
269
270} // namespace yaze::editor
float GetCanvasScale() const
Get canvas global scale.
zelda3::Room * GetCurrentRoom() const
Get current room (convenience method)
bool HasValidContext() const
Check if context is valid.
ImVec2 GetCanvasZeroPoint() const
Get canvas zero point (for screen coordinate conversion)
void PlaceSpriteAtPosition(int canvas_x, int canvas_y)
Place sprite at position.
void HandleRelease() override
Handle mouse release.
void SelectSprite(size_t index)
Select sprite at 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.
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.
A class for managing sprites in the overworld and underworld.
Definition sprite.h:35
Editors are the view controllers for the application.
Definition agent_chat.cc:23
const zelda3::Room * GetCurrentRoomConst() const
Get const pointer to current room.
void NotifyEntityChanged() const
Notify that entity has changed.
void NotifyInvalidateCache() const
Notify that cache invalidation is needed.
void NotifyMutation() const
Notify that a mutation is about to happen.