10#include <simplesquirrel/simplesquirrel.hpp>
26 throw Exception(
"Motion.loops: unknown mode '%s' (use restart|yoyo)", mode.c_str());
29Animation *requireAnimation(Animation *animation) {
30 if (!animation)
throw Exception(
"MotionSequence: Animation is null");
34class ScriptMotionHandle {
36 ScriptMotionHandle(MotionRuntime *runtime, MotionHandle
handle)
37 : runtime_(runtime), handle_(
handle) {}
39 bool isActive()
const {
return runtime_ && runtime_->isActive(handle_); }
42 if (!runtime_)
throw Exception(
"Motion.value: no runtime");
43 auto result = runtime_->floatValue(handle_);
44 if (!result.ok()) throwMotionError(result.error());
45 return result.value();
49 if (!runtime_)
throw Exception(
"Motion.complete: no runtime");
50 auto result = runtime_->complete(handle_);
51 if (!result.ok()) throwMotionError(result.error());
55 if (!runtime_)
throw Exception(
"Motion.cancel: no runtime");
56 auto result = runtime_->cancel(handle_);
57 if (!result.ok()) throwMotionError(result.error());
61 MotionRuntime *runtime_ =
nullptr;
62 MotionHandle handle_{};
65class ScriptMotionBuilder {
67 ScriptMotionBuilder(Animation *animation,
float from,
float to,
float duration)
68 : animation_(animation) {
69 if (!animation_)
throw Exception(
"MotionBuilder: Animation is null");
75 void ease(std::string
kind) { desc_.ease = std::move(
kind); }
76 void delay(
float seconds) { desc_.delay = seconds; }
77 void loops(
int count, std::string mode =
"restart") {
79 desc_.loopMode = parseLoopMode(mode);
81 void cancelOnError(
bool enabled) { desc_.cancelOnError =
enabled; }
82 void frequency(
int count) { desc_.frequency =
count; }
83 void dampingRatio(
float ratio) { desc_.dampingRatio = ratio; }
84 void seed(
int value) { desc_.seed =
static_cast<std::uint32_t
>(
value); }
88 ScriptMotionHandle *
run() {
92 auto spawned = animation_->motions().spawnFloat(desc_);
94 return new ScriptMotionHandle(&animation_->motions(),
spawned.value());
97 [[nodiscard]] MotionRuntime &runtime() {
99 return animation_->motions();
110 if (!animation_)
throw Exception(
"MotionBuilder: Animation is null");
111 if (consumed_)
throw Exception(
"MotionBuilder: already consumed (run/append)");
115 MotionRuntime::FloatDesc desc_{};
116 bool consumed_ =
false;
119class ScriptMotionSequenceHandle {
121 explicit ScriptMotionSequenceHandle(MotionSequenceHandle
handle) : handle_(
std::move(
handle)) {}
123 bool isActive()
const {
return handle_.isActive(); }
124 int childCount()
const {
return handle_.childCount(); }
127 auto result = handle_.complete();
128 if (!result.ok()) throwMotionError(result.error());
132 auto result = handle_.cancel();
133 if (!result.ok()) throwMotionError(result.error());
137 MotionSequenceHandle handle_;
140class ScriptMotionSequence {
142 explicit ScriptMotionSequence(Animation *animation)
143 : animation_(animation), sequence_(requireAnimation(animation)->motions()) {}
145 void append(ScriptMotionBuilder *builder) {
146 if (!builder)
throw Exception(
"MotionSequence.append: builder is null");
147 auto taken = builder->takeDesc();
148 if (!taken.ok()) throwMotionError(taken.error());
149 auto desc = std::move(taken).value();
150 MotionBuilder bridge(animation_->motions(),
desc.from,
desc.to,
desc.duration);
151 bridge.ease(
desc.ease).delay(
desc.delay).loops(
desc.loops,
desc.loopMode).cancelOnError(
desc.cancelOnError)
152 .style(
desc.style).frequency(
desc.frequency).dampingRatio(
desc.dampingRatio).seed(
desc.seed);
153 if (
desc.sink) bridge.to(*
desc.sink);
154 auto scheduled = sequence_.append(std::move(bridge));
158 void join(ScriptMotionBuilder *builder) {
159 if (!builder)
throw Exception(
"MotionSequence.join: builder is null");
160 auto taken = builder->takeDesc();
161 if (!taken.ok()) throwMotionError(taken.error());
162 auto desc = std::move(taken).value();
163 MotionBuilder bridge(animation_->motions(),
desc.from,
desc.to,
desc.duration);
164 bridge.ease(
desc.ease).delay(
desc.delay).loops(
desc.loops,
desc.loopMode).cancelOnError(
desc.cancelOnError)
165 .style(
desc.style).frequency(
desc.frequency).dampingRatio(
desc.dampingRatio).seed(
desc.seed);
166 if (
desc.sink) bridge.to(*
desc.sink);
167 auto scheduled = sequence_.join(std::move(bridge));
171 void insert(
float atSeconds, ScriptMotionBuilder *builder) {
172 if (!builder)
throw Exception(
"MotionSequence.insert: builder is null");
173 auto taken = builder->takeDesc();
174 if (!taken.ok()) throwMotionError(taken.error());
175 auto desc = std::move(taken).value();
176 MotionBuilder bridge(animation_->motions(),
desc.from,
desc.to,
desc.duration);
177 bridge.ease(
desc.ease).delay(
desc.delay).loops(
desc.loops,
desc.loopMode).cancelOnError(
desc.cancelOnError)
178 .style(
desc.style).frequency(
desc.frequency).dampingRatio(
desc.dampingRatio).seed(
desc.seed);
179 if (
desc.sink) bridge.to(*
desc.sink);
180 auto scheduled = sequence_.insert(atSeconds, std::move(bridge));
184 void appendInterval(
float seconds) {
185 auto scheduled = sequence_.appendInterval(seconds);
189 float cursor()
const {
return sequence_.cursor(); }
190 float duration()
const {
return sequence_.duration(); }
191 int itemCount()
const {
return sequence_.itemCount(); }
193 ScriptMotionSequenceHandle *
run() {
194 auto started = sequence_.run();
196 return new ScriptMotionSequenceHandle(std::move(
started).
value());
201 MotionSequence sequence_;
207 auto motion = table.addClass<ScriptMotionHandle>(
208 "Motion", std::function<ScriptMotionHandle *()>([]() -> ScriptMotionHandle * {
return nullptr; }),
210 motion.addFunc(
"isActive", &ScriptMotionHandle::isActive);
211 motion.addFunc(
"value", &ScriptMotionHandle::value);
212 motion.addFunc(
"complete", &ScriptMotionHandle::complete);
213 motion.addFunc(
"cancel", &ScriptMotionHandle::cancel);
215 auto builder = table.addClass<ScriptMotionBuilder>(
217 std::function<ScriptMotionBuilder *()>([]() -> ScriptMotionBuilder * {
return nullptr; }),
true);
218 builder.addFunc(
"ease", &ScriptMotionBuilder::ease);
219 builder.addFunc(
"delay", &ScriptMotionBuilder::delay);
220 builder.addFunc(
"loops", &ScriptMotionBuilder::loops);
221 builder.addFunc(
"cancelOnError", &ScriptMotionBuilder::cancelOnError);
222 builder.addFunc(
"frequency", &ScriptMotionBuilder::frequency);
223 builder.addFunc(
"dampingRatio", &ScriptMotionBuilder::dampingRatio);
224 builder.addFunc(
"seed", &ScriptMotionBuilder::seed);
225 builder.addFunc(
"run", &ScriptMotionBuilder::run);
227 auto seqHandle = table.addClass<ScriptMotionSequenceHandle>(
228 "MotionSequenceHandle",
229 std::function<ScriptMotionSequenceHandle *()>([]() -> ScriptMotionSequenceHandle * {
return nullptr; }),
231 seqHandle.addFunc(
"isActive", &ScriptMotionSequenceHandle::isActive);
232 seqHandle.addFunc(
"childCount", &ScriptMotionSequenceHandle::childCount);
233 seqHandle.addFunc(
"complete", &ScriptMotionSequenceHandle::complete);
234 seqHandle.addFunc(
"cancel", &ScriptMotionSequenceHandle::cancel);
236 auto seq = table.addClass<ScriptMotionSequence>(
238 std::function<ScriptMotionSequence *()>([]() -> ScriptMotionSequence * {
return nullptr; }),
true);
239 seq.addFunc(
"append", &ScriptMotionSequence::append);
240 seq.addFunc(
"join", &ScriptMotionSequence::join);
241 seq.addFunc(
"insert", &ScriptMotionSequence::insert);
242 seq.addFunc(
"appendInterval", &ScriptMotionSequence::appendInterval);
243 seq.addFunc(
"cursor", &ScriptMotionSequence::cursor);
244 seq.addFunc(
"duration", &ScriptMotionSequence::duration);
245 seq.addFunc(
"itemCount", &ScriptMotionSequence::itemCount);
246 seq.addFunc(
"run", &ScriptMotionSequence::run);
248 animationClass.addFunc(
250 std::function<ScriptMotionBuilder *(
Animation *,
float,
float,
float)>(
254 animationClass.addFunc(
256 std::function<ScriptMotionBuilder *(
Animation *,
float,
float,
float)>(
262 animationClass.addFunc(
264 std::function<ScriptMotionBuilder *(
Animation *,
float,
float,
float)>(
270 animationClass.addFunc(
272 std::function<ScriptMotionSequence *(
Animation *)>(
273 [](
Animation *self) -> ScriptMotionSequence * {
return new ScriptMotionSequence(self); }));
274 animationClass.addFunc(
275 "ensureMotionCapacity",
278 if (!self)
throw Exception(
"ensureMotionCapacity: Animation is null");
279 if (
count < 0)
throw Exception(
"ensureMotionCapacity: count must be >= 0");
282 animationClass.addFunc(
283 "getMotionFloatCapacity",
286 if (!self)
throw Exception(
"getMotionFloatCapacity: Animation is null");
289 animationClass.addFunc(
290 "getMotionFloatFreeCount",
293 if (!self)
throw Exception(
"getMotionFloatFreeCount: Animation is null");
HexCoordinates to
Cell the unit walks towards on this segment.
Fluent LitMotion-style builder that commits into MotionRuntime.
Dense motion storage advanced by Animation::advance(SimulationStep).
Squirrel GC wrappers for Motion builder / handle / sequence.
Lean LitMotion-style sequence: Append / Join / Insert / AppendInterval.
LitMotion-style typed motion handles, values, and sink interfaces.
A structured explanation of a failed, degraded, or noteworthy result.
const std::string & message() const noexcept
Human-readable explanation.
EVENGINE_API_FOUNDATION public API.
Move-only operation result carrying either a value or Status.
static Result success(T value)
Construct a successful result owning value.
Animation module — tween factory + 2D sprite-sheet / Spine + 3D skeletal playback (player / state mac...
std::size_t getMotionFloatFreeCount() const noexcept
Returns the motion float free count.
std::size_t getMotionFloatCapacity() const noexcept
Returns the motion float capacity.
void ensureMotionCapacity(std::size_t floatCount)
Pre-size motion float pool for churn-free spawn (Phase 4).
MotionLoopMode
Loop behaviour after a cycle completes.
@ Restart
Jump back to start each cycle.
@ Yoyo
Alternate direction each cycle.
void exposeMotionScriptBindings(ssq::Table &table, ssq::Class &animationClass)
Register Motion / MotionSequence script types and Animation factories.
@ Punch
Finite damped sine punch about from.
@ Shake
Punch with per-axis deterministic random sign.