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())
23 return false;
24
26 PlaceSpriteAtPosition(canvas_x, canvas_y);
27 return true;
28 }
29
30 // Try to select sprite at position
31 auto sprite_index = GetEntityAtPosition(canvas_x, canvas_y);
32 if (sprite_index.has_value()) {
33 SelectSprite(*sprite_index);
34 is_dragging_ = true;
36 ImVec2(static_cast<float>(canvas_x), static_cast<float>(canvas_y));
38 return true;
39 }
40
42 return false;
43}
44
45void SpriteInteractionHandler::HandleDrag(ImVec2 current_pos, ImVec2 delta) {
46 if (!is_dragging_ || !selected_sprite_index_.has_value())
47 return;
48 drag_current_pos_ = current_pos;
49}
50
52 if (!is_dragging_ || !selected_sprite_index_.has_value()) {
53 is_dragging_ = false;
54 return;
55 }
56
57 auto* room = GetCurrentRoom();
58 if (!room) {
59 is_dragging_ = false;
60 return;
61 }
62
63 // Convert to sprite coordinates (16-pixel units)
64 auto [tile_x, tile_y] =
65 CanvasToSpriteCoords(static_cast<int>(drag_current_pos_.x),
66 static_cast<int>(drag_current_pos_.y));
67
68 // Clamp to valid range (sprites use 0-31 range)
69 tile_x = std::clamp(tile_x, 0, dungeon_coords::kSpriteGridMax);
70 tile_y = std::clamp(tile_y, 0, dungeon_coords::kSpriteGridMax);
71
72 auto& sprites = room->GetSprites();
73 if (*selected_sprite_index_ < sprites.size()) {
75
76 sprites[*selected_sprite_index_].set_x(tile_x);
77 sprites[*selected_sprite_index_].set_y(tile_y);
78
80 }
81
82 is_dragging_ = false;
83}
84
87 return;
88
89 auto* canvas = ctx_->canvas;
90 if (!canvas->IsMouseHovering())
91 return;
92
93 const ImGuiIO& io = ImGui::GetIO();
94 ImVec2 canvas_pos = canvas->zero_point();
95 float scale = GetCanvasScale();
96
97 // Convert to room coordinates (sprites use 16-pixel grid)
98 int canvas_x = static_cast<int>((io.MousePos.x - canvas_pos.x) / scale);
99 int canvas_y = static_cast<int>((io.MousePos.y - canvas_pos.y) / scale);
100
101 // Snap to 16-pixel grid
102 int snapped_x = (canvas_x / dungeon_coords::kSpriteTileSize) *
104 int snapped_y = (canvas_y / dungeon_coords::kSpriteTileSize) *
106
107 // Draw ghost rectangle for sprite preview
108 ImVec2 rect_min(canvas_pos.x + snapped_x * scale,
109 canvas_pos.y + snapped_y * scale);
110 ImVec2 rect_max(rect_min.x + dungeon_coords::kSpriteTileSize * scale,
111 rect_min.y + dungeon_coords::kSpriteTileSize * scale);
112
113 // Semi-transparent green for sprites
114 ImU32 fill_color = IM_COL32(50, 200, 50, 100);
115 ImU32 outline_color = IM_COL32(50, 255, 50, 200);
116
117 canvas->draw_list()->AddRectFilled(rect_min, rect_max, fill_color);
118 canvas->draw_list()->AddRect(rect_min, rect_max, outline_color, 0.0f, 0,
119 2.0f);
120
121 // Draw sprite ID label
122 std::string label = absl::StrFormat("%02X", preview_sprite_id_);
123 canvas->draw_list()->AddText(rect_min, IM_COL32(255, 255, 255, 255),
124 label.c_str());
125}
126
128 if (!selected_sprite_index_.has_value() || !HasValidContext())
129 return;
130
131 auto* room = GetCurrentRoom();
132 if (!room)
133 return;
134
135 const auto& sprites = room->GetSprites();
136 if (*selected_sprite_index_ >= sprites.size())
137 return;
138
139 const auto& sprite = sprites[*selected_sprite_index_];
140
141 // Sprites use 16-pixel coordinate system
142 int pixel_x = sprite.x() * dungeon_coords::kSpriteTileSize;
143 int pixel_y = sprite.y() * dungeon_coords::kSpriteTileSize;
144
145 // If dragging, use current drag position (snapped to 16-pixel grid)
146 if (is_dragging_) {
147 auto [tile_x, tile_y] =
148 CanvasToSpriteCoords(static_cast<int>(drag_current_pos_.x),
149 static_cast<int>(drag_current_pos_.y));
150 tile_x = std::clamp(tile_x, 0, dungeon_coords::kSpriteGridMax);
151 tile_y = std::clamp(tile_y, 0, dungeon_coords::kSpriteGridMax);
152 pixel_x = tile_x * dungeon_coords::kSpriteTileSize;
153 pixel_y = tile_y * dungeon_coords::kSpriteTileSize;
154 }
155
156 ImDrawList* draw_list = ImGui::GetWindowDrawList();
157 ImVec2 canvas_pos = GetCanvasZeroPoint();
158 float scale = GetCanvasScale();
159
160 ImVec2 pos(canvas_pos.x + pixel_x * scale, canvas_pos.y + pixel_y * scale);
161 ImVec2 size(dungeon_coords::kSpriteTileSize * scale,
163
164 // Animated selection
165 static float pulse = 0.0f;
166 pulse += ImGui::GetIO().DeltaTime * 3.0f;
167 float alpha = 0.5f + 0.3f * sinf(pulse);
168
169 ImU32 color = IM_COL32(0, 255, 0, 180); // Green
170 ImU32 fill_color =
171 (color & 0x00FFFFFF) | (static_cast<ImU32>(alpha * 100) << 24);
172
173 draw_list->AddRectFilled(pos, ImVec2(pos.x + size.x, pos.y + size.y),
174 fill_color);
175 draw_list->AddRect(pos, ImVec2(pos.x + size.x, pos.y + size.y), color, 0.0f,
176 0, 2.0f);
177
178 // Draw label
179 ImVec2 text_pos(pos.x, pos.y - 14 * scale);
180 draw_list->AddText(text_pos, IM_COL32(255, 255, 255, 220), "Sprite");
181}
182
184 int canvas_x, int canvas_y) const {
185 if (!HasValidContext())
186 return std::nullopt;
187
188 auto* room = ctx_->GetCurrentRoomConst();
189 if (!room)
190 return std::nullopt;
191
192 // Convert screen coordinates to room coordinates
193 float scale = GetCanvasScale();
194 int room_x = static_cast<int>(canvas_x / scale);
195 int room_y = static_cast<int>(canvas_y / scale);
196
197 // Check sprites (16x16 hitbox)
198 const auto& sprites = room->GetSprites();
199 for (size_t i = 0; i < sprites.size(); ++i) {
200 const auto& sprite = sprites[i];
201
202 // Sprites use 16-pixel coordinate system
203 int sprite_x = sprite.x() * dungeon_coords::kSpriteTileSize;
204 int sprite_y = sprite.y() * dungeon_coords::kSpriteTileSize;
205
206 // 16x16 hitbox
207 if (room_x >= sprite_x &&
208 room_x < sprite_x + dungeon_coords::kSpriteTileSize &&
209 room_y >= sprite_y &&
210 room_y < sprite_y + dungeon_coords::kSpriteTileSize) {
211 return i;
212 }
213 }
214
215 return std::nullopt;
216}
217
222
227
229 if (!selected_sprite_index_.has_value() || !HasValidContext())
230 return;
231
232 auto* room = GetCurrentRoom();
233 if (!room)
234 return;
235
236 auto& sprites = room->GetSprites();
237 if (*selected_sprite_index_ >= sprites.size())
238 return;
239
241 sprites.erase(sprites.begin() +
242 static_cast<ptrdiff_t>(*selected_sprite_index_));
245}
246
248 int canvas_y) {
249 if (!HasValidContext())
250 return;
251
252 auto* room = GetCurrentRoom();
253 if (!room)
254 return;
255
256 auto [sprite_x, sprite_y] = CanvasToSpriteCoords(canvas_x, canvas_y);
257
258 // Clamp to valid range
259 sprite_x = std::clamp(sprite_x, 0, dungeon_coords::kSpriteGridMax);
260 sprite_y = std::clamp(sprite_y, 0, dungeon_coords::kSpriteGridMax);
261
263
264 // Create the sprite
265 zelda3::Sprite new_sprite(preview_sprite_id_, static_cast<uint8_t>(sprite_x),
266 static_cast<uint8_t>(sprite_y), 0, 0);
267
268 // Add sprite to room
269 room->GetSprites().push_back(new_sprite);
270
272}
273
275 int canvas_x, int canvas_y) const {
276 float scale = GetCanvasScale();
277 // Convert to pixel coordinates, then to sprite tile coordinates
278 int pixel_x = static_cast<int>(canvas_x / scale);
279 int pixel_y = static_cast<int>(canvas_y / scale);
280 return {pixel_x / dungeon_coords::kSpriteTileSize,
282}
283
284} // 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.
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.