@zylem/behaviors 0.4.0 → 0.5.1

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