@zylem/behaviors 0.4.1 → 0.5.2

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 (148) hide show
  1. package/README.md +13 -1
  2. package/dist/{behavior-system-y8SGrGWy.d.ts → behavior-system-CldqNUgr.d.ts} +334 -19
  3. package/dist/{chunk-GFARAJV2.js → chunk-2PAHQZZD.js} +44 -73
  4. package/dist/chunk-2PAHQZZD.js.map +1 -0
  5. package/dist/chunk-4CEW64HO.js +210 -0
  6. package/dist/chunk-4CEW64HO.js.map +1 -0
  7. package/dist/chunk-4LLK2FJI.js +344 -0
  8. package/dist/chunk-4LLK2FJI.js.map +1 -0
  9. package/dist/chunk-4MZ57HPD.js +323 -0
  10. package/dist/chunk-4MZ57HPD.js.map +1 -0
  11. package/dist/chunk-52FJIDGL.js +30 -0
  12. package/dist/chunk-52FJIDGL.js.map +1 -0
  13. package/dist/{chunk-UYFOLI2X.js → chunk-5A5AYP2C.js} +1 -1
  14. package/dist/{chunk-UYFOLI2X.js.map → chunk-5A5AYP2C.js.map} +1 -1
  15. package/dist/chunk-5WRI5ZAA.js +31 -0
  16. package/dist/chunk-5WRI5ZAA.js.map +1 -0
  17. package/dist/{chunk-ZRQWSURL.js → chunk-5XPPLTC7.js} +2 -2
  18. package/dist/chunk-5XPPLTC7.js.map +1 -0
  19. package/dist/{chunk-VMQHNJJI.js → chunk-7DGXUAPV.js} +17 -23
  20. package/dist/chunk-7DGXUAPV.js.map +1 -0
  21. package/dist/{chunk-5SM5SH5O.js → chunk-7JLWZXPA.js} +2 -2
  22. package/dist/{chunk-QDCJLJUV.js → chunk-7W3KY55G.js} +22 -13
  23. package/dist/chunk-7W3KY55G.js.map +1 -0
  24. package/dist/chunk-AS3HR7AS.js +188 -0
  25. package/dist/chunk-AS3HR7AS.js.map +1 -0
  26. package/dist/chunk-BAXJ7XHJ.js +155 -0
  27. package/dist/chunk-BAXJ7XHJ.js.map +1 -0
  28. package/dist/chunk-DZ4LLQON.js +867 -0
  29. package/dist/chunk-DZ4LLQON.js.map +1 -0
  30. package/dist/{chunk-4L36OPY2.js → chunk-G2KUXVU6.js} +81 -135
  31. package/dist/chunk-G2KUXVU6.js.map +1 -0
  32. package/dist/{chunk-BXP4K7RF.js → chunk-JVADRLWX.js} +57 -186
  33. package/dist/chunk-JVADRLWX.js.map +1 -0
  34. package/dist/{chunk-ZA43LPRM.js → chunk-KIYUUSPK.js} +2 -2
  35. package/dist/chunk-LZIHSJZR.js +191 -0
  36. package/dist/chunk-LZIHSJZR.js.map +1 -0
  37. package/dist/{chunk-WWVD5DR5.js → chunk-M3QDKKDF.js} +2 -8
  38. package/dist/chunk-M3QDKKDF.js.map +1 -0
  39. package/dist/{chunk-FTDZAKZW.js → chunk-NQPYMUDF.js} +7 -5
  40. package/dist/{chunk-FTDZAKZW.js.map → chunk-NQPYMUDF.js.map} +1 -1
  41. package/dist/{chunk-XP3ASVGJ.js → chunk-NW4SE5XW.js} +2 -2
  42. package/dist/{chunk-KDY5N6J4.js → chunk-OPZ2LVS2.js} +1 -22
  43. package/dist/{chunk-KDY5N6J4.js.map → chunk-OPZ2LVS2.js.map} +1 -1
  44. package/dist/{chunk-5XZWTFGF.js → chunk-PLIXVF7B.js} +3 -3
  45. package/dist/chunk-PLIXVF7B.js.map +1 -0
  46. package/dist/{chunk-27UPCP5B.js → chunk-QS3VXWVO.js} +22 -13
  47. package/dist/chunk-QS3VXWVO.js.map +1 -0
  48. package/dist/{chunk-Y7YIAWPW.js → chunk-SGHIGTVZ.js} +1 -11
  49. package/dist/{chunk-Y7YIAWPW.js.map → chunk-SGHIGTVZ.js.map} +1 -1
  50. package/dist/{chunk-UL6Q5HDP.js → chunk-TBUKJKY6.js} +3 -25
  51. package/dist/chunk-TBUKJKY6.js.map +1 -0
  52. package/dist/{chunk-Z72F2ENM.js → chunk-WU6XXL3D.js} +17 -26
  53. package/dist/chunk-WU6XXL3D.js.map +1 -0
  54. package/dist/{chunk-A4CA35DQ.js → chunk-Y4PHT2HG.js} +25 -45
  55. package/dist/chunk-Y4PHT2HG.js.map +1 -0
  56. package/dist/components-BmievOxF.d.ts +30 -0
  57. package/dist/cooldown.d.ts +2 -2
  58. package/dist/cooldown.js +2 -1
  59. package/dist/coordinators/boundary-ricochet-3d.d.ts +4 -4
  60. package/dist/coordinators/boundary-ricochet-3d.js +1 -0
  61. package/dist/coordinators/boundary-ricochet.d.ts +4 -4
  62. package/dist/coordinators/boundary-ricochet.js +3 -2
  63. package/dist/coordinators/first-person-shooter.js +1 -0
  64. package/dist/coordinators/multidirectional-space-shooter.d.ts +3 -3
  65. package/dist/coordinators/multidirectional-space-shooter.js +5 -3
  66. package/dist/coordinators/top-down-shooter.d.ts +3 -3
  67. package/dist/coordinators/top-down-shooter.js +5 -3
  68. package/dist/core.d.ts +298 -8
  69. package/dist/core.js +502 -14
  70. package/dist/core.js.map +1 -1
  71. package/dist/destructible-3d.d.ts +32 -4
  72. package/dist/destructible-3d.js +2 -1
  73. package/dist/destructible-prebake.js +1 -0
  74. package/dist/{entity-types-CmtQ-IBp.d.ts → entity-types-DhfsZbSK.d.ts} +5 -3
  75. package/dist/first-person.d.ts +27 -38
  76. package/dist/first-person.js +8 -8
  77. package/dist/index.d.ts +14 -15
  78. package/dist/index.js +38 -53
  79. package/dist/jumper-2d.d.ts +3 -17
  80. package/dist/jumper-2d.js +4 -9
  81. package/dist/jumper-3d.d.ts +5 -39
  82. package/dist/jumper-3d.js +5 -10
  83. package/dist/{moveable-Cl9h-_w7.d.ts → moveable-rdIvWiak.d.ts} +21 -4
  84. package/dist/particle-emitter/presets.js +1 -0
  85. package/dist/particle-emitter.d.ts +3 -3
  86. package/dist/particle-emitter.js +3 -2
  87. package/dist/platformer-2d.d.ts +21 -82
  88. package/dist/platformer-2d.js +4 -5
  89. package/dist/platformer-3d.d.ts +21 -78
  90. package/dist/platformer-3d.js +4 -5
  91. package/dist/ricochet-2d.d.ts +3 -3
  92. package/dist/ricochet-2d.js +4 -3
  93. package/dist/ricochet-3d.d.ts +3 -3
  94. package/dist/ricochet-3d.js +4 -3
  95. package/dist/runtime-2d.d.ts +2 -2
  96. package/dist/runtime-2d.js +1 -0
  97. package/dist/runtime-2d.js.map +1 -1
  98. package/dist/screen-visibility.d.ts +2 -2
  99. package/dist/screen-visibility.js +1 -0
  100. package/dist/screen-wrap.d.ts +2 -2
  101. package/dist/screen-wrap.js +4 -3
  102. package/dist/shooter-2d.d.ts +3 -3
  103. package/dist/shooter-2d.js +4 -3
  104. package/dist/thruster.d.ts +5 -34
  105. package/dist/thruster.js +4 -6
  106. package/dist/top-down-movement.d.ts +9 -27
  107. package/dist/top-down-movement.js +6 -6
  108. package/dist/{vector-Bm65-FOG.d.ts → vector-DjhLUrak.d.ts} +2 -4
  109. package/dist/world-boundary-2d.d.ts +2 -2
  110. package/dist/world-boundary-2d.js +5 -4
  111. package/dist/world-boundary-3d.d.ts +2 -2
  112. package/dist/world-boundary-3d.js +4 -3
  113. package/package.json +4 -5
  114. package/dist/chunk-27UPCP5B.js.map +0 -1
  115. package/dist/chunk-4L36OPY2.js.map +0 -1
  116. package/dist/chunk-4URF65BR.js +0 -44
  117. package/dist/chunk-4URF65BR.js.map +0 -1
  118. package/dist/chunk-5XZWTFGF.js.map +0 -1
  119. package/dist/chunk-67XLJ26X.js +0 -536
  120. package/dist/chunk-67XLJ26X.js.map +0 -1
  121. package/dist/chunk-75FJIUIG.js +0 -240
  122. package/dist/chunk-75FJIUIG.js.map +0 -1
  123. package/dist/chunk-A4CA35DQ.js.map +0 -1
  124. package/dist/chunk-BXP4K7RF.js.map +0 -1
  125. package/dist/chunk-F72WX4MH.js +0 -422
  126. package/dist/chunk-F72WX4MH.js.map +0 -1
  127. package/dist/chunk-GFARAJV2.js.map +0 -1
  128. package/dist/chunk-JXMF4NUI.js +0 -309
  129. package/dist/chunk-JXMF4NUI.js.map +0 -1
  130. package/dist/chunk-OTLSUYQQ.js +0 -29
  131. package/dist/chunk-OTLSUYQQ.js.map +0 -1
  132. package/dist/chunk-Q75LKHDF.js +0 -56
  133. package/dist/chunk-Q75LKHDF.js.map +0 -1
  134. package/dist/chunk-QDCJLJUV.js.map +0 -1
  135. package/dist/chunk-RDHSHEZM.js +0 -479
  136. package/dist/chunk-RDHSHEZM.js.map +0 -1
  137. package/dist/chunk-UIG7MWS2.js +0 -518
  138. package/dist/chunk-UIG7MWS2.js.map +0 -1
  139. package/dist/chunk-UL6Q5HDP.js.map +0 -1
  140. package/dist/chunk-VMQHNJJI.js.map +0 -1
  141. package/dist/chunk-WWVD5DR5.js.map +0 -1
  142. package/dist/chunk-Z72F2ENM.js.map +0 -1
  143. package/dist/chunk-ZRQWSURL.js.map +0 -1
  144. package/dist/components-BWG1GkZA.d.ts +0 -20
  145. package/dist/use-behavior-B85d5u7-.d.ts +0 -13
  146. /package/dist/{chunk-5SM5SH5O.js.map → chunk-7JLWZXPA.js.map} +0 -0
  147. /package/dist/{chunk-ZA43LPRM.js.map → chunk-KIYUUSPK.js.map} +0 -0
  148. /package/dist/{chunk-XP3ASVGJ.js.map → chunk-NW4SE5XW.js.map} +0 -0
package/dist/core.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  createPhysicsBodyComponent,
3
3
  createTransformComponent,
4
4
  useBehavior
5
- } from "./chunk-UYFOLI2X.js";
5
+ } from "./chunk-5A5AYP2C.js";
6
6
  import {
7
7
  VEC2_ONE,
8
8
  VEC2_ZERO,
@@ -12,35 +12,40 @@ import {
12
12
  isVec3Input,
13
13
  normalizeVec2,
14
14
  normalizeVec3,
15
- toRapierVector3,
16
15
  toThreeVector2,
17
16
  toThreeVector3
18
- } from "./chunk-WWVD5DR5.js";
19
- import {
20
- clearVelocityIntent,
21
- setVelocityIntent
22
- } from "./chunk-OTLSUYQQ.js";
17
+ } from "./chunk-M3QDKKDF.js";
23
18
  import {
24
19
  SyncStateMachine,
25
- t
26
- } from "./chunk-4URF65BR.js";
20
+ import_typescript_fsm
21
+ } from "./chunk-AS3HR7AS.js";
27
22
  import {
23
+ STAGE_EVENT_STRIDE,
28
24
  STAGE_INPUT_BUTTON_JUMP,
29
25
  STAGE_INPUT_BUTTON_RUN,
26
+ STAGE_INPUT_STRIDE,
27
+ STAGE_INVALID_SLOT,
28
+ STAGE_RENDER_STRIDE,
29
+ STAGE_SNAPSHOT_STRIDE,
30
30
  StageBodyKind,
31
31
  StageBoundaryDim,
32
32
  StageEventType,
33
+ StagePlatformerShapeSource,
33
34
  StageRicochetDim,
34
35
  StageRicochetReflection,
35
36
  StageTopDownPlane,
36
- readBehaviorSnapshot
37
- } from "./chunk-Q75LKHDF.js";
37
+ WasmStageRuntime,
38
+ createWasmStageRuntime,
39
+ readBehaviorSnapshot,
40
+ readStageRenderSlot
41
+ } from "./chunk-DZ4LLQON.js";
38
42
  import {
39
43
  defineBehavior
40
44
  } from "./chunk-EZNCCO4L.js";
45
+ import "./chunk-5WRI5ZAA.js";
41
46
 
42
47
  // src/lib/capabilities/transform-store.ts
43
- import { proxy } from "valtio";
48
+ import { proxy } from "valtio/vanilla";
44
49
  function createTransformStore(initial) {
45
50
  const defaultState = {
46
51
  position: { x: 0, y: 0, z: 0 },
@@ -69,6 +74,30 @@ function createTransformStore(initial) {
69
74
  });
70
75
  }
71
76
 
77
+ // src/lib/capabilities/velocity-intents.ts
78
+ function setVelocityIntent(store, sourceId, vector, options = {}) {
79
+ const prev = store.velocityChannels[sourceId];
80
+ const mode = options.mode ?? prev?.mode ?? "replace";
81
+ const priority = options.priority ?? prev?.priority ?? 0;
82
+ const isAdditiveMerge = mode === "add" && prev?.mode === "add";
83
+ const x = vector.x == null ? prev?.x : isAdditiveMerge ? (prev?.x ?? 0) + vector.x : vector.x;
84
+ const y = vector.y == null ? prev?.y : isAdditiveMerge ? (prev?.y ?? 0) + vector.y : vector.y;
85
+ const z = vector.z == null ? prev?.z : isAdditiveMerge ? (prev?.z ?? 0) + vector.z : vector.z;
86
+ store.velocityChannels[sourceId] = {
87
+ x,
88
+ y,
89
+ z,
90
+ mode,
91
+ priority
92
+ };
93
+ store.dirty.velocityChannels = true;
94
+ }
95
+ function clearVelocityIntent(store, sourceId) {
96
+ if (!(sourceId in store.velocityChannels)) return;
97
+ delete store.velocityChannels[sourceId];
98
+ store.dirty.velocityChannels = true;
99
+ }
100
+
72
101
  // src/lib/capabilities/moveable.ts
73
102
  import { Vector3 } from "three";
74
103
  function moveX(entity, delta) {
@@ -192,6 +221,452 @@ function makeMoveable(entity) {
192
221
  return moveable;
193
222
  }
194
223
 
224
+ // src/lib/runtime/mock-behavior-runtime.ts
225
+ function freshSlot() {
226
+ return {
227
+ pose: { position: [0, 0, 0], rotation: [0, 0, 0, 1] },
228
+ snapshot: null,
229
+ input: { axisA: 0, axisB: 0, yawDelta: 0, pitchDelta: 0, reservedA: 0, reservedB: 0, buttons: 0 },
230
+ attachments: /* @__PURE__ */ new Map(),
231
+ inputs: [],
232
+ screenWrapQuery: null,
233
+ worldBoundaryQuery: null
234
+ };
235
+ }
236
+ var MockBehaviorRuntime = class {
237
+ slots = /* @__PURE__ */ new Map();
238
+ slot(handle) {
239
+ let state = this.slots.get(handle);
240
+ if (!state) {
241
+ state = freshSlot();
242
+ this.slots.set(handle, state);
243
+ }
244
+ return state;
245
+ }
246
+ // ─── Test seams ─────────────────────────────────────────────────────
247
+ /** Seed the pose returned by {@link getPose} for a handle. */
248
+ setPose(handle, pose) {
249
+ this.slot(handle).pose = pose;
250
+ }
251
+ /** Seed the snapshot returned by {@link readSnapshot} for a handle. */
252
+ setSnapshot(handle, snapshot) {
253
+ this.slot(handle).snapshot = snapshot;
254
+ }
255
+ /** Seed {@link queryScreenWrap} results. */
256
+ setScreenWrapQuery(handle, value) {
257
+ this.slot(handle).screenWrapQuery = value;
258
+ }
259
+ /** Seed {@link queryWorldBoundary} results. */
260
+ setWorldBoundaryQuery(handle, value) {
261
+ this.slot(handle).worldBoundaryQuery = value;
262
+ }
263
+ /** Attachment options recorded for a handle, keyed by behavior name. */
264
+ getAttachment(handle, kind) {
265
+ return this.slots.get(handle)?.attachments.get(kind);
266
+ }
267
+ hasAttachment(handle, kind) {
268
+ return this.slots.get(handle)?.attachments.has(kind) ?? false;
269
+ }
270
+ /** Every per-entity input call recorded for a handle, oldest first. */
271
+ getInputCalls(handle) {
272
+ return this.slots.get(handle)?.inputs ?? [];
273
+ }
274
+ /** Last written slot-indexed input state for a handle. */
275
+ getBatchedInput(handle) {
276
+ return this.slot(handle).input;
277
+ }
278
+ reset() {
279
+ this.slots.clear();
280
+ }
281
+ // ─── BehaviorRuntime contract ───────────────────────────────────────
282
+ setPosition(handle, x, y, z) {
283
+ const pose = this.slot(handle).pose;
284
+ pose.position[0] = x;
285
+ pose.position[1] = y;
286
+ pose.position[2] = z;
287
+ return true;
288
+ }
289
+ setRotation(handle, x, y, z, w) {
290
+ const pose = this.slot(handle).pose;
291
+ pose.rotation[0] = x;
292
+ pose.rotation[1] = y;
293
+ pose.rotation[2] = z;
294
+ pose.rotation[3] = w;
295
+ return true;
296
+ }
297
+ getPose(handle, out) {
298
+ const state = this.slots.get(handle);
299
+ if (!state) return null;
300
+ const result = out ?? { position: [0, 0, 0], rotation: [0, 0, 0, 1] };
301
+ result.position[0] = state.pose.position[0];
302
+ result.position[1] = state.pose.position[1];
303
+ result.position[2] = state.pose.position[2];
304
+ result.rotation[0] = state.pose.rotation[0];
305
+ result.rotation[1] = state.pose.rotation[1];
306
+ result.rotation[2] = state.pose.rotation[2];
307
+ result.rotation[3] = state.pose.rotation[3];
308
+ return result;
309
+ }
310
+ writeInputAxes(handle, axisA, axisB) {
311
+ const input = this.slot(handle).input;
312
+ input.axisA = axisA;
313
+ input.axisB = axisB;
314
+ return true;
315
+ }
316
+ writeInputLook(handle, yawDelta, pitchDelta) {
317
+ const input = this.slot(handle).input;
318
+ input.yawDelta = yawDelta;
319
+ input.pitchDelta = pitchDelta;
320
+ return true;
321
+ }
322
+ writeInputReserved(handle, reservedA, reservedB) {
323
+ const input = this.slot(handle).input;
324
+ input.reservedA = reservedA;
325
+ input.reservedB = reservedB;
326
+ return true;
327
+ }
328
+ writeInputButton(handle, buttonMask, pressed) {
329
+ const input = this.slot(handle).input;
330
+ input.buttons = pressed ? input.buttons | buttonMask : input.buttons & ~buttonMask;
331
+ return true;
332
+ }
333
+ readSnapshot(handle, out) {
334
+ const snapshot = this.slots.get(handle)?.snapshot ?? null;
335
+ if (!snapshot) return null;
336
+ if (!out) return { ...snapshot };
337
+ Object.assign(out, snapshot);
338
+ return out;
339
+ }
340
+ attachThruster(handle, opts) {
341
+ this.slot(handle).attachments.set("thruster", opts);
342
+ return true;
343
+ }
344
+ setThrusterInput(handle, thrust, rotate, vectorX = 0, vectorY = 0) {
345
+ this.slot(handle).inputs.push({
346
+ kind: "thruster",
347
+ args: [thrust, rotate, vectorX, vectorY]
348
+ });
349
+ }
350
+ attachTopDown(handle, opts) {
351
+ this.slot(handle).attachments.set("topDown", opts);
352
+ return true;
353
+ }
354
+ setTopDownInput(handle, moveX2, moveY2) {
355
+ this.slot(handle).inputs.push({ kind: "topDown", args: [moveX2, moveY2] });
356
+ }
357
+ attachShooter2D(handle, opts) {
358
+ this.slot(handle).attachments.set("shooter2d", opts);
359
+ return true;
360
+ }
361
+ attachScreenWrap(handle, opts) {
362
+ this.slot(handle).attachments.set("screenWrap", opts);
363
+ return true;
364
+ }
365
+ queryScreenWrap(handle) {
366
+ return this.slots.get(handle)?.screenWrapQuery ?? null;
367
+ }
368
+ attachWorldBoundary(handle, dim, opts) {
369
+ this.slot(handle).attachments.set("worldBoundary", { dim, ...opts });
370
+ return true;
371
+ }
372
+ queryWorldBoundary(handle) {
373
+ return this.slots.get(handle)?.worldBoundaryQuery ?? null;
374
+ }
375
+ attachRicochet(handle, dim, opts) {
376
+ this.slot(handle).attachments.set("ricochet", { dim, ...opts });
377
+ return true;
378
+ }
379
+ attachPlatformer3D(handle, opts) {
380
+ this.slot(handle).attachments.set("platformer3d", opts);
381
+ return true;
382
+ }
383
+ setPlatformer3DInputAxes(handle, moveX2, moveZ2) {
384
+ this.slot(handle).inputs.push({ kind: "platformer3dAxes", args: [moveX2, moveZ2] });
385
+ }
386
+ setPlatformer3DInputButtons(handle, jump, run) {
387
+ this.slot(handle).inputs.push({ kind: "platformer3dButtons", args: [jump, run] });
388
+ }
389
+ attachPlatformer2D(handle, opts) {
390
+ this.slot(handle).attachments.set("platformer2d", opts);
391
+ return true;
392
+ }
393
+ setPlatformer2DInputAxes(handle, moveX2) {
394
+ this.slot(handle).inputs.push({ kind: "platformer2dAxes", args: [moveX2] });
395
+ }
396
+ setPlatformer2DInputButtons(handle, jump, run) {
397
+ this.slot(handle).inputs.push({ kind: "platformer2dButtons", args: [jump, run] });
398
+ }
399
+ attachFirstPerson(handle, opts) {
400
+ this.slot(handle).attachments.set("firstPerson", opts);
401
+ return true;
402
+ }
403
+ setFirstPersonInput(handle, input) {
404
+ this.slot(handle).inputs.push({ kind: "firstPerson", args: [input] });
405
+ }
406
+ attachJumper2D(handle, opts) {
407
+ this.slot(handle).attachments.set("jumper2d", opts);
408
+ return true;
409
+ }
410
+ setJumper2DInput(handle, jumpPressed) {
411
+ this.slot(handle).inputs.push({ kind: "jumper2d", args: [jumpPressed] });
412
+ }
413
+ attachJumper3D(handle, opts) {
414
+ this.slot(handle).attachments.set("jumper3d", opts);
415
+ return true;
416
+ }
417
+ setJumper3DInput(handle, jumpPressed) {
418
+ this.slot(handle).inputs.push({ kind: "jumper3d", args: [jumpPressed] });
419
+ }
420
+ };
421
+
422
+ // src/lib/runtime/simulation.ts
423
+ var DEFAULT_FIXED_TIMESTEP = 1 / 60;
424
+ var DEFAULT_MAX_SUB_STEPS = 2;
425
+ var Simulation = class {
426
+ /**
427
+ * The low-level wasm adapter. Behavior systems receive this as their
428
+ * `wasmStage` context so descriptors can attach/query behaviors by slot;
429
+ * hosts should prefer the handle-based methods on the Simulation itself.
430
+ */
431
+ adapter;
432
+ fixedTimestep;
433
+ maxSubSteps;
434
+ accumulator = 0;
435
+ nextEntityId = 1;
436
+ slotToHandle = /* @__PURE__ */ new Map();
437
+ idToHandle = /* @__PURE__ */ new Map();
438
+ previousRender = new Float32Array(0);
439
+ disposed = false;
440
+ eventsEnabled = true;
441
+ constructor(adapter, options = {}) {
442
+ this.adapter = adapter;
443
+ this.fixedTimestep = options.fixedTimestep ?? DEFAULT_FIXED_TIMESTEP;
444
+ this.maxSubSteps = Math.max(1, options.maxSubSteps ?? DEFAULT_MAX_SUB_STEPS);
445
+ }
446
+ // ─── Entity lifecycle ───────────────────────────────────────────────
447
+ /**
448
+ * Create a runtime entity from a plain definition. Returns `null` when
449
+ * the runtime rejects the allocation.
450
+ */
451
+ spawn(definition = {}) {
452
+ const slot = this.adapter.createEntity();
453
+ if (slot === 4294967295) return null;
454
+ const body = definition.body;
455
+ const bodyConfig = {
456
+ kind: body?.kind ?? StageBodyKind.Dynamic,
457
+ position: body?.position ?? [0, 0, 0],
458
+ rotation: body?.rotation ?? [0, 0, 0, 1],
459
+ linearDamping: body?.linearDamping ?? 0,
460
+ angularDamping: body?.angularDamping ?? 0,
461
+ gravityScale: body?.gravityScale ?? 1,
462
+ canSleep: body?.canSleep ?? false,
463
+ ccdEnabled: body?.ccdEnabled ?? true,
464
+ lockRotation: body?.lockRotation ?? [false, false, false],
465
+ lockTranslation: body?.lockTranslation ?? [false, false, false]
466
+ };
467
+ this.adapter.attachBody(slot, bodyConfig);
468
+ for (const collider of definition.colliders ?? []) {
469
+ const config = {
470
+ shape: collider.shape,
471
+ offset: collider.offset ?? [0, 0, 0],
472
+ friction: collider.friction ?? 0.5,
473
+ restitution: collider.restitution ?? 0,
474
+ sensor: collider.sensor ?? false,
475
+ collisionGroups: collider.collisionGroups ?? 4294967295
476
+ };
477
+ this.adapter.addCollider(slot, config);
478
+ }
479
+ const handle = { id: this.nextEntityId++, slot };
480
+ this.slotToHandle.set(slot, handle);
481
+ this.idToHandle.set(handle.id, handle);
482
+ return handle;
483
+ }
484
+ despawn(handle) {
485
+ const registered = this.idToHandle.get(handle.id);
486
+ if (!registered) return false;
487
+ this.idToHandle.delete(handle.id);
488
+ if (this.slotToHandle.get(registered.slot) === registered) {
489
+ this.slotToHandle.delete(registered.slot);
490
+ }
491
+ return this.adapter.destroyEntity(registered.slot);
492
+ }
493
+ /** Resolve a raw runtime slot back to its public handle, if alive. */
494
+ handleForSlot(slot) {
495
+ return this.slotToHandle.get(slot) ?? null;
496
+ }
497
+ /**
498
+ * Toggle collision-event collection. When disabled the wasm stage skips
499
+ * per-step contact-pair hashing and `update()` skips event draining /
500
+ * translation entirely (`frame.events` stays empty). Re-enabling emits
501
+ * `collisionStarted` for every currently overlapping pair on the next
502
+ * step, so late listeners observe current state.
503
+ */
504
+ setEventsEnabled(enabled) {
505
+ if (this.eventsEnabled === enabled) return;
506
+ this.eventsEnabled = enabled;
507
+ this.adapter.setEventsEnabled(enabled);
508
+ }
509
+ get areEventsEnabled() {
510
+ return this.eventsEnabled;
511
+ }
512
+ get entityCount() {
513
+ return this.idToHandle.size;
514
+ }
515
+ // ─── Per-frame update ───────────────────────────────────────────────
516
+ /**
517
+ * Advance the simulation by `deltaTime` seconds using a fixed-timestep
518
+ * accumulator. Runs zero or more fixed steps and returns a
519
+ * {@link SimulationFrame} with translated events and interpolation data.
520
+ */
521
+ update(deltaTime) {
522
+ const events = [];
523
+ this.accumulator += Math.max(0, deltaTime);
524
+ let steps = 0;
525
+ while (this.accumulator >= this.fixedTimestep && steps < this.maxSubSteps) {
526
+ this.capturePreviousRender();
527
+ this.adapter.step(this.fixedTimestep);
528
+ if (this.eventsEnabled) {
529
+ this.translateEvents(this.adapter.drainEvents(), events);
530
+ }
531
+ this.accumulator -= this.fixedTimestep;
532
+ steps++;
533
+ }
534
+ if (steps === this.maxSubSteps && this.accumulator >= this.fixedTimestep) {
535
+ this.accumulator = this.accumulator % this.fixedTimestep;
536
+ }
537
+ return {
538
+ deltaTime,
539
+ stepsTaken: steps,
540
+ alpha: this.fixedTimestep > 0 ? this.accumulator / this.fixedTimestep : 0,
541
+ render: this.adapter.render,
542
+ previousRender: this.previousRender,
543
+ renderStride: STAGE_RENDER_STRIDE,
544
+ events
545
+ };
546
+ }
547
+ capturePreviousRender() {
548
+ this.adapter.refreshViews();
549
+ const current = this.adapter.render;
550
+ if (this.previousRender.length !== current.length) {
551
+ this.previousRender = new Float32Array(current.length);
552
+ }
553
+ this.previousRender.set(current);
554
+ }
555
+ translateEvents(raw, out) {
556
+ for (const event of raw) {
557
+ const primary = event.primarySlot !== null ? this.handleForSlot(event.primarySlot) : null;
558
+ const secondary = event.secondarySlot !== null ? this.handleForSlot(event.secondarySlot) : null;
559
+ switch (event.type) {
560
+ case StageEventType.CollisionStarted:
561
+ case StageEventType.CollisionStopped:
562
+ out.push({
563
+ type: event.type === StageEventType.CollisionStarted ? "collisionStarted" : "collisionStopped",
564
+ a: primary,
565
+ b: secondary,
566
+ sensor: event.payload[0] !== 0,
567
+ normal: [event.payload[1], event.payload[2]]
568
+ });
569
+ break;
570
+ case StageEventType.BoundaryHit:
571
+ out.push({
572
+ type: "boundaryHit",
573
+ entity: primary,
574
+ axes: event.payload[0] | 0,
575
+ signs: event.payload[1] | 0
576
+ });
577
+ break;
578
+ case StageEventType.Ricochet:
579
+ out.push({ type: "ricochet", entity: primary, kind: event.payload[0] | 0 });
580
+ break;
581
+ case StageEventType.RegionEntered:
582
+ out.push({ type: "regionEntered", entity: primary, triggerId: event.payload[0] | 0 });
583
+ break;
584
+ case StageEventType.JumpStarted:
585
+ out.push({ type: "jumpStarted", entity: primary, jumpCount: event.payload[0] | 0 });
586
+ break;
587
+ case StageEventType.Landed:
588
+ out.push({ type: "landed", entity: primary });
589
+ break;
590
+ case StageEventType.Wrapped:
591
+ out.push({ type: "wrapped", entity: primary, axes: event.payload[0] | 0 });
592
+ break;
593
+ case StageEventType.CooldownFired:
594
+ out.push({ type: "cooldownFired", entity: primary, cooldownId: event.payload[0] | 0 });
595
+ break;
596
+ default:
597
+ break;
598
+ }
599
+ }
600
+ }
601
+ // ─── Handle-based reads / writes ────────────────────────────────────
602
+ getPose(handle, out) {
603
+ return this.adapter.getPose(handle.slot, out);
604
+ }
605
+ setPosition(handle, x, y, z) {
606
+ return this.adapter.setPosition(handle.slot, x, y, z);
607
+ }
608
+ setRotation(handle, x, y, z, w) {
609
+ return this.adapter.setRotation(handle.slot, x, y, z, w);
610
+ }
611
+ setLinearVelocity(handle, x, y, z) {
612
+ return this.adapter.setLinearVelocity(handle.slot, x, y, z);
613
+ }
614
+ getLinearVelocity(handle) {
615
+ return this.adapter.getLinearVelocity(handle.slot);
616
+ }
617
+ setAngularVelocity(handle, x, y, z) {
618
+ return this.adapter.setAngularVelocity(handle.slot, x, y, z);
619
+ }
620
+ applyImpulse(handle, x, y, z) {
621
+ return this.adapter.applyImpulse(handle.slot, x, y, z);
622
+ }
623
+ readRenderSlot(handle, out) {
624
+ return readStageRenderSlot(this.adapter.render, handle.slot, out);
625
+ }
626
+ /** Decode a handle's pose from the previous-step render copy. */
627
+ readPreviousRenderSlot(handle, out) {
628
+ return readStageRenderSlot(this.previousRender, handle.slot, out);
629
+ }
630
+ readSnapshot(handle, out) {
631
+ return this.adapter.readSnapshot(handle.slot, out);
632
+ }
633
+ // ─── World-level controls / diagnostics ─────────────────────────────
634
+ setGravity(x, y, z) {
635
+ this.adapter.setGravity(x, y, z);
636
+ }
637
+ /** Physics raycast; the hit's slot is resolved to an entity handle. */
638
+ castRay(origin, dir, maxDistance, solidOnly = true) {
639
+ const hit = this.adapter.castRay(origin, dir, maxDistance, solidOnly);
640
+ if (!hit) return null;
641
+ return {
642
+ distance: hit.distance,
643
+ normal: hit.normal,
644
+ entity: this.handleForSlot(hit.slot)
645
+ };
646
+ }
647
+ getBodyInfo(handle) {
648
+ return this.adapter.getBodyInfo(handle.slot);
649
+ }
650
+ getDebugRender() {
651
+ return this.adapter.getDebugRender();
652
+ }
653
+ get isDisposed() {
654
+ return this.disposed;
655
+ }
656
+ /** Destroy the wasm stage. The Simulation must not be used afterwards. */
657
+ dispose() {
658
+ if (this.disposed) return;
659
+ this.disposed = true;
660
+ this.slotToHandle.clear();
661
+ this.idToHandle.clear();
662
+ this.adapter.dispose();
663
+ }
664
+ };
665
+ async function createSimulation(options = {}) {
666
+ const adapter = await createWasmStageRuntime(options);
667
+ return new Simulation(adapter, options);
668
+ }
669
+
195
670
  // src/lib/entities/entity.ts
196
671
  var GameEntity = class {
197
672
  uuid;
@@ -201,6 +676,7 @@ var GameEntity = class {
201
676
  group = null;
202
677
  mesh;
203
678
  materials;
679
+ /** Opaque host-owned physics references (never inspected by behaviors). */
204
680
  body = null;
205
681
  collider = null;
206
682
  colliderDesc;
@@ -253,13 +729,22 @@ var GameEntity = class {
253
729
  return this.__behaviorRefs;
254
730
  }
255
731
  };
732
+ var export_t = import_typescript_fsm.t;
256
733
  export {
257
734
  GameEntity,
735
+ MockBehaviorRuntime,
736
+ STAGE_EVENT_STRIDE,
258
737
  STAGE_INPUT_BUTTON_JUMP,
259
738
  STAGE_INPUT_BUTTON_RUN,
739
+ STAGE_INPUT_STRIDE,
740
+ STAGE_INVALID_SLOT,
741
+ STAGE_RENDER_STRIDE,
742
+ STAGE_SNAPSHOT_STRIDE,
743
+ Simulation,
260
744
  StageBodyKind,
261
745
  StageBoundaryDim,
262
746
  StageEventType,
747
+ StagePlatformerShapeSource,
263
748
  StageRicochetDim,
264
749
  StageRicochetReflection,
265
750
  StageTopDownPlane,
@@ -268,10 +753,13 @@ export {
268
753
  VEC2_ZERO,
269
754
  VEC3_ONE,
270
755
  VEC3_ZERO,
756
+ WasmStageRuntime,
271
757
  clearVelocityIntent,
272
758
  createPhysicsBodyComponent,
759
+ createSimulation,
273
760
  createTransformComponent,
274
761
  createTransformStore,
762
+ createWasmStageRuntime,
275
763
  defineBehavior,
276
764
  getPosition,
277
765
  getVelocity,
@@ -288,14 +776,14 @@ export {
288
776
  normalizeVec2,
289
777
  normalizeVec3,
290
778
  readBehaviorSnapshot,
779
+ readStageRenderSlot,
291
780
  resetVelocity,
292
781
  setPosition,
293
782
  setPositionX,
294
783
  setPositionY,
295
784
  setPositionZ,
296
785
  setVelocityIntent,
297
- t,
298
- toRapierVector3,
786
+ export_t as t,
299
787
  toThreeVector2,
300
788
  toThreeVector3,
301
789
  useBehavior,