yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
door_interaction_handler.cc
Go to the documentation of this file.
1// Related header
3
4// Third-party library headers
5#include "imgui/imgui.h"
6
7// Project headers
9
10namespace yaze::editor {
11
16
22
23bool DoorInteractionHandler::HandleClick(int canvas_x, int canvas_y) {
24 if (!HasValidContext()) return false;
25
27 PlaceDoorAtSnappedPosition(canvas_x, canvas_y);
28 return true;
29 }
30
31 // Try to select door at position
32 auto door_index = GetEntityAtPosition(canvas_x, canvas_y);
33 if (door_index.has_value()) {
34 SelectDoor(*door_index);
35 is_dragging_ = true;
36 drag_start_pos_ = ImVec2(static_cast<float>(canvas_x),
37 static_cast<float>(canvas_y));
39 return true;
40 }
41
43 return false;
44}
45
46void DoorInteractionHandler::HandleDrag(ImVec2 current_pos, ImVec2 delta) {
47 if (!is_dragging_ || !selected_door_index_.has_value()) return;
48
49 drag_current_pos_ = current_pos;
50}
51
53 if (!is_dragging_ || !selected_door_index_.has_value()) {
54 is_dragging_ = false;
55 return;
56 }
57
58 auto* room = GetCurrentRoom();
59 if (!room) {
60 is_dragging_ = false;
61 return;
62 }
63
64 int drag_x = static_cast<int>(drag_current_pos_.x);
65 int drag_y = static_cast<int>(drag_current_pos_.y);
66
67 // Detect wall from final position
68 zelda3::DoorDirection direction;
70 direction)) {
72 drag_x, drag_y, direction);
73
74 if (zelda3::DoorPositionManager::IsValidPosition(position, direction)) {
76
77 auto& doors = room->GetDoors();
78 if (*selected_door_index_ < doors.size()) {
79 doors[*selected_door_index_].position = position;
80 doors[*selected_door_index_].direction = direction;
81
82 // Re-encode bytes for ROM storage
83 auto [b1, b2] = doors[*selected_door_index_].EncodeBytes();
84 doors[*selected_door_index_].byte1 = b1;
85 doors[*selected_door_index_].byte2 = b2;
86
87 room->MarkObjectsDirty();
89 }
90 }
91 }
92
93 is_dragging_ = false;
94}
95
97 if (!door_placement_mode_ || !HasValidContext()) return;
98
99 auto* canvas = ctx_->canvas;
100 if (!canvas->IsMouseHovering()) return;
101
102 const ImGuiIO& io = ImGui::GetIO();
103 ImVec2 canvas_pos = canvas->zero_point();
104 int canvas_x = static_cast<int>(io.MousePos.x - canvas_pos.x);
105 int canvas_y = static_cast<int>(io.MousePos.y - canvas_pos.y);
106
107 // Try to update snapped position
108 if (!UpdateSnappedPosition(canvas_x, canvas_y)) {
109 return; // Not near a wall
110 }
111
112 // Get door position in tile coordinates
115
116 // Get door dimensions
118 int door_width_px = dims.width_tiles * 8;
119 int door_height_px = dims.height_tiles * 8;
120
121 // Convert to canvas pixel coordinates
122 auto [snap_canvas_x, snap_canvas_y] = RoomToCanvas(tile_x, tile_y);
123
124 // Draw ghost preview
125 ImDrawList* draw_list = ImGui::GetWindowDrawList();
126 float scale = GetCanvasScale();
127
128 ImVec2 preview_start(canvas_pos.x + snap_canvas_x * scale,
129 canvas_pos.y + snap_canvas_y * scale);
130 ImVec2 preview_end(preview_start.x + door_width_px * scale,
131 preview_start.y + door_height_px * scale);
132
133 const auto& theme = AgentUI::GetTheme();
134
135 // Draw semi-transparent filled rectangle
136 ImU32 fill_color =
137 IM_COL32(theme.dungeon_selection_primary.x * 255,
138 theme.dungeon_selection_primary.y * 255,
139 theme.dungeon_selection_primary.z * 255, 80);
140 draw_list->AddRectFilled(preview_start, preview_end, fill_color);
141
142 // Draw outline
143 ImVec4 outline_color = ImVec4(theme.dungeon_selection_primary.x,
144 theme.dungeon_selection_primary.y,
145 theme.dungeon_selection_primary.z, 0.9f);
146 draw_list->AddRect(preview_start, preview_end,
147 ImGui::GetColorU32(outline_color), 0.0f, 0, 2.0f);
148
149 // Draw door type label
150 std::string type_name(zelda3::GetDoorTypeName(preview_door_type_));
152 std::string label = type_name + " (" + dir_name + ")";
153
154 ImVec2 text_pos(preview_start.x, preview_start.y - 16 * scale);
155 draw_list->AddText(text_pos, IM_COL32(255, 255, 255, 200), label.c_str());
156}
157
159 if (!selected_door_index_.has_value() || !HasValidContext()) return;
160
161 auto* room = GetCurrentRoom();
162 if (!room) return;
163
164 const auto& doors = room->GetDoors();
165 if (*selected_door_index_ >= doors.size()) return;
166
167 const auto& door = doors[*selected_door_index_];
168 auto [tile_x, tile_y] = door.GetTileCoords();
169 auto dims = zelda3::GetDoorDimensions(door.direction);
170
171 // If dragging, use current drag position for door preview
172 if (is_dragging_) {
173 int drag_x = static_cast<int>(drag_current_pos_.x);
174 int drag_y = static_cast<int>(drag_current_pos_.y);
175
177 bool is_inner = false;
179 is_inner)) {
180 uint8_t snap_pos =
182 auto [snap_x, snap_y] =
184 tile_x = snap_x;
185 tile_y = snap_y;
186 dims = zelda3::GetDoorDimensions(dir);
187 }
188 }
189
190 ImDrawList* draw_list = ImGui::GetWindowDrawList();
191 ImVec2 canvas_pos = GetCanvasZeroPoint();
192 float scale = GetCanvasScale();
193
194 ImVec2 pos(canvas_pos.x + tile_x * 8 * scale,
195 canvas_pos.y + tile_y * 8 * scale);
196 ImVec2 size(dims.width_tiles * 8 * scale, dims.height_tiles * 8 * scale);
197
198 // Animated selection
199 static float pulse = 0.0f;
200 pulse += ImGui::GetIO().DeltaTime * 3.0f;
201 float alpha = 0.5f + 0.3f * sinf(pulse);
202
203 ImU32 color = IM_COL32(255, 165, 0, 180); // Orange
204 ImU32 fill_color = (color & 0x00FFFFFF) | (static_cast<ImU32>(alpha * 100) << 24);
205
206 draw_list->AddRectFilled(pos, ImVec2(pos.x + size.x, pos.y + size.y),
207 fill_color);
208 draw_list->AddRect(pos, ImVec2(pos.x + size.x, pos.y + size.y), color, 0.0f,
209 0, 2.0f);
210
211 // Draw label
212 ImVec2 text_pos(pos.x, pos.y - 14 * scale);
213 draw_list->AddText(text_pos, IM_COL32(255, 255, 255, 220), "Door");
214
215 // Draw snap indicators when dragging
216 if (is_dragging_) {
218 }
219}
220
222 int canvas_x, int canvas_y) const {
223 if (!HasValidContext()) return std::nullopt;
224
225 auto* room = ctx_->GetCurrentRoomConst();
226 if (!room) return std::nullopt;
227
228 // Convert screen coordinates to room coordinates
229 float scale = GetCanvasScale();
230 int room_x = static_cast<int>(canvas_x / scale);
231 int room_y = static_cast<int>(canvas_y / scale);
232
233 const auto& doors = room->GetDoors();
234 for (size_t i = 0; i < doors.size(); ++i) {
235 const auto& door = doors[i];
236
237 auto [tile_x, tile_y] = door.GetTileCoords();
238 auto dims = zelda3::GetDoorDimensions(door.direction);
239
240 int door_x = tile_x * 8;
241 int door_y = tile_y * 8;
242 int door_w = dims.width_tiles * 8;
243 int door_h = dims.height_tiles * 8;
244
245 if (room_x >= door_x && room_x < door_x + door_w && room_y >= door_y &&
246 room_y < door_y + door_h) {
247 return i;
248 }
249 }
250
251 return std::nullopt;
252}
253
258
260 selected_door_index_ = std::nullopt;
261 is_dragging_ = false;
262}
263
265 if (!selected_door_index_.has_value() || !HasValidContext()) return;
266
267 auto* room = GetCurrentRoom();
268 if (!room) return;
269
270 auto& doors = room->GetDoors();
271 if (*selected_door_index_ >= doors.size()) return;
272
274 doors.erase(doors.begin() + static_cast<ptrdiff_t>(*selected_door_index_));
275 room->MarkObjectsDirty();
278}
279
281 int canvas_y) {
282 if (!HasValidContext()) return;
283
284 auto* room = GetCurrentRoom();
285 if (!room) return;
286
287 // Detect wall from position
288 zelda3::DoorDirection direction;
290 direction)) {
291 return;
292 }
293
294 // Snap to nearest valid position
295 uint8_t position =
296 zelda3::DoorPositionManager::SnapToNearestPosition(canvas_x, canvas_y, direction);
297
298 // Validate position
299 if (!zelda3::DoorPositionManager::IsValidPosition(position, direction)) {
300 return;
301 }
302
304
305 // Create the door
306 zelda3::Room::Door new_door;
307 new_door.position = position;
308 new_door.type = preview_door_type_;
309 new_door.direction = direction;
310
311 // Encode bytes for ROM storage
312 auto [byte1, byte2] = new_door.EncodeBytes();
313 new_door.byte1 = byte1;
314 new_door.byte2 = byte2;
315
316 // Add door to room
317 room->AddDoor(new_door);
318
320}
321
322bool DoorInteractionHandler::UpdateSnappedPosition(int canvas_x, int canvas_y) {
323 zelda3::DoorDirection direction;
325 direction)) {
326 return false;
327 }
328
329 detected_door_direction_ = direction;
331 zelda3::DoorPositionManager::SnapToNearestPosition(canvas_x, canvas_y, direction);
332 return true;
333}
334
336 if (!is_dragging_ || !HasValidContext()) return;
337
338 int drag_x = static_cast<int>(drag_current_pos_.x);
339 int drag_y = static_cast<int>(drag_current_pos_.y);
340
341 zelda3::DoorDirection direction;
342 bool is_inner = false;
343 if (!zelda3::DoorPositionManager::DetectWallSection(drag_x, drag_y, direction,
344 is_inner)) {
345 return;
346 }
347
348 uint8_t start_pos =
350 uint8_t nearest_snap =
352
353 ImDrawList* draw_list = ImGui::GetWindowDrawList();
354 ImVec2 canvas_pos = GetCanvasZeroPoint();
355 float scale = GetCanvasScale();
356 const auto& theme = AgentUI::GetTheme();
357 auto dims = zelda3::GetDoorDimensions(direction);
358
359 // Draw indicators for 6 positions in this section
360 for (uint8_t i = 0; i < 6; ++i) {
361 uint8_t pos = start_pos + i;
362 auto [tile_x, tile_y] =
364 float pixel_x = tile_x * 8.0f;
365 float pixel_y = tile_y * 8.0f;
366
367 ImVec2 snap_start(canvas_pos.x + pixel_x * scale,
368 canvas_pos.y + pixel_y * scale);
369 ImVec2 snap_end(snap_start.x + dims.width_pixels() * scale,
370 snap_start.y + dims.height_pixels() * scale);
371
372 if (pos == nearest_snap) {
373 // Highlighted nearest position
374 ImVec4 highlight = ImVec4(theme.dungeon_selection_primary.x,
375 theme.dungeon_selection_primary.y,
376 theme.dungeon_selection_primary.z, 0.75f);
377 draw_list->AddRect(snap_start, snap_end, ImGui::GetColorU32(highlight),
378 0.0f, 0, 2.5f);
379 } else {
380 // Ghosted other positions
381 ImVec4 ghost = ImVec4(1.0f, 1.0f, 1.0f, 0.25f);
382 draw_list->AddRect(snap_start, snap_end, ImGui::GetColorU32(ghost), 0.0f,
383 0, 1.0f);
384 }
385 }
386}
387
388} // 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.
std::pair< int, int > RoomToCanvas(int room_x, int room_y) const
Convert room tile coordinates to canvas pixel coordinates.
ImVec2 GetCanvasZeroPoint() const
Get canvas zero point (for screen coordinate conversion)
void DrawSelectionHighlight() override
Draw selection highlight for selected entities.
void DrawSnapIndicators()
Draw snap position indicators during door drag.
void SelectDoor(size_t index)
Select door at index.
void DrawGhostPreview() override
Draw ghost preview during placement.
void HandleDrag(ImVec2 current_pos, ImVec2 delta) override
Handle mouse drag.
void CancelPlacement() override
Cancel current placement.
void BeginPlacement() override
Begin placement mode.
void HandleRelease() override
Handle mouse release.
bool HandleClick(int canvas_x, int canvas_y) override
Handle mouse click at canvas position.
bool UpdateSnappedPosition(int canvas_x, int canvas_y)
Update snapped position based on cursor.
void PlaceDoorAtSnappedPosition(int canvas_x, int canvas_y)
Place door at snapped position.
std::optional< size_t > GetEntityAtPosition(int canvas_x, int canvas_y) const override
Get entity at canvas position.
static uint8_t GetSectionStartPosition(DoorDirection direction, bool is_inner)
Get the starting position index for outer/inner section.
static bool DetectWallFromPosition(int canvas_x, int canvas_y, DoorDirection &out_direction)
Detect which wall the cursor is near.
static bool IsValidPosition(uint8_t position, DoorDirection direction)
Check if a position is valid for door placement.
static std::pair< int, int > PositionToTileCoords(uint8_t position, DoorDirection direction)
Convert encoded position to tile coordinates.
static bool DetectWallSection(int canvas_x, int canvas_y, DoorDirection &out_direction, bool &out_is_inner)
Detect wall with inner/outer section information.
static uint8_t SnapToNearestPosition(int canvas_x, int canvas_y, DoorDirection direction)
Convert canvas coordinates to nearest valid door position.
const AgentUITheme & GetTheme()
Editors are the view controllers for the application.
Definition agent_chat.cc:23
constexpr DoorDimensions GetDoorDimensions(DoorDirection dir)
Get door dimensions based on direction.
Definition door_types.h:192
constexpr std::string_view GetDoorDirectionName(DoorDirection dir)
Get human-readable name for door direction.
Definition door_types.h:161
constexpr std::string_view GetDoorTypeName(DoorType type)
Get human-readable name for door type.
Definition door_types.h:106
DoorDirection
Door direction on room walls.
Definition door_types.h:18
@ North
Top wall (horizontal door, 4x3 tiles)
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.
Represents a door in a dungeon room.
Definition room.h:263
uint8_t byte1
Original ROM byte 1 (position data)
Definition room.h:268
DoorType type
Door type (determines appearance/behavior)
Definition room.h:265
std::pair< uint8_t, uint8_t > EncodeBytes() const
Encode door data for ROM storage.
Definition room.h:302
DoorDirection direction
Which wall the door is on.
Definition room.h:266
uint8_t position
Encoded position (5-bit, 0-31)
Definition room.h:264
uint8_t byte2
Original ROM byte 2 (type + direction)
Definition room.h:269