@zylem/game-lib 0.6.2 → 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +9 -16
  3. package/dist/actions.d.ts +30 -21
  4. package/dist/actions.js +628 -145
  5. package/dist/actions.js.map +1 -1
  6. package/dist/behavior/platformer-3d.d.ts +296 -0
  7. package/dist/behavior/platformer-3d.js +518 -0
  8. package/dist/behavior/platformer-3d.js.map +1 -0
  9. package/dist/behavior/ricochet-2d.d.ts +274 -0
  10. package/dist/behavior/ricochet-2d.js +394 -0
  11. package/dist/behavior/ricochet-2d.js.map +1 -0
  12. package/dist/behavior/screen-wrap.d.ts +86 -0
  13. package/dist/behavior/screen-wrap.js +195 -0
  14. package/dist/behavior/screen-wrap.js.map +1 -0
  15. package/dist/behavior/thruster.d.ts +10 -0
  16. package/dist/behavior/thruster.js +234 -0
  17. package/dist/behavior/thruster.js.map +1 -0
  18. package/dist/behavior/world-boundary-2d.d.ts +141 -0
  19. package/dist/behavior/world-boundary-2d.js +181 -0
  20. package/dist/behavior/world-boundary-2d.js.map +1 -0
  21. package/dist/behavior-descriptor-BWNWmIjv.d.ts +142 -0
  22. package/dist/{blueprints-Cq3Ko6_G.d.ts → blueprints-BWGz8fII.d.ts} +2 -2
  23. package/dist/camera-B5e4c78l.d.ts +468 -0
  24. package/dist/camera.d.ts +3 -2
  25. package/dist/camera.js +900 -211
  26. package/dist/camera.js.map +1 -1
  27. package/dist/composition-DrzFrbqI.d.ts +218 -0
  28. package/dist/{core-bO8TzV7u.d.ts → core-DAkskq6Y.d.ts} +60 -62
  29. package/dist/core.d.ts +10 -6
  30. package/dist/core.js +6896 -5020
  31. package/dist/core.js.map +1 -1
  32. package/dist/{entities-DvByhMGU.d.ts → entities-DC9ce_vx.d.ts} +113 -3
  33. package/dist/entities.d.ts +5 -3
  34. package/dist/entities.js +3727 -3167
  35. package/dist/entities.js.map +1 -1
  36. package/dist/entity-BpbZqg19.d.ts +1100 -0
  37. package/dist/global-change-Dc8uCKi2.d.ts +25 -0
  38. package/dist/main.d.ts +418 -15
  39. package/dist/main.js +11384 -8515
  40. package/dist/main.js.map +1 -1
  41. package/dist/{stage-types-Bd-KtcYT.d.ts → stage-types-BFsm3qsZ.d.ts} +205 -13
  42. package/dist/stage.d.ts +10 -7
  43. package/dist/stage.js +5311 -3880
  44. package/dist/stage.js.map +1 -1
  45. package/dist/thruster-DhRaJnoL.d.ts +172 -0
  46. package/dist/world-Be5m1XC1.d.ts +31 -0
  47. package/package.json +29 -13
  48. package/dist/behaviors.d.ts +0 -854
  49. package/dist/behaviors.js +0 -1209
  50. package/dist/behaviors.js.map +0 -1
  51. package/dist/camera-CeJPAgGg.d.ts +0 -116
  52. package/dist/moveable-B_vyA6cw.d.ts +0 -67
  53. package/dist/transformable-CUhvyuYO.d.ts +0 -67
  54. package/dist/world-C8tQ7Plj.d.ts +0 -774
package/dist/behaviors.js DELETED
@@ -1,1209 +0,0 @@
1
- // src/lib/behaviors/behavior-descriptor.ts
2
- function defineBehavior(config) {
3
- return {
4
- key: /* @__PURE__ */ Symbol.for(`zylem:behavior:${config.name}`),
5
- defaultOptions: config.defaultOptions,
6
- systemFactory: config.systemFactory,
7
- createHandle: config.createHandle
8
- };
9
- }
10
-
11
- // src/lib/behaviors/use-behavior.ts
12
- function useBehavior(entity, descriptor, options) {
13
- entity.use(descriptor, options);
14
- return entity;
15
- }
16
-
17
- // src/lib/behaviors/components.ts
18
- import { Vector3, Quaternion } from "three";
19
- function createTransformComponent() {
20
- return {
21
- position: new Vector3(),
22
- rotation: new Quaternion()
23
- };
24
- }
25
- function createPhysicsBodyComponent(body) {
26
- return { body };
27
- }
28
-
29
- // src/lib/behaviors/physics-step.behavior.ts
30
- var PhysicsStepBehavior = class {
31
- constructor(physicsWorld) {
32
- this.physicsWorld = physicsWorld;
33
- }
34
- update(dt) {
35
- this.physicsWorld.timestep = dt;
36
- this.physicsWorld.step();
37
- }
38
- };
39
-
40
- // src/lib/behaviors/physics-sync.behavior.ts
41
- var PhysicsSyncBehavior = class {
42
- constructor(world) {
43
- this.world = world;
44
- }
45
- /**
46
- * Query entities that have both physics body and transform components
47
- */
48
- queryEntities() {
49
- const entities = [];
50
- for (const [, entity] of this.world.collisionMap) {
51
- const gameEntity = entity;
52
- if (gameEntity.physics?.body && gameEntity.transform) {
53
- entities.push({
54
- physics: gameEntity.physics,
55
- transform: gameEntity.transform
56
- });
57
- }
58
- }
59
- return entities;
60
- }
61
- update(_dt) {
62
- const entities = this.queryEntities();
63
- for (const e of entities) {
64
- const body = e.physics.body;
65
- const transform = e.transform;
66
- const p = body.translation();
67
- transform.position.set(p.x, p.y, p.z);
68
- const r = body.rotation();
69
- transform.rotation.set(r.x, r.y, r.z, r.w);
70
- }
71
- }
72
- };
73
-
74
- // src/lib/behaviors/thruster/components.ts
75
- function createThrusterMovementComponent(linearThrust, angularThrust, options) {
76
- return {
77
- linearThrust,
78
- angularThrust,
79
- linearDamping: options?.linearDamping,
80
- angularDamping: options?.angularDamping
81
- };
82
- }
83
- function createThrusterInputComponent() {
84
- return {
85
- thrust: 0,
86
- rotate: 0
87
- };
88
- }
89
- function createThrusterStateComponent() {
90
- return {
91
- enabled: true,
92
- currentThrust: 0
93
- };
94
- }
95
-
96
- // src/lib/behaviors/thruster/thruster-fsm.ts
97
- import { StateMachine, t } from "typescript-fsm";
98
- var ThrusterState = /* @__PURE__ */ ((ThrusterState2) => {
99
- ThrusterState2["Idle"] = "idle";
100
- ThrusterState2["Active"] = "active";
101
- ThrusterState2["Boosting"] = "boosting";
102
- ThrusterState2["Disabled"] = "disabled";
103
- ThrusterState2["Docked"] = "docked";
104
- return ThrusterState2;
105
- })(ThrusterState || {});
106
- var ThrusterEvent = /* @__PURE__ */ ((ThrusterEvent2) => {
107
- ThrusterEvent2["Activate"] = "activate";
108
- ThrusterEvent2["Deactivate"] = "deactivate";
109
- ThrusterEvent2["Boost"] = "boost";
110
- ThrusterEvent2["EndBoost"] = "endBoost";
111
- ThrusterEvent2["Disable"] = "disable";
112
- ThrusterEvent2["Enable"] = "enable";
113
- ThrusterEvent2["Dock"] = "dock";
114
- ThrusterEvent2["Undock"] = "undock";
115
- return ThrusterEvent2;
116
- })(ThrusterEvent || {});
117
- var ThrusterFSM = class {
118
- constructor(ctx) {
119
- this.ctx = ctx;
120
- this.machine = new StateMachine(
121
- "idle" /* Idle */,
122
- [
123
- // Core transitions
124
- t("idle" /* Idle */, "activate" /* Activate */, "active" /* Active */),
125
- t("active" /* Active */, "deactivate" /* Deactivate */, "idle" /* Idle */),
126
- t("active" /* Active */, "boost" /* Boost */, "boosting" /* Boosting */),
127
- t("active" /* Active */, "disable" /* Disable */, "disabled" /* Disabled */),
128
- t("active" /* Active */, "dock" /* Dock */, "docked" /* Docked */),
129
- t("boosting" /* Boosting */, "endBoost" /* EndBoost */, "active" /* Active */),
130
- t("boosting" /* Boosting */, "disable" /* Disable */, "disabled" /* Disabled */),
131
- t("disabled" /* Disabled */, "enable" /* Enable */, "idle" /* Idle */),
132
- t("docked" /* Docked */, "undock" /* Undock */, "idle" /* Idle */),
133
- // Self-transitions (no-ops for redundant events)
134
- t("idle" /* Idle */, "deactivate" /* Deactivate */, "idle" /* Idle */),
135
- t("active" /* Active */, "activate" /* Activate */, "active" /* Active */)
136
- ]
137
- );
138
- }
139
- machine;
140
- /**
141
- * Get current state
142
- */
143
- getState() {
144
- return this.machine.getState();
145
- }
146
- /**
147
- * Dispatch an event to transition state
148
- */
149
- dispatch(event) {
150
- if (this.machine.can(event)) {
151
- this.machine.dispatch(event);
152
- }
153
- }
154
- /**
155
- * Update FSM state based on player input.
156
- * Auto-transitions between Idle/Active to report current state.
157
- * Does NOT modify input - just observes and reports.
158
- */
159
- update(playerInput) {
160
- const state = this.machine.getState();
161
- const hasInput = Math.abs(playerInput.thrust) > 0.01 || Math.abs(playerInput.rotate) > 0.01;
162
- if (hasInput && state === "idle" /* Idle */) {
163
- this.dispatch("activate" /* Activate */);
164
- } else if (!hasInput && state === "active" /* Active */) {
165
- this.dispatch("deactivate" /* Deactivate */);
166
- }
167
- }
168
- };
169
-
170
- // src/lib/behaviors/thruster/thruster-movement.behavior.ts
171
- var ThrusterMovementBehavior = class {
172
- constructor(world) {
173
- this.world = world;
174
- }
175
- /**
176
- * Query function - returns entities with required thruster components
177
- */
178
- queryEntities() {
179
- const entities = [];
180
- for (const [, entity] of this.world.collisionMap) {
181
- const gameEntity = entity;
182
- if (gameEntity.physics?.body && gameEntity.thruster && gameEntity.$thruster) {
183
- entities.push({
184
- physics: gameEntity.physics,
185
- thruster: gameEntity.thruster,
186
- $thruster: gameEntity.$thruster
187
- });
188
- }
189
- }
190
- return entities;
191
- }
192
- update(_dt) {
193
- const entities = this.queryEntities();
194
- for (const e of entities) {
195
- const body = e.physics.body;
196
- const thruster = e.thruster;
197
- const input = e.$thruster;
198
- const q = body.rotation();
199
- const rotationZ = Math.atan2(2 * (q.w * q.z + q.x * q.y), 1 - 2 * (q.y * q.y + q.z * q.z));
200
- if (input.thrust !== 0) {
201
- const currentVel = body.linvel();
202
- if (input.thrust > 0) {
203
- const forwardX = Math.sin(-rotationZ);
204
- const forwardY = Math.cos(-rotationZ);
205
- const thrustAmount = thruster.linearThrust * input.thrust * 0.1;
206
- body.setLinvel({
207
- x: currentVel.x + forwardX * thrustAmount,
208
- y: currentVel.y + forwardY * thrustAmount,
209
- z: currentVel.z
210
- }, true);
211
- } else {
212
- const brakeAmount = 0.9;
213
- body.setLinvel({
214
- x: currentVel.x * brakeAmount,
215
- y: currentVel.y * brakeAmount,
216
- z: currentVel.z
217
- }, true);
218
- }
219
- }
220
- if (input.rotate !== 0) {
221
- body.setAngvel({ x: 0, y: 0, z: -thruster.angularThrust * input.rotate }, true);
222
- } else {
223
- const angVel = body.angvel();
224
- body.setAngvel({ x: angVel.x, y: angVel.y, z: 0 }, true);
225
- }
226
- }
227
- }
228
- };
229
-
230
- // src/lib/behaviors/thruster/thruster.descriptor.ts
231
- var defaultOptions = {
232
- linearThrust: 10,
233
- angularThrust: 5
234
- };
235
- var ThrusterBehaviorSystem = class {
236
- constructor(world) {
237
- this.world = world;
238
- this.movementBehavior = new ThrusterMovementBehavior(world);
239
- }
240
- movementBehavior;
241
- update(ecs, delta) {
242
- if (!this.world?.collisionMap) return;
243
- for (const [, entity] of this.world.collisionMap) {
244
- const gameEntity = entity;
245
- if (typeof gameEntity.getBehaviorRefs !== "function") continue;
246
- const refs = gameEntity.getBehaviorRefs();
247
- const thrusterRef = refs.find(
248
- (r) => r.descriptor.key === /* @__PURE__ */ Symbol.for("zylem:behavior:thruster")
249
- );
250
- if (!thrusterRef || !gameEntity.body) continue;
251
- const options = thrusterRef.options;
252
- if (!gameEntity.thruster) {
253
- gameEntity.thruster = {
254
- linearThrust: options.linearThrust,
255
- angularThrust: options.angularThrust
256
- };
257
- }
258
- if (!gameEntity.$thruster) {
259
- gameEntity.$thruster = {
260
- thrust: 0,
261
- rotate: 0
262
- };
263
- }
264
- if (!gameEntity.physics) {
265
- gameEntity.physics = { body: gameEntity.body };
266
- }
267
- if (!thrusterRef.fsm && gameEntity.$thruster) {
268
- thrusterRef.fsm = new ThrusterFSM({ input: gameEntity.$thruster });
269
- }
270
- if (thrusterRef.fsm && gameEntity.$thruster) {
271
- thrusterRef.fsm.update({
272
- thrust: gameEntity.$thruster.thrust,
273
- rotate: gameEntity.$thruster.rotate
274
- });
275
- }
276
- }
277
- this.movementBehavior.update(delta);
278
- }
279
- destroy(_ecs) {
280
- }
281
- };
282
- var ThrusterBehavior = defineBehavior({
283
- name: "thruster",
284
- defaultOptions,
285
- systemFactory: (ctx) => new ThrusterBehaviorSystem(ctx.world)
286
- });
287
-
288
- // src/lib/behaviors/screen-wrap/screen-wrap-fsm.ts
289
- import { StateMachine as StateMachine2, t as t2 } from "typescript-fsm";
290
- var ScreenWrapState = /* @__PURE__ */ ((ScreenWrapState2) => {
291
- ScreenWrapState2["Center"] = "center";
292
- ScreenWrapState2["NearEdgeLeft"] = "near-edge-left";
293
- ScreenWrapState2["NearEdgeRight"] = "near-edge-right";
294
- ScreenWrapState2["NearEdgeTop"] = "near-edge-top";
295
- ScreenWrapState2["NearEdgeBottom"] = "near-edge-bottom";
296
- ScreenWrapState2["Wrapped"] = "wrapped";
297
- return ScreenWrapState2;
298
- })(ScreenWrapState || {});
299
- var ScreenWrapEvent = /* @__PURE__ */ ((ScreenWrapEvent2) => {
300
- ScreenWrapEvent2["EnterCenter"] = "enter-center";
301
- ScreenWrapEvent2["ApproachLeft"] = "approach-left";
302
- ScreenWrapEvent2["ApproachRight"] = "approach-right";
303
- ScreenWrapEvent2["ApproachTop"] = "approach-top";
304
- ScreenWrapEvent2["ApproachBottom"] = "approach-bottom";
305
- ScreenWrapEvent2["Wrap"] = "wrap";
306
- return ScreenWrapEvent2;
307
- })(ScreenWrapEvent || {});
308
- var ScreenWrapFSM = class {
309
- machine;
310
- constructor() {
311
- this.machine = new StateMachine2(
312
- "center" /* Center */,
313
- [
314
- // From Center
315
- t2("center" /* Center */, "approach-left" /* ApproachLeft */, "near-edge-left" /* NearEdgeLeft */),
316
- t2("center" /* Center */, "approach-right" /* ApproachRight */, "near-edge-right" /* NearEdgeRight */),
317
- t2("center" /* Center */, "approach-top" /* ApproachTop */, "near-edge-top" /* NearEdgeTop */),
318
- t2("center" /* Center */, "approach-bottom" /* ApproachBottom */, "near-edge-bottom" /* NearEdgeBottom */),
319
- // From NearEdge to Wrapped
320
- t2("near-edge-left" /* NearEdgeLeft */, "wrap" /* Wrap */, "wrapped" /* Wrapped */),
321
- t2("near-edge-right" /* NearEdgeRight */, "wrap" /* Wrap */, "wrapped" /* Wrapped */),
322
- t2("near-edge-top" /* NearEdgeTop */, "wrap" /* Wrap */, "wrapped" /* Wrapped */),
323
- t2("near-edge-bottom" /* NearEdgeBottom */, "wrap" /* Wrap */, "wrapped" /* Wrapped */),
324
- // From NearEdge back to Center
325
- t2("near-edge-left" /* NearEdgeLeft */, "enter-center" /* EnterCenter */, "center" /* Center */),
326
- t2("near-edge-right" /* NearEdgeRight */, "enter-center" /* EnterCenter */, "center" /* Center */),
327
- t2("near-edge-top" /* NearEdgeTop */, "enter-center" /* EnterCenter */, "center" /* Center */),
328
- t2("near-edge-bottom" /* NearEdgeBottom */, "enter-center" /* EnterCenter */, "center" /* Center */),
329
- // From Wrapped back to Center
330
- t2("wrapped" /* Wrapped */, "enter-center" /* EnterCenter */, "center" /* Center */),
331
- // From Wrapped to NearEdge (landed near opposite edge)
332
- t2("wrapped" /* Wrapped */, "approach-left" /* ApproachLeft */, "near-edge-left" /* NearEdgeLeft */),
333
- t2("wrapped" /* Wrapped */, "approach-right" /* ApproachRight */, "near-edge-right" /* NearEdgeRight */),
334
- t2("wrapped" /* Wrapped */, "approach-top" /* ApproachTop */, "near-edge-top" /* NearEdgeTop */),
335
- t2("wrapped" /* Wrapped */, "approach-bottom" /* ApproachBottom */, "near-edge-bottom" /* NearEdgeBottom */),
336
- // Self-transitions (no-ops for redundant events)
337
- t2("center" /* Center */, "enter-center" /* EnterCenter */, "center" /* Center */),
338
- t2("near-edge-left" /* NearEdgeLeft */, "approach-left" /* ApproachLeft */, "near-edge-left" /* NearEdgeLeft */),
339
- t2("near-edge-right" /* NearEdgeRight */, "approach-right" /* ApproachRight */, "near-edge-right" /* NearEdgeRight */),
340
- t2("near-edge-top" /* NearEdgeTop */, "approach-top" /* ApproachTop */, "near-edge-top" /* NearEdgeTop */),
341
- t2("near-edge-bottom" /* NearEdgeBottom */, "approach-bottom" /* ApproachBottom */, "near-edge-bottom" /* NearEdgeBottom */)
342
- ]
343
- );
344
- }
345
- getState() {
346
- return this.machine.getState();
347
- }
348
- dispatch(event) {
349
- if (this.machine.can(event)) {
350
- this.machine.dispatch(event);
351
- }
352
- }
353
- /**
354
- * Update FSM based on entity position relative to bounds
355
- */
356
- update(position, bounds, wrapped) {
357
- const { x, y } = position;
358
- const { minX, maxX, minY, maxY, edgeThreshold } = bounds;
359
- if (wrapped) {
360
- this.dispatch("wrap" /* Wrap */);
361
- return;
362
- }
363
- const nearLeft = x < minX + edgeThreshold;
364
- const nearRight = x > maxX - edgeThreshold;
365
- const nearBottom = y < minY + edgeThreshold;
366
- const nearTop = y > maxY - edgeThreshold;
367
- if (nearLeft) {
368
- this.dispatch("approach-left" /* ApproachLeft */);
369
- } else if (nearRight) {
370
- this.dispatch("approach-right" /* ApproachRight */);
371
- } else if (nearTop) {
372
- this.dispatch("approach-top" /* ApproachTop */);
373
- } else if (nearBottom) {
374
- this.dispatch("approach-bottom" /* ApproachBottom */);
375
- } else {
376
- this.dispatch("enter-center" /* EnterCenter */);
377
- }
378
- }
379
- };
380
-
381
- // src/lib/behaviors/screen-wrap/screen-wrap.descriptor.ts
382
- var defaultOptions2 = {
383
- width: 20,
384
- height: 15,
385
- centerX: 0,
386
- centerY: 0,
387
- edgeThreshold: 2
388
- };
389
- var ScreenWrapSystem = class {
390
- constructor(world) {
391
- this.world = world;
392
- }
393
- update(ecs, delta) {
394
- if (!this.world?.collisionMap) return;
395
- for (const [, entity] of this.world.collisionMap) {
396
- const gameEntity = entity;
397
- if (typeof gameEntity.getBehaviorRefs !== "function") continue;
398
- const refs = gameEntity.getBehaviorRefs();
399
- const wrapRef = refs.find(
400
- (r) => r.descriptor.key === /* @__PURE__ */ Symbol.for("zylem:behavior:screen-wrap")
401
- );
402
- if (!wrapRef || !gameEntity.body) continue;
403
- const options = wrapRef.options;
404
- if (!wrapRef.fsm) {
405
- wrapRef.fsm = new ScreenWrapFSM();
406
- }
407
- const wrapped = this.wrapEntity(gameEntity, options);
408
- const pos = gameEntity.body.translation();
409
- const { width, height, centerX, centerY, edgeThreshold } = options;
410
- const halfWidth = width / 2;
411
- const halfHeight = height / 2;
412
- wrapRef.fsm.update(
413
- { x: pos.x, y: pos.y },
414
- {
415
- minX: centerX - halfWidth,
416
- maxX: centerX + halfWidth,
417
- minY: centerY - halfHeight,
418
- maxY: centerY + halfHeight,
419
- edgeThreshold
420
- },
421
- wrapped
422
- );
423
- }
424
- }
425
- wrapEntity(entity, options) {
426
- const body = entity.body;
427
- if (!body) return false;
428
- const { width, height, centerX, centerY } = options;
429
- const halfWidth = width / 2;
430
- const halfHeight = height / 2;
431
- const minX = centerX - halfWidth;
432
- const maxX = centerX + halfWidth;
433
- const minY = centerY - halfHeight;
434
- const maxY = centerY + halfHeight;
435
- const pos = body.translation();
436
- let newX = pos.x;
437
- let newY = pos.y;
438
- let wrapped = false;
439
- if (pos.x < minX) {
440
- newX = maxX - (minX - pos.x);
441
- wrapped = true;
442
- } else if (pos.x > maxX) {
443
- newX = minX + (pos.x - maxX);
444
- wrapped = true;
445
- }
446
- if (pos.y < minY) {
447
- newY = maxY - (minY - pos.y);
448
- wrapped = true;
449
- } else if (pos.y > maxY) {
450
- newY = minY + (pos.y - maxY);
451
- wrapped = true;
452
- }
453
- if (wrapped) {
454
- body.setTranslation({ x: newX, y: newY, z: pos.z }, true);
455
- }
456
- return wrapped;
457
- }
458
- destroy(_ecs) {
459
- }
460
- };
461
- var ScreenWrapBehavior = defineBehavior({
462
- name: "screen-wrap",
463
- defaultOptions: defaultOptions2,
464
- systemFactory: (ctx) => new ScreenWrapSystem(ctx.world)
465
- });
466
-
467
- // src/lib/behaviors/world-boundary-2d/world-boundary-2d-fsm.ts
468
- import { StateMachine as StateMachine3, t as t3 } from "typescript-fsm";
469
- var WorldBoundary2DState = /* @__PURE__ */ ((WorldBoundary2DState2) => {
470
- WorldBoundary2DState2["Inside"] = "inside";
471
- WorldBoundary2DState2["Touching"] = "touching";
472
- return WorldBoundary2DState2;
473
- })(WorldBoundary2DState || {});
474
- var WorldBoundary2DEvent = /* @__PURE__ */ ((WorldBoundary2DEvent2) => {
475
- WorldBoundary2DEvent2["EnterInside"] = "enter-inside";
476
- WorldBoundary2DEvent2["TouchBoundary"] = "touch-boundary";
477
- return WorldBoundary2DEvent2;
478
- })(WorldBoundary2DEvent || {});
479
- function computeWorldBoundary2DHits(position, bounds) {
480
- const hits = {
481
- top: false,
482
- bottom: false,
483
- left: false,
484
- right: false
485
- };
486
- if (position.x <= bounds.left) hits.left = true;
487
- else if (position.x >= bounds.right) hits.right = true;
488
- if (position.y <= bounds.bottom) hits.bottom = true;
489
- else if (position.y >= bounds.top) hits.top = true;
490
- return hits;
491
- }
492
- function hasAnyWorldBoundary2DHit(hits) {
493
- return !!(hits.left || hits.right || hits.top || hits.bottom);
494
- }
495
- var WorldBoundary2DFSM = class {
496
- machine;
497
- lastHits = { top: false, bottom: false, left: false, right: false };
498
- lastPosition = null;
499
- lastUpdatedAtMs = null;
500
- constructor() {
501
- this.machine = new StateMachine3(
502
- "inside" /* Inside */,
503
- [
504
- t3("inside" /* Inside */, "touch-boundary" /* TouchBoundary */, "touching" /* Touching */),
505
- t3("touching" /* Touching */, "enter-inside" /* EnterInside */, "inside" /* Inside */),
506
- // Self transitions (no-ops)
507
- t3("inside" /* Inside */, "enter-inside" /* EnterInside */, "inside" /* Inside */),
508
- t3("touching" /* Touching */, "touch-boundary" /* TouchBoundary */, "touching" /* Touching */)
509
- ]
510
- );
511
- }
512
- getState() {
513
- return this.machine.getState();
514
- }
515
- /**
516
- * Returns the last computed hits (always available after first update call).
517
- */
518
- getLastHits() {
519
- return this.lastHits;
520
- }
521
- /**
522
- * Returns adjusted movement values based on boundary hits.
523
- * If the entity is touching a boundary and trying to move further into it,
524
- * that axis component is zeroed out.
525
- *
526
- * @param moveX - The desired X movement
527
- * @param moveY - The desired Y movement
528
- * @returns Adjusted { moveX, moveY } with boundary-blocked axes zeroed
529
- */
530
- getMovement(moveX, moveY) {
531
- const hits = this.lastHits;
532
- let adjustedX = moveX;
533
- let adjustedY = moveY;
534
- if (hits.left && moveX < 0 || hits.right && moveX > 0) {
535
- adjustedX = 0;
536
- }
537
- if (hits.bottom && moveY < 0 || hits.top && moveY > 0) {
538
- adjustedY = 0;
539
- }
540
- return { moveX: adjustedX, moveY: adjustedY };
541
- }
542
- /**
543
- * Returns the last position passed to `update`, if any.
544
- */
545
- getLastPosition() {
546
- return this.lastPosition;
547
- }
548
- /**
549
- * Best-effort timestamp (ms) of the last `update(...)` call.
550
- * This is optional metadata; systems can ignore it.
551
- */
552
- getLastUpdatedAtMs() {
553
- return this.lastUpdatedAtMs;
554
- }
555
- /**
556
- * Update FSM + extended state based on current position and bounds.
557
- * Returns the computed hits for convenience.
558
- */
559
- update(position, bounds) {
560
- const hits = computeWorldBoundary2DHits(position, bounds);
561
- this.lastHits = hits;
562
- this.lastPosition = { x: position.x, y: position.y };
563
- this.lastUpdatedAtMs = Date.now();
564
- if (hasAnyWorldBoundary2DHit(hits)) {
565
- this.dispatch("touch-boundary" /* TouchBoundary */);
566
- } else {
567
- this.dispatch("enter-inside" /* EnterInside */);
568
- }
569
- return hits;
570
- }
571
- dispatch(event) {
572
- if (this.machine.can(event)) {
573
- this.machine.dispatch(event);
574
- }
575
- }
576
- };
577
-
578
- // src/lib/behaviors/world-boundary-2d/world-boundary-2d.descriptor.ts
579
- var defaultOptions3 = {
580
- boundaries: { top: 0, bottom: 0, left: 0, right: 0 }
581
- };
582
- function createWorldBoundary2DHandle(ref) {
583
- return {
584
- getLastHits: () => {
585
- const fsm = ref.fsm;
586
- return fsm?.getLastHits() ?? null;
587
- },
588
- getMovement: (moveX, moveY) => {
589
- const fsm = ref.fsm;
590
- return fsm?.getMovement(moveX, moveY) ?? { moveX, moveY };
591
- }
592
- };
593
- }
594
- var WorldBoundary2DSystem = class {
595
- constructor(world) {
596
- this.world = world;
597
- }
598
- update(_ecs, _delta) {
599
- if (!this.world?.collisionMap) return;
600
- for (const [, entity] of this.world.collisionMap) {
601
- const gameEntity = entity;
602
- if (typeof gameEntity.getBehaviorRefs !== "function") continue;
603
- const refs = gameEntity.getBehaviorRefs();
604
- const boundaryRef = refs.find(
605
- (r) => r.descriptor.key === /* @__PURE__ */ Symbol.for("zylem:behavior:world-boundary-2d")
606
- );
607
- if (!boundaryRef || !gameEntity.body) continue;
608
- const options = boundaryRef.options;
609
- if (!boundaryRef.fsm) {
610
- boundaryRef.fsm = new WorldBoundary2DFSM();
611
- }
612
- const body = gameEntity.body;
613
- const pos = body.translation();
614
- boundaryRef.fsm.update(
615
- { x: pos.x, y: pos.y },
616
- options.boundaries
617
- );
618
- }
619
- }
620
- destroy(_ecs) {
621
- }
622
- };
623
- var WorldBoundary2DBehavior = defineBehavior({
624
- name: "world-boundary-2d",
625
- defaultOptions: defaultOptions3,
626
- systemFactory: (ctx) => new WorldBoundary2DSystem(ctx.world),
627
- createHandle: createWorldBoundary2DHandle
628
- });
629
-
630
- // src/lib/behaviors/ricochet-2d/ricochet-2d-fsm.ts
631
- import { StateMachine as StateMachine4, t as t4 } from "typescript-fsm";
632
- var Ricochet2DState = /* @__PURE__ */ ((Ricochet2DState2) => {
633
- Ricochet2DState2["Idle"] = "idle";
634
- Ricochet2DState2["Ricocheting"] = "ricocheting";
635
- return Ricochet2DState2;
636
- })(Ricochet2DState || {});
637
- var Ricochet2DEvent = /* @__PURE__ */ ((Ricochet2DEvent2) => {
638
- Ricochet2DEvent2["StartRicochet"] = "start-ricochet";
639
- Ricochet2DEvent2["EndRicochet"] = "end-ricochet";
640
- return Ricochet2DEvent2;
641
- })(Ricochet2DEvent || {});
642
- function clamp(value, min, max) {
643
- return Math.max(min, Math.min(max, value));
644
- }
645
- var Ricochet2DFSM = class {
646
- machine;
647
- lastResult = null;
648
- lastUpdatedAtMs = null;
649
- constructor() {
650
- this.machine = new StateMachine4(
651
- "idle" /* Idle */,
652
- [
653
- t4("idle" /* Idle */, "start-ricochet" /* StartRicochet */, "ricocheting" /* Ricocheting */),
654
- t4("ricocheting" /* Ricocheting */, "end-ricochet" /* EndRicochet */, "idle" /* Idle */),
655
- // Self transitions (no-ops)
656
- t4("idle" /* Idle */, "end-ricochet" /* EndRicochet */, "idle" /* Idle */),
657
- t4("ricocheting" /* Ricocheting */, "start-ricochet" /* StartRicochet */, "ricocheting" /* Ricocheting */)
658
- ]
659
- );
660
- }
661
- getState() {
662
- return this.machine.getState();
663
- }
664
- /**
665
- * Returns the last computed ricochet result, or null if none.
666
- */
667
- getLastResult() {
668
- return this.lastResult;
669
- }
670
- /**
671
- * Best-effort timestamp (ms) of the last computation.
672
- */
673
- getLastUpdatedAtMs() {
674
- return this.lastUpdatedAtMs;
675
- }
676
- /**
677
- * Compute a ricochet result from collision context.
678
- * Returns the result for the consumer to apply, or null if invalid input.
679
- */
680
- computeRicochet(ctx, options = {}) {
681
- const {
682
- minSpeed = 2,
683
- maxSpeed = 20,
684
- speedMultiplier = 1.05,
685
- reflectionMode = "angled",
686
- maxAngleDeg = 60
687
- } = options;
688
- const { selfVelocity, selfPosition, otherPosition, otherSize } = this.extractDataFromEntities(ctx);
689
- if (!selfVelocity) {
690
- this.dispatch("end-ricochet" /* EndRicochet */);
691
- return null;
692
- }
693
- const speed = Math.hypot(selfVelocity.x, selfVelocity.y);
694
- if (speed === 0) {
695
- this.dispatch("end-ricochet" /* EndRicochet */);
696
- return null;
697
- }
698
- const normal = ctx.contact.normal ?? this.computeNormalFromPositions(selfPosition, otherPosition);
699
- if (!normal) {
700
- this.dispatch("end-ricochet" /* EndRicochet */);
701
- return null;
702
- }
703
- let reflected = this.computeBasicReflection(selfVelocity, normal);
704
- if (reflectionMode === "angled") {
705
- reflected = this.computeAngledDeflection(
706
- selfVelocity,
707
- normal,
708
- speed,
709
- maxAngleDeg,
710
- speedMultiplier,
711
- selfPosition,
712
- otherPosition,
713
- otherSize,
714
- ctx.contact.position
715
- );
716
- }
717
- reflected = this.applySpeedClamp(reflected, minSpeed, maxSpeed);
718
- const result = {
719
- velocity: { x: reflected.x, y: reflected.y, z: 0 },
720
- speed: Math.hypot(reflected.x, reflected.y),
721
- normal: { x: normal.x, y: normal.y, z: 0 }
722
- };
723
- this.lastResult = result;
724
- this.lastUpdatedAtMs = Date.now();
725
- this.dispatch("start-ricochet" /* StartRicochet */);
726
- return result;
727
- }
728
- /**
729
- * Extract velocity, position, and size data from entities or context.
730
- */
731
- extractDataFromEntities(ctx) {
732
- let selfVelocity = ctx.selfVelocity;
733
- let selfPosition = ctx.selfPosition;
734
- let otherPosition = ctx.otherPosition;
735
- let otherSize = ctx.otherSize;
736
- if (ctx.entity?.body) {
737
- const vel = ctx.entity.body.linvel();
738
- selfVelocity = selfVelocity ?? { x: vel.x, y: vel.y, z: vel.z };
739
- const pos = ctx.entity.body.translation();
740
- selfPosition = selfPosition ?? { x: pos.x, y: pos.y, z: pos.z };
741
- }
742
- if (ctx.otherEntity?.body) {
743
- const pos = ctx.otherEntity.body.translation();
744
- otherPosition = otherPosition ?? { x: pos.x, y: pos.y, z: pos.z };
745
- }
746
- if (ctx.otherEntity && "size" in ctx.otherEntity) {
747
- const size = ctx.otherEntity.size;
748
- if (size && typeof size.x === "number") {
749
- otherSize = otherSize ?? { x: size.x, y: size.y, z: size.z };
750
- }
751
- }
752
- return { selfVelocity, selfPosition, otherPosition, otherSize };
753
- }
754
- /**
755
- * Compute collision normal from entity positions using AABB heuristic.
756
- */
757
- computeNormalFromPositions(selfPosition, otherPosition) {
758
- if (!selfPosition || !otherPosition) return null;
759
- const dx = selfPosition.x - otherPosition.x;
760
- const dy = selfPosition.y - otherPosition.y;
761
- if (Math.abs(dx) > Math.abs(dy)) {
762
- return { x: dx > 0 ? 1 : -1, y: 0, z: 0 };
763
- } else {
764
- return { x: 0, y: dy > 0 ? 1 : -1, z: 0 };
765
- }
766
- }
767
- /**
768
- * Compute basic reflection using the formula: v' = v - 2(v·n)n
769
- */
770
- computeBasicReflection(velocity, normal) {
771
- const vx = velocity.x;
772
- const vy = velocity.y;
773
- const dotProduct = vx * normal.x + vy * normal.y;
774
- return {
775
- x: vx - 2 * dotProduct * normal.x,
776
- y: vy - 2 * dotProduct * normal.y
777
- };
778
- }
779
- /**
780
- * Compute angled deflection for paddle-style reflections.
781
- */
782
- computeAngledDeflection(velocity, normal, speed, maxAngleDeg, speedMultiplier, selfPosition, otherPosition, otherSize, contactPosition) {
783
- const maxAngleRad = maxAngleDeg * Math.PI / 180;
784
- let tx = -normal.y;
785
- let ty = normal.x;
786
- if (Math.abs(normal.x) > Math.abs(normal.y)) {
787
- if (ty < 0) {
788
- tx = -tx;
789
- ty = -ty;
790
- }
791
- } else {
792
- if (tx < 0) {
793
- tx = -tx;
794
- ty = -ty;
795
- }
796
- }
797
- const offset = this.computeHitOffset(
798
- velocity,
799
- normal,
800
- speed,
801
- tx,
802
- ty,
803
- selfPosition,
804
- otherPosition,
805
- otherSize,
806
- contactPosition
807
- );
808
- const angle = clamp(offset, -1, 1) * maxAngleRad;
809
- const cosA = Math.cos(angle);
810
- const sinA = Math.sin(angle);
811
- const newSpeed = speed * speedMultiplier;
812
- return {
813
- x: newSpeed * (normal.x * cosA + tx * sinA),
814
- y: newSpeed * (normal.y * cosA + ty * sinA)
815
- };
816
- }
817
- /**
818
- * Compute hit offset for angled deflection (-1 to 1).
819
- */
820
- computeHitOffset(velocity, normal, speed, tx, ty, selfPosition, otherPosition, otherSize, contactPosition) {
821
- if (otherPosition && otherSize) {
822
- const useY = Math.abs(normal.x) > Math.abs(normal.y);
823
- const halfExtent = useY ? otherSize.y / 2 : otherSize.x / 2;
824
- if (useY) {
825
- const selfY = selfPosition?.y ?? contactPosition?.y ?? 0;
826
- const paddleY = otherPosition.y;
827
- return (selfY - paddleY) / halfExtent;
828
- } else {
829
- const selfX = selfPosition?.x ?? contactPosition?.x ?? 0;
830
- const paddleX = otherPosition.x;
831
- return (selfX - paddleX) / halfExtent;
832
- }
833
- }
834
- return (velocity.x * tx + velocity.y * ty) / speed;
835
- }
836
- /**
837
- * Apply speed constraints to the reflected velocity.
838
- */
839
- applySpeedClamp(velocity, minSpeed, maxSpeed) {
840
- const currentSpeed = Math.hypot(velocity.x, velocity.y);
841
- if (currentSpeed === 0) return velocity;
842
- const targetSpeed = clamp(currentSpeed, minSpeed, maxSpeed);
843
- const scale = targetSpeed / currentSpeed;
844
- return {
845
- x: velocity.x * scale,
846
- y: velocity.y * scale
847
- };
848
- }
849
- /**
850
- * Clear the ricochet state (call after consumer has applied the result).
851
- */
852
- clearRicochet() {
853
- this.dispatch("end-ricochet" /* EndRicochet */);
854
- }
855
- dispatch(event) {
856
- if (this.machine.can(event)) {
857
- this.machine.dispatch(event);
858
- }
859
- }
860
- };
861
-
862
- // src/lib/behaviors/ricochet-2d/ricochet-2d.descriptor.ts
863
- var defaultOptions4 = {
864
- minSpeed: 2,
865
- maxSpeed: 20,
866
- speedMultiplier: 1.05,
867
- reflectionMode: "angled",
868
- maxAngleDeg: 60
869
- };
870
- function createRicochet2DHandle(ref) {
871
- return {
872
- getRicochet: (ctx) => {
873
- const fsm = ref.fsm;
874
- if (!fsm) return null;
875
- return fsm.computeRicochet(ctx, ref.options);
876
- },
877
- getLastResult: () => {
878
- const fsm = ref.fsm;
879
- return fsm?.getLastResult() ?? null;
880
- }
881
- };
882
- }
883
- var Ricochet2DSystem = class {
884
- constructor(world) {
885
- this.world = world;
886
- }
887
- update(_ecs, _delta) {
888
- if (!this.world?.collisionMap) return;
889
- for (const [, entity] of this.world.collisionMap) {
890
- const gameEntity = entity;
891
- if (typeof gameEntity.getBehaviorRefs !== "function") continue;
892
- const refs = gameEntity.getBehaviorRefs();
893
- const ricochetRef = refs.find(
894
- (r) => r.descriptor.key === /* @__PURE__ */ Symbol.for("zylem:behavior:ricochet-2d")
895
- );
896
- if (!ricochetRef) continue;
897
- if (!ricochetRef.fsm) {
898
- ricochetRef.fsm = new Ricochet2DFSM();
899
- }
900
- }
901
- }
902
- destroy(_ecs) {
903
- }
904
- };
905
- var Ricochet2DBehavior = defineBehavior({
906
- name: "ricochet-2d",
907
- defaultOptions: defaultOptions4,
908
- systemFactory: (ctx) => new Ricochet2DSystem(ctx.world),
909
- createHandle: createRicochet2DHandle
910
- });
911
-
912
- // src/lib/behaviors/movement-sequence-2d/movement-sequence-2d-fsm.ts
913
- import { StateMachine as StateMachine5, t as t5 } from "typescript-fsm";
914
- var MovementSequence2DState = /* @__PURE__ */ ((MovementSequence2DState2) => {
915
- MovementSequence2DState2["Idle"] = "idle";
916
- MovementSequence2DState2["Running"] = "running";
917
- MovementSequence2DState2["Paused"] = "paused";
918
- MovementSequence2DState2["Completed"] = "completed";
919
- return MovementSequence2DState2;
920
- })(MovementSequence2DState || {});
921
- var MovementSequence2DEvent = /* @__PURE__ */ ((MovementSequence2DEvent2) => {
922
- MovementSequence2DEvent2["Start"] = "start";
923
- MovementSequence2DEvent2["Pause"] = "pause";
924
- MovementSequence2DEvent2["Resume"] = "resume";
925
- MovementSequence2DEvent2["Complete"] = "complete";
926
- MovementSequence2DEvent2["Reset"] = "reset";
927
- return MovementSequence2DEvent2;
928
- })(MovementSequence2DEvent || {});
929
- var MovementSequence2DFSM = class {
930
- machine;
931
- sequence = [];
932
- loop = true;
933
- currentIndex = 0;
934
- timeRemaining = 0;
935
- constructor() {
936
- this.machine = new StateMachine5(
937
- "idle" /* Idle */,
938
- [
939
- // From Idle
940
- t5("idle" /* Idle */, "start" /* Start */, "running" /* Running */),
941
- // From Running
942
- t5("running" /* Running */, "pause" /* Pause */, "paused" /* Paused */),
943
- t5("running" /* Running */, "complete" /* Complete */, "completed" /* Completed */),
944
- t5("running" /* Running */, "reset" /* Reset */, "idle" /* Idle */),
945
- // From Paused
946
- t5("paused" /* Paused */, "resume" /* Resume */, "running" /* Running */),
947
- t5("paused" /* Paused */, "reset" /* Reset */, "idle" /* Idle */),
948
- // From Completed
949
- t5("completed" /* Completed */, "reset" /* Reset */, "idle" /* Idle */),
950
- t5("completed" /* Completed */, "start" /* Start */, "running" /* Running */),
951
- // Self-transitions (no-ops)
952
- t5("idle" /* Idle */, "pause" /* Pause */, "idle" /* Idle */),
953
- t5("idle" /* Idle */, "resume" /* Resume */, "idle" /* Idle */),
954
- t5("running" /* Running */, "start" /* Start */, "running" /* Running */),
955
- t5("running" /* Running */, "resume" /* Resume */, "running" /* Running */),
956
- t5("paused" /* Paused */, "pause" /* Pause */, "paused" /* Paused */),
957
- t5("completed" /* Completed */, "complete" /* Complete */, "completed" /* Completed */)
958
- ]
959
- );
960
- }
961
- /**
962
- * Initialize the sequence. Call this once with options.
963
- */
964
- init(sequence, loop) {
965
- this.sequence = sequence;
966
- this.loop = loop;
967
- this.currentIndex = 0;
968
- this.timeRemaining = sequence.length > 0 ? sequence[0].timeInSeconds : 0;
969
- }
970
- getState() {
971
- return this.machine.getState();
972
- }
973
- /**
974
- * Start the sequence (from Idle or Completed).
975
- */
976
- start() {
977
- if (this.machine.getState() === "idle" /* Idle */ || this.machine.getState() === "completed" /* Completed */) {
978
- this.currentIndex = 0;
979
- this.timeRemaining = this.sequence.length > 0 ? this.sequence[0].timeInSeconds : 0;
980
- }
981
- this.dispatch("start" /* Start */);
982
- }
983
- /**
984
- * Pause the sequence.
985
- */
986
- pause() {
987
- this.dispatch("pause" /* Pause */);
988
- }
989
- /**
990
- * Resume a paused sequence.
991
- */
992
- resume() {
993
- this.dispatch("resume" /* Resume */);
994
- }
995
- /**
996
- * Reset to Idle state.
997
- */
998
- reset() {
999
- this.dispatch("reset" /* Reset */);
1000
- this.currentIndex = 0;
1001
- this.timeRemaining = this.sequence.length > 0 ? this.sequence[0].timeInSeconds : 0;
1002
- }
1003
- /**
1004
- * Update the sequence with delta time.
1005
- * Returns the current movement to apply.
1006
- * Automatically starts if in Idle state.
1007
- */
1008
- update(delta) {
1009
- if (this.sequence.length === 0) {
1010
- return { moveX: 0, moveY: 0 };
1011
- }
1012
- if (this.machine.getState() === "idle" /* Idle */) {
1013
- this.start();
1014
- }
1015
- if (this.machine.getState() !== "running" /* Running */) {
1016
- if (this.machine.getState() === "completed" /* Completed */) {
1017
- return { moveX: 0, moveY: 0 };
1018
- }
1019
- const step2 = this.sequence[this.currentIndex];
1020
- return { moveX: step2?.moveX ?? 0, moveY: step2?.moveY ?? 0 };
1021
- }
1022
- let timeLeft = this.timeRemaining - delta;
1023
- while (timeLeft <= 0) {
1024
- const overflow = -timeLeft;
1025
- this.currentIndex += 1;
1026
- if (this.currentIndex >= this.sequence.length) {
1027
- if (!this.loop) {
1028
- this.dispatch("complete" /* Complete */);
1029
- return { moveX: 0, moveY: 0 };
1030
- }
1031
- this.currentIndex = 0;
1032
- }
1033
- timeLeft = this.sequence[this.currentIndex].timeInSeconds - overflow;
1034
- }
1035
- this.timeRemaining = timeLeft;
1036
- const step = this.sequence[this.currentIndex];
1037
- return { moveX: step?.moveX ?? 0, moveY: step?.moveY ?? 0 };
1038
- }
1039
- /**
1040
- * Get the current movement without advancing time.
1041
- */
1042
- getMovement() {
1043
- if (this.sequence.length === 0 || this.machine.getState() === "completed" /* Completed */) {
1044
- return { moveX: 0, moveY: 0 };
1045
- }
1046
- const step = this.sequence[this.currentIndex];
1047
- return { moveX: step?.moveX ?? 0, moveY: step?.moveY ?? 0 };
1048
- }
1049
- /**
1050
- * Get current step info.
1051
- */
1052
- getCurrentStep() {
1053
- if (this.sequence.length === 0) return null;
1054
- const step = this.sequence[this.currentIndex];
1055
- if (!step) return null;
1056
- return {
1057
- name: step.name,
1058
- index: this.currentIndex,
1059
- moveX: step.moveX ?? 0,
1060
- moveY: step.moveY ?? 0,
1061
- timeRemaining: this.timeRemaining
1062
- };
1063
- }
1064
- /**
1065
- * Get sequence progress.
1066
- */
1067
- getProgress() {
1068
- return {
1069
- stepIndex: this.currentIndex,
1070
- totalSteps: this.sequence.length,
1071
- stepTimeRemaining: this.timeRemaining,
1072
- done: this.machine.getState() === "completed" /* Completed */
1073
- };
1074
- }
1075
- dispatch(event) {
1076
- if (this.machine.can(event)) {
1077
- this.machine.dispatch(event);
1078
- }
1079
- }
1080
- };
1081
-
1082
- // src/lib/behaviors/movement-sequence-2d/movement-sequence-2d.descriptor.ts
1083
- var defaultOptions5 = {
1084
- sequence: [],
1085
- loop: true
1086
- };
1087
- function createMovementSequence2DHandle(ref) {
1088
- return {
1089
- getMovement: () => {
1090
- const fsm = ref.fsm;
1091
- return fsm?.getMovement() ?? { moveX: 0, moveY: 0 };
1092
- },
1093
- getCurrentStep: () => {
1094
- const fsm = ref.fsm;
1095
- return fsm?.getCurrentStep() ?? null;
1096
- },
1097
- getProgress: () => {
1098
- const fsm = ref.fsm;
1099
- return fsm?.getProgress() ?? { stepIndex: 0, totalSteps: 0, stepTimeRemaining: 0, done: true };
1100
- },
1101
- pause: () => {
1102
- const fsm = ref.fsm;
1103
- fsm?.pause();
1104
- },
1105
- resume: () => {
1106
- const fsm = ref.fsm;
1107
- fsm?.resume();
1108
- },
1109
- reset: () => {
1110
- const fsm = ref.fsm;
1111
- fsm?.reset();
1112
- }
1113
- };
1114
- }
1115
- var MovementSequence2DSystem = class {
1116
- constructor(world) {
1117
- this.world = world;
1118
- }
1119
- update(_ecs, delta) {
1120
- if (!this.world?.collisionMap) return;
1121
- for (const [, entity] of this.world.collisionMap) {
1122
- const gameEntity = entity;
1123
- if (typeof gameEntity.getBehaviorRefs !== "function") continue;
1124
- const refs = gameEntity.getBehaviorRefs();
1125
- const sequenceRef = refs.find(
1126
- (r) => r.descriptor.key === /* @__PURE__ */ Symbol.for("zylem:behavior:movement-sequence-2d")
1127
- );
1128
- if (!sequenceRef) continue;
1129
- const options = sequenceRef.options;
1130
- if (!sequenceRef.fsm) {
1131
- sequenceRef.fsm = new MovementSequence2DFSM();
1132
- sequenceRef.fsm.init(options.sequence, options.loop);
1133
- }
1134
- sequenceRef.fsm.update(delta);
1135
- }
1136
- }
1137
- destroy(_ecs) {
1138
- }
1139
- };
1140
- var MovementSequence2DBehavior = defineBehavior({
1141
- name: "movement-sequence-2d",
1142
- defaultOptions: defaultOptions5,
1143
- systemFactory: (ctx) => new MovementSequence2DSystem(ctx.world),
1144
- createHandle: createMovementSequence2DHandle
1145
- });
1146
-
1147
- // src/lib/coordinators/boundary-ricochet.coordinator.ts
1148
- var BoundaryRicochetCoordinator = class {
1149
- constructor(entity, boundary, ricochet) {
1150
- this.entity = entity;
1151
- this.boundary = boundary;
1152
- this.ricochet = ricochet;
1153
- }
1154
- /**
1155
- * Update loop - call this every frame
1156
- */
1157
- update() {
1158
- const hits = this.boundary.getLastHits();
1159
- if (!hits) return null;
1160
- const anyHit = hits.left || hits.right || hits.top || hits.bottom;
1161
- if (!anyHit) return null;
1162
- let normalX = 0;
1163
- let normalY = 0;
1164
- if (hits.left) normalX = 1;
1165
- if (hits.right) normalX = -1;
1166
- if (hits.bottom) normalY = 1;
1167
- if (hits.top) normalY = -1;
1168
- return this.ricochet.getRicochet({
1169
- entity: this.entity,
1170
- contact: { normal: { x: normalX, y: normalY } }
1171
- });
1172
- }
1173
- };
1174
- export {
1175
- BoundaryRicochetCoordinator,
1176
- MovementSequence2DBehavior,
1177
- MovementSequence2DEvent,
1178
- MovementSequence2DFSM,
1179
- MovementSequence2DState,
1180
- PhysicsStepBehavior,
1181
- PhysicsSyncBehavior,
1182
- Ricochet2DBehavior,
1183
- Ricochet2DEvent,
1184
- Ricochet2DFSM,
1185
- Ricochet2DState,
1186
- ScreenWrapBehavior,
1187
- ScreenWrapEvent,
1188
- ScreenWrapFSM,
1189
- ScreenWrapState,
1190
- ThrusterBehavior,
1191
- ThrusterEvent,
1192
- ThrusterFSM,
1193
- ThrusterMovementBehavior,
1194
- ThrusterState,
1195
- WorldBoundary2DBehavior,
1196
- WorldBoundary2DEvent,
1197
- WorldBoundary2DFSM,
1198
- WorldBoundary2DState,
1199
- computeWorldBoundary2DHits,
1200
- createPhysicsBodyComponent,
1201
- createThrusterInputComponent,
1202
- createThrusterMovementComponent,
1203
- createThrusterStateComponent,
1204
- createTransformComponent,
1205
- defineBehavior,
1206
- hasAnyWorldBoundary2DHit,
1207
- useBehavior
1208
- };
1209
- //# sourceMappingURL=behaviors.js.map