yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
base_entity_handler.h
Go to the documentation of this file.
1#ifndef YAZE_APP_EDITOR_DUNGEON_INTERACTION_BASE_ENTITY_HANDLER_H_
2#define YAZE_APP_EDITOR_DUNGEON_INTERACTION_BASE_ENTITY_HANDLER_H_
3
4#include <algorithm>
5#include <optional>
6#include <utility>
7
13#include "imgui/imgui.h"
14
15namespace yaze {
16namespace editor {
17
29 public:
30 virtual ~BaseEntityHandler() = default;
31
38 void SetContext(InteractionContext* ctx) { ctx_ = ctx; }
39
43 InteractionContext* context() const { return ctx_; }
44
45 // ========================================================================
46 // Placement Lifecycle
47 // ========================================================================
48
55 virtual void BeginPlacement() = 0;
56
63 virtual void CancelPlacement() = 0;
64
68 virtual bool IsPlacementActive() const = 0;
69
70 // ========================================================================
71 // Mouse Interaction
72 // ========================================================================
73
81 virtual bool HandleClick(int canvas_x, int canvas_y) = 0;
82
89 virtual void HandleDrag(ImVec2 current_pos, ImVec2 delta) = 0;
90
96 virtual void HandleRelease() = 0;
97 virtual bool HandleMouseWheel(float delta) { return false; }
98
99 // ========================================================================
100 // Rendering
101 // ========================================================================
102
109 virtual void DrawGhostPreview() = 0;
110
116 virtual void DrawSelectionHighlight() = 0;
117
118 // ========================================================================
119 // Hit Testing
120 // ========================================================================
121
129 virtual std::optional<size_t> GetEntityAtPosition(int canvas_x,
130 int canvas_y) const = 0;
131
132 // Per-frame toast render called unconditionally by InteractionCoordinator so
133 // the "Placed" message remains visible even after the user exits placement
134 // mode immediately after a successful click.
136 if (ImGui::GetCurrentContext() == nullptr)
137 return;
139 "Placed", ImGui::GetColorU32(AgentUI::GetTheme().status_success));
140 }
141
142 protected:
144
145 // ========================================================================
146 // Placement Success Toast (shared by all derived handlers)
147 // ========================================================================
148
149 // Call after a successful entity placement. Extends the toast display by
150 // kToastDuration seconds; rapid placements simply prolong the same toast.
152 if (ImGui::GetCurrentContext() == nullptr)
153 return;
154 toast_expire_time_ = static_cast<float>(ImGui::GetTime()) + kToastDuration;
155 }
156
157 // Draw a fading "OK" toast near the canvas origin.
158 // @param msg Short message to display (e.g. "Placed").
159 // @param color Opaque RGBA colour for the text (alpha will be modulated).
160 void DrawSuccessToastOverlay(const char* msg, ImU32 color) const {
161 if (ImGui::GetCurrentContext() == nullptr)
162 return;
163 if (toast_expire_time_ <= 0.0f || ImGui::GetTime() >= toast_expire_time_) {
164 return;
165 }
166 float remaining = toast_expire_time_ - static_cast<float>(ImGui::GetTime());
167 // Fade out during the last 0.4 s; keep full opacity the rest of the time.
168 float alpha = std::min(1.0f, remaining / 0.4f);
169 // Blend alpha into supplied colour.
170 ImU32 base_rgb = color & 0x00FFFFFFu;
171 ImU32 alpha_ch = static_cast<ImU32>(alpha * 255.0f) << 24;
172 ImU32 toast_color = base_rgb | alpha_ch;
173
174 ImVec2 canvas_pos = GetCanvasZeroPoint();
175 ImDrawList* draw_list = ImGui::GetWindowDrawList();
176 // Render near the top-left canvas corner so it never overlaps tiles.
177 draw_list->AddText(ImVec2(canvas_pos.x + 8.0f, canvas_pos.y + 6.0f),
178 toast_color, msg);
179 }
180
181 // Shared toast expiry timestamp (seconds, from ImGui::GetTime()).
182 float toast_expire_time_ = 0.0f;
183
184 static constexpr float kToastDuration = 1.5f;
185
186 // ========================================================================
187 // Helper Methods (available to all derived handlers)
188 // ========================================================================
189
193 std::pair<int, int> RoomToCanvas(int room_x, int room_y) const {
194 return dungeon_coords::RoomToCanvas(room_x, room_y);
195 }
196
200 std::pair<int, int> CanvasToRoom(int canvas_x, int canvas_y) const {
201 return dungeon_coords::CanvasToRoom(canvas_x, canvas_y);
202 }
203
207 bool IsWithinBounds(int canvas_x, int canvas_y) const {
208 return dungeon_coords::IsWithinBounds(canvas_x, canvas_y);
209 }
210
214 ImVec2 GetCanvasZeroPoint() const {
215 if (!ctx_ || !ctx_->canvas)
216 return ImVec2(0, 0);
217 return ctx_->canvas->zero_point();
218 }
219
221 if (!ctx_ || !ctx_->canvas) {
222 return DungeonCanvasTransform(ImVec2(0, 0), ImVec2(0, 0), 1.0f);
223 }
227 }
228
229 std::optional<ImVec2> GetPointerScreenPosition() const {
230 if (ImGui::GetCurrentContext() == nullptr || !ctx_ || !ctx_->canvas) {
231 return std::nullopt;
232 }
233
234 const DungeonCanvasTransform transform = GetCanvasTransform();
235 const auto is_within_room = [&transform](ImVec2 screen_pos) {
236 const auto [room_x, room_y] =
237 transform.ScreenToRoomPixelCoordinates(screen_pos);
238 return dungeon_coords::IsWithinBounds(room_x, room_y);
239 };
240
241 if (ctx_->canvas->IsMouseHovering()) {
242 const ImVec2 mouse_pos = ImGui::GetIO().MousePos;
243 if (is_within_room(mouse_pos)) {
244 return mouse_pos;
245 }
246 }
247
249 return std::nullopt;
250 }
251
252 for (int i = 0; i < gui::TouchInput::kMaxTouchPoints; ++i) {
253 const auto touch = gui::TouchInput::GetTouchPoint(i);
254 if (!touch.active) {
255 continue;
256 }
257 if (is_within_room(touch.position)) {
258 return touch.position;
259 }
260 }
261
262 return std::nullopt;
263 }
264
268 bool HasValidContext() const { return ctx_ && ctx_->IsValid(); }
269
274 return ctx_ ? ctx_->GetCurrentRoom() : nullptr;
275 }
276};
277
278} // namespace editor
279} // namespace yaze
280
281#endif // YAZE_APP_EDITOR_DUNGEON_INTERACTION_BASE_ENTITY_HANDLER_H_
Abstract base class for entity interaction handlers.
virtual void HandleRelease()=0
Handle mouse release.
virtual bool IsPlacementActive() const =0
Check if placement mode is active.
virtual void BeginPlacement()=0
Begin placement mode.
InteractionContext * context() const
Get the interaction context.
std::optional< ImVec2 > GetPointerScreenPosition() const
virtual void HandleDrag(ImVec2 current_pos, ImVec2 delta)=0
Handle mouse drag.
virtual bool HandleClick(int canvas_x, int canvas_y)=0
Handle mouse click at canvas position.
virtual void DrawSelectionHighlight()=0
Draw selection highlight for selected entities.
virtual void DrawGhostPreview()=0
Draw ghost preview during placement.
DungeonCanvasTransform GetCanvasTransform() const
std::pair< int, int > CanvasToRoom(int canvas_x, int canvas_y) const
Convert canvas pixel coordinates to room tile coordinates.
void DrawSuccessToastOverlay(const char *msg, ImU32 color) const
bool IsWithinBounds(int canvas_x, int canvas_y) const
Check if coordinates are within room bounds.
virtual void CancelPlacement()=0
Cancel current placement.
zelda3::Room * GetCurrentRoom() const
Get current room (convenience method)
virtual ~BaseEntityHandler()=default
bool HasValidContext() const
Check if context is valid.
virtual bool HandleMouseWheel(float delta)
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)
virtual std::optional< size_t > GetEntityAtPosition(int canvas_x, int canvas_y) const =0
Get entity at canvas position.
void SetContext(InteractionContext *ctx)
Set the interaction context.
static constexpr float kToastDuration
std::pair< int, int > ScreenToRoomPixelCoordinates(ImVec2 screen) const
auto global_scale() const
Definition canvas.h:397
auto zero_point() const
Definition canvas.h:348
bool IsMouseHovering() const
Definition canvas.h:338
auto scrolling() const
Definition canvas.h:350
static TouchPoint GetTouchPoint(int index)
Get raw touch point data.
static bool IsTouchActive()
Check if touch input is currently being used.
static constexpr int kMaxTouchPoints
Maximum number of simultaneous touch points supported.
const AgentUITheme & GetTheme()
std::pair< int, int > RoomToCanvas(int room_x, int room_y)
Convert room tile coordinates to canvas pixel coordinates.
bool IsWithinBounds(int canvas_x, int canvas_y, int margin=0)
Check if coordinates are within room bounds.
std::pair< int, int > CanvasToRoom(int canvas_x, int canvas_y)
Convert canvas pixel coordinates to room tile coordinates.
Shared context for all interaction handlers.
bool IsValid() const
Check if context has required dependencies.
zelda3::Room * GetCurrentRoom() const
Get pointer to current room.