yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
tile_object_handler.h
Go to the documentation of this file.
1#ifndef YAZE_APP_EDITOR_DUNGEON_INTERACTION_TILE_OBJECT_HANDLER_H
2#define YAZE_APP_EDITOR_DUNGEON_INTERACTION_TILE_OBJECT_HANDLER_H
3
4#include <functional>
5#include <optional>
6
7#include <utility>
8#include <vector>
9#include "absl/status/status.h"
14#include "zelda3/dungeon/room.h"
16
17namespace yaze::editor {
18
26 public:
38
45
46 enum class PlacementPolicy { kRepeat, kOnce };
47
50
51 // ========================================================================
52 // BaseEntityHandler interface
53 // ========================================================================
54
55 void BeginPlacement() override;
56 void CancelPlacement() override;
57 bool IsPlacementActive() const override { return object_placement_mode_; }
58
59 bool HandleClick(int canvas_x, int canvas_y) override;
60 void HandleDrag(ImVec2 current_pos, ImVec2 delta) override;
61 void HandleRelease() override;
62 bool HandleMouseWheel(float delta) override;
63
64 void DrawGhostPreview() override;
65 void DrawSelectionHighlight() override;
66
67 void InitDrag(const ImVec2& start_pos);
68
69 // ========================================================================
70 // Marquee (Rectangle) Selection
71 // ========================================================================
72
73 // Begin a rectangle selection drag on empty space (canvas-local coords).
74 void BeginMarqueeSelection(const ImVec2& start_pos);
75
76 // Update/draw active marquee selection and finalize on mouse release.
77 void HandleMarqueeSelection(const ImVec2& mouse_pos, bool mouse_left_down,
78 bool mouse_left_released, bool shift_down,
79 bool toggle_down, bool alt_down,
80 bool draw_box = true);
81
82 std::optional<size_t> GetEntityAtPosition(int canvas_x,
83 int canvas_y) const override;
84
89 bool MoveObjects(int room_id, const std::vector<size_t>& indices, int delta_x,
90 int delta_y, bool notify_mutation = true);
91
96 std::vector<size_t> DuplicateObjects(int room_id,
97 const std::vector<size_t>& indices,
98 int delta_x, int delta_y);
99
103 bool DeleteObjects(int room_id, std::vector<size_t> indices);
104
108 bool DeleteAllObjects(int room_id);
109
113 void SendToFront(int room_id, const std::vector<size_t>& indices);
114 void SendToBack(int room_id, const std::vector<size_t>& indices);
115 void MoveForward(int room_id, const std::vector<size_t>& indices);
116 void MoveBackward(int room_id, const std::vector<size_t>& indices);
117
123 bool ResizeObjects(int room_id, const std::vector<size_t>& indices, int delta,
124 bool horizontal = false, bool uniform = false);
125
129 bool PlaceObjectAt(int room_id, const zelda3::RoomObject& object, int x,
130 int y);
131
133 placement_policy_ = policy;
134 }
137 std::function<absl::Status(int, const std::vector<zelda3::RoomObject>&,
138 const std::vector<chest_data>&)>;
140 object_mutation_preflight_ = std::move(callback);
141 }
142 // Runs only after a discrete command passes preflight, before publishing it.
143 // Continuous drag increments must not finish their own gesture.
144 void SetBeforeObjectCommandCallback(std::function<void()> callback) {
145 before_object_command_callback_ = std::move(callback);
146 }
147 const absl::Status& mutation_status() const { return mutation_status_; }
149 std::function<void(const absl::Status&)> callback) {
150 mutation_error_callback_ = std::move(callback);
151 }
152
154 std::function<void(const zelda3::RoomObject&)> callback) {
155 placement_callback_ = std::move(callback);
156 }
157
169
170 void UpdateObjectsId(int room_id, const std::vector<size_t>& indices,
171 int16_t new_id);
172
173 void UpdateObjectsSize(int room_id, const std::vector<size_t>& indices,
174 uint8_t new_size);
175
176 // Returns false when the request is rejected. Accepted no-op updates return
177 // true so inspector controls can distinguish validation failures from an
178 // already-selected target.
179 bool UpdateObjectsLayer(int room_id, const std::vector<size_t>& indices,
180 int new_layer);
181
185 void SetPreviewObject(const zelda3::RoomObject& object);
187
188 // Preview edits do not mutate the room or create undo entries. Accepted
189 // no-op updates return true; unavailable sizes/layers return false.
190 bool SetPreviewSize(uint8_t size);
191 bool SetPreviewLayer(int layer);
192
195
197 static GhostPreviewGeometry CalculateGhostPreviewGeometry(
198 const zelda3::RoomObject& object);
199
203
204 // ========================================================================
205 // Clipboard Operations
206 // ========================================================================
207
211 void CopyObjectsToClipboard(int room_id, const std::vector<size_t>& indices);
212
217 std::vector<size_t> PasteFromClipboard(int room_id, int offset_x,
218 int offset_y);
219
224 std::vector<size_t> PasteFromClipboardAt(int room_id, int target_x,
225 int target_y);
226
230 bool HasClipboardData() const { return !clipboard_.empty(); }
231
236 clipboard_.clear();
237 clipboard_chests_.clear();
238 clipboard_status_ = absl::OkStatus();
239 }
240
241 private:
242 // Placement state
245 std::function<void(const zelda3::RoomObject&)> placement_callback_;
248 std::unique_ptr<gfx::BackgroundBuffer> ghost_preview_buffer_;
253
254 // Clipboard
255 std::vector<zelda3::RoomObject> clipboard_;
256 std::vector<std::optional<chest_data>> clipboard_chests_;
257 absl::Status clipboard_status_;
258 absl::Status mutation_status_;
260 std::function<void()> before_object_command_callback_;
261 std::function<void(const absl::Status&)> mutation_error_callback_;
262 bool RejectMutation(absl::Status status);
263
264 bool CommitCandidate(
265 int room_id, std::vector<zelda3::RoomObject> candidate,
266 const std::vector<std::optional<size_t>>& sources,
267 const std::vector<std::optional<chest_data>>& chest_overrides = {},
268 std::optional<std::vector<size_t>> selection = std::nullopt);
269
271
272 // Drag state
273 bool is_dragging_ = false;
274 ImVec2 drag_start_{0, 0};
275 ImVec2 drag_current_{0, 0};
280 std::optional<std::pair<int, int>> drag_rejected_delta_;
281
282 ImVec2 ApplyDragModifiers(const ImVec2& delta) const;
283 void DrawSmartGuides(const std::vector<zelda3::RoomObject>& objects) const;
284
285 zelda3::Room* GetRoom(int room_id);
286 void NotifyChange(zelda3::Room* room);
287};
288
289} // namespace yaze::editor
290
291#endif // YAZE_APP_EDITOR_DUNGEON_INTERACTION_TILE_OBJECT_HANDLER_H
Abstract base class for entity interaction handlers.
void SetContext(InteractionContext *ctx)
Set the interaction context.
Handles functional mutations and queries for tile objects.
void SetPlacementCallback(std::function< void(const zelda3::RoomObject &)> callback)
bool DeleteObjects(int room_id, std::vector< size_t > indices)
Delete objects by indices.
void DrawGhostPreview() override
Draw ghost preview during placement.
void DrawSmartGuides(const std::vector< zelda3::RoomObject > &objects) const
void RefreshPreviewGraphics()
Refresh graphics without replacing the current placement geometry.
std::vector< zelda3::RoomObject > clipboard_
void SetObjectMutationPreflight(ObjectMutationPreflight callback)
bool was_placement_blocked() const
True if the most recent PlaceObjectAt was blocked.
void HandleMarqueeSelection(const ImVec2 &mouse_pos, bool mouse_left_down, bool mouse_left_released, bool shift_down, bool toggle_down, bool alt_down, bool draw_box=true)
void BeginPlacement() override
Begin placement mode.
bool HandleMouseWheel(float delta) override
std::unique_ptr< gfx::BackgroundBuffer > ghost_preview_buffer_
void ClearClipboard()
Clear the clipboard.
ObjectMutationPreflight object_mutation_preflight_
bool HasClipboardData() const
Check if clipboard has data.
void UpdateObjectsSize(int room_id, const std::vector< size_t > &indices, uint8_t new_size)
bool MoveObjects(int room_id, const std::vector< size_t > &indices, int delta_x, int delta_y, bool notify_mutation=true)
Move a set of objects by a tile delta.
PlacementPolicy GetPlacementPolicy() const
void SendToFront(int room_id, const std::vector< size_t > &indices)
Reorder objects.
void SetMutationErrorCallback(std::function< void(const absl::Status &)> callback)
const gfx::BackgroundBuffer * ghost_preview_buffer_for_testing() const
std::vector< std::optional< chest_data > > clipboard_chests_
const zelda3::RoomObject & GetPreviewObject() const
void MoveBackward(int room_id, const std::vector< size_t > &indices)
bool CommitCandidate(int room_id, std::vector< zelda3::RoomObject > candidate, const std::vector< std::optional< size_t > > &sources, const std::vector< std::optional< chest_data > > &chest_overrides={}, std::optional< std::vector< size_t > > selection=std::nullopt)
ImVec2 ApplyDragModifiers(const ImVec2 &delta) const
void HandleRelease() override
Handle mouse release.
void CopyObjectsToClipboard(int room_id, const std::vector< size_t > &indices)
Copy objects to internal clipboard.
void HandleDrag(ImVec2 current_pos, ImVec2 delta) override
Handle mouse drag.
std::vector< size_t > DuplicateObjects(int room_id, const std::vector< size_t > &indices, int delta_x, int delta_y)
Clone a set of objects and move them by a tile delta.
std::vector< size_t > PasteFromClipboardAt(int room_id, int target_x, int target_y)
Paste objects from clipboard at target location. Use first clipboard item as origin.
void SetBeforeObjectCommandCallback(std::function< void()> callback)
std::optional< std::pair< int, int > > drag_rejected_delta_
void DrawSelectionHighlight() override
Draw selection highlight for selected entities.
bool IsPlacementActive() const override
Check if placement mode is active.
bool UpdateObjectsLayer(int room_id, const std::vector< size_t > &indices, int new_layer)
bool PlaceObjectAt(int room_id, const zelda3::RoomObject &object, int x, int y)
Place a new object. Returns false if blocked by ROM limits.
void SendToBack(int room_id, const std::vector< size_t > &indices)
std::vector< size_t > PasteFromClipboard(int room_id, int offset_x, int offset_y)
Paste objects from clipboard with offset.
bool HandleClick(int canvas_x, int canvas_y) override
Handle mouse click at canvas position.
std::function< void()> before_object_command_callback_
void SetPreviewObject(const zelda3::RoomObject &object)
Set object for placement.
PlacementBlockReason placement_block_reason() const
void BeginMarqueeSelection(const ImVec2 &start_pos)
void MoveForward(int room_id, const std::vector< size_t > &indices)
bool RejectMutation(absl::Status status)
void CancelPlacement() override
Cancel current placement.
std::function< void(const zelda3::RoomObject &) placement_callback_)
std::function< absl::Status(int, const std::vector< zelda3::RoomObject > &, const std::vector< chest_data > &)> ObjectMutationPreflight
PlacementBlockReason placement_block_reason_
bool DeleteAllObjects(int room_id)
Delete all objects in a room.
TileObjectHandler(InteractionContext *ctx)
static GhostPreviewGeometry CalculateGhostPreviewGeometry(const zelda3::RoomObject &object)
Resolve the render anchor and visual extent used by placement previews.
bool ResizeObjects(int room_id, const std::vector< size_t > &indices, int delta, bool horizontal=false, bool uniform=false)
Resize objects by a delta. Uniform changes both packed axes; otherwise horizontal selects packed-floo...
void UpdateObjectsId(int room_id, const std::vector< size_t > &indices, int16_t new_id)
const absl::Status & mutation_status() const
zelda3::Room * GetRoom(int room_id)
PlacementCapacityState GhostCapacityState
std::function< void(const absl::Status &) mutation_error_callback_)
void SetPlacementPolicy(PlacementPolicy policy)
void NotifyChange(zelda3::Room *room)
GhostCapacityState GetPlacementGhostCapacityState() const
std::optional< size_t > GetEntityAtPosition(int canvas_x, int canvas_y) const override
Get entity at canvas position.
void InitDrag(const ImVec2 &start_pos)
Editors are the view controllers for the application.
Shared context for all interaction handlers.