@zylem/behaviors 0.4.1 → 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 (145) hide show
  1. package/README.md +13 -1
  2. package/dist/{behavior-system-y8SGrGWy.d.ts → behavior-system-CeJXzNvr.d.ts} +305 -17
  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-A4CA35DQ.js → chunk-5EBIIYZD.js} +25 -45
  10. package/dist/chunk-5EBIIYZD.js.map +1 -0
  11. package/dist/{chunk-4L36OPY2.js → chunk-5NJGASUJ.js} +81 -135
  12. package/dist/chunk-5NJGASUJ.js.map +1 -0
  13. package/dist/{chunk-27UPCP5B.js → chunk-5UMCWV5A.js} +22 -13
  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-Z72F2ENM.js → chunk-AEBDZWCP.js} +17 -26
  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-VMQHNJJI.js → chunk-F6ESSHQK.js} +17 -23
  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-QDCJLJUV.js → chunk-KOWGILCN.js} +22 -13
  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 +480 -13
  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 -8
  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 -9
  79. package/dist/jumper-3d.d.ts +5 -39
  80. package/dist/jumper-3d.js +5 -10
  81. package/dist/{moveable-Cl9h-_w7.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 -5
  87. package/dist/platformer-3d.d.ts +13 -78
  88. package/dist/platformer-3d.js +4 -5
  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 +2 -3
  112. package/dist/chunk-27UPCP5B.js.map +0 -1
  113. package/dist/chunk-4L36OPY2.js.map +0 -1
  114. package/dist/chunk-4URF65BR.js +0 -44
  115. package/dist/chunk-4URF65BR.js.map +0 -1
  116. package/dist/chunk-5XZWTFGF.js.map +0 -1
  117. package/dist/chunk-67XLJ26X.js +0 -536
  118. package/dist/chunk-67XLJ26X.js.map +0 -1
  119. package/dist/chunk-75FJIUIG.js +0 -240
  120. package/dist/chunk-75FJIUIG.js.map +0 -1
  121. package/dist/chunk-A4CA35DQ.js.map +0 -1
  122. package/dist/chunk-BXP4K7RF.js.map +0 -1
  123. package/dist/chunk-F72WX4MH.js +0 -422
  124. package/dist/chunk-F72WX4MH.js.map +0 -1
  125. package/dist/chunk-GFARAJV2.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-OTLSUYQQ.js +0 -29
  129. package/dist/chunk-OTLSUYQQ.js.map +0 -1
  130. package/dist/chunk-Q75LKHDF.js +0 -56
  131. package/dist/chunk-Q75LKHDF.js.map +0 -1
  132. package/dist/chunk-QDCJLJUV.js.map +0 -1
  133. package/dist/chunk-RDHSHEZM.js +0 -479
  134. package/dist/chunk-RDHSHEZM.js.map +0 -1
  135. package/dist/chunk-UIG7MWS2.js +0 -518
  136. package/dist/chunk-UIG7MWS2.js.map +0 -1
  137. package/dist/chunk-UL6Q5HDP.js.map +0 -1
  138. package/dist/chunk-VMQHNJJI.js.map +0 -1
  139. package/dist/chunk-WWVD5DR5.js.map +0 -1
  140. package/dist/chunk-Z72F2ENM.js.map +0 -1
  141. package/dist/components-BWG1GkZA.d.ts +0 -20
  142. package/dist/use-behavior-B85d5u7-.d.ts +0 -13
  143. /package/dist/{chunk-5SM5SH5O.js.map → chunk-7JLWZXPA.js.map} +0 -0
  144. /package/dist/{chunk-ZA43LPRM.js.map → chunk-KIYUUSPK.js.map} +0 -0
  145. /package/dist/{chunk-XP3ASVGJ.js.map → chunk-NW4SE5XW.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/behaviors/use-behavior.ts","../src/lib/behaviors/components.ts"],"sourcesContent":["import { GameEntity } from '../entities/entity';\nimport { BehaviorDescriptor } from './behavior-descriptor';\n\n/**\n * Type-safe helper to apply a behavior to an entity and return the entity cast to the behavior's interface.\n * \n * @param entity The entity to apply the behavior to\n * @param descriptor The behavior descriptor\n * @param options Behavior options\n * @returns The entity, cast to E & I (where I is the behavior's interface)\n */\nexport function useBehavior<\n E extends GameEntity<any>,\n O extends Record<string, any>,\n H extends Record<string, any>,\n I // Entity Interface\n>(\n entity: E,\n descriptor: BehaviorDescriptor<O, H, I>,\n options?: Partial<O>\n): E & I {\n entity.use(descriptor, options);\n return entity as unknown as E & I;\n}\n","/**\n * Core behavior helper types\n *\n * Plain data interfaces shared by movement behaviors and coordinators.\n */\n\nimport type { RigidBody } from '@dimforge/rapier3d-compat';\nimport { Vector3, Quaternion } from 'three';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// TransformComponent (render-facing)\n// Written only by PhysicsSyncBehavior, read by rendering/animation\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface TransformComponent {\n\tposition: Vector3;\n\trotation: Quaternion;\n}\n\nexport function createTransformComponent(): TransformComponent {\n\treturn {\n\t\tposition: new Vector3(),\n\t\trotation: new Quaternion(),\n\t};\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// PhysicsBodyComponent (bridge to Rapier, not state)\n// ECS does not store velocity, mass, etc. - Rapier owns all physical truth\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface PhysicsBodyComponent {\n\tbody: RigidBody;\n}\n\nexport function createPhysicsBodyComponent(body: RigidBody): PhysicsBodyComponent {\n\treturn { body };\n}\n"],"mappings":";AAWO,SAAS,YAMZ,QACA,YACA,SACK;AACL,SAAO,IAAI,YAAY,OAAO;AAC9B,SAAO;AACX;;;AChBA,SAAS,SAAS,kBAAkB;AAY7B,SAAS,2BAA+C;AAC9D,SAAO;AAAA,IACN,UAAU,IAAI,QAAQ;AAAA,IACtB,UAAU,IAAI,WAAW;AAAA,EAC1B;AACD;AAWO,SAAS,2BAA2B,MAAuC;AACjF,SAAO,EAAE,KAAK;AACf;","names":[]}
1
+ {"version":3,"sources":["../src/lib/behaviors/use-behavior.ts","../src/lib/behaviors/components.ts"],"sourcesContent":["import { GameEntity } from '../entities/entity';\nimport { BehaviorDescriptor } from './behavior-descriptor';\n\n/**\n * Type-safe helper to apply a behavior to an entity and return the entity cast to the behavior's interface.\n * \n * @param entity The entity to apply the behavior to\n * @param descriptor The behavior descriptor\n * @param options Behavior options\n * @returns The entity, cast to E & I (where I is the behavior's interface)\n */\nexport function useBehavior<\n E extends GameEntity<any>,\n O extends Record<string, any>,\n H extends Record<string, any>,\n I // Entity Interface\n>(\n entity: E,\n descriptor: BehaviorDescriptor<O, H, I>,\n options?: Partial<O>\n): E & I {\n entity.use(descriptor, options);\n return entity as unknown as E & I;\n}\n","/**\n * Core behavior helper types\n *\n * Plain data interfaces shared by movement behaviors and coordinators.\n */\n\nimport { Vector3, Quaternion } from 'three';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// TransformComponent (render-facing)\n// Written only by PhysicsSyncBehavior, read by rendering/animation\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface TransformComponent {\n\tposition: Vector3;\n\trotation: Quaternion;\n}\n\nexport function createTransformComponent(): TransformComponent {\n\treturn {\n\t\tposition: new Vector3(),\n\t\trotation: new Quaternion(),\n\t};\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// PhysicsBodyComponent (bridge to the host physics reference, not state)\n// ECS does not store velocity, mass, etc. — the wasm runtime owns all\n// physical truth; `body` is an opaque host-owned reference.\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface PhysicsBodyComponent {\n\tbody: unknown;\n}\n\nexport function createPhysicsBodyComponent(body: unknown): PhysicsBodyComponent {\n\treturn { body };\n}\n"],"mappings":";AAWO,SAAS,YAMZ,QACA,YACA,SACK;AACL,SAAO,IAAI,YAAY,OAAO;AAC9B,SAAO;AACX;;;ACjBA,SAAS,SAAS,kBAAkB;AAY7B,SAAS,2BAA+C;AAC9D,SAAO;AAAA,IACN,UAAU,IAAI,QAAQ;AAAA,IACtB,UAAU,IAAI,WAAW;AAAA,EAC1B;AACD;AAYO,SAAS,2BAA2B,MAAqC;AAC/E,SAAO,EAAE,KAAK;AACf;","names":[]}
@@ -4,46 +4,19 @@ import {
4
4
  createTopDownMovementStateComponent
5
5
  } from "./chunk-EIWQM6U3.js";
6
6
  import {
7
- angleFromDirection2D,
8
- normalizeDirection2DInto
9
- } from "./chunk-Y7YIAWPW.js";
7
+ resolveAim2D
8
+ } from "./chunk-TBUKJKY6.js";
10
9
  import {
11
10
  StageTopDownPlane
12
- } from "./chunk-Q75LKHDF.js";
11
+ } from "./chunk-E4X4YJZB.js";
13
12
  import {
14
13
  defineBehavior
15
14
  } from "./chunk-EZNCCO4L.js";
16
15
 
17
- // src/lib/behaviors/top-down-movement/top-down-movement.system.ts
18
- var _moveDirection = { x: 0, y: 0 };
19
- var _facingDirection = { x: 0, y: 0 };
20
- var TopDownMovementSystem = class {
21
- updateEntity(entity, _delta) {
22
- if (!entity.body || !entity.transformStore || !entity.topDownMovement || !entity.$topDownMovement || !entity.topDownMovementState) {
23
- return;
24
- }
25
- const movement = entity.topDownMovement;
26
- const input = entity.$topDownMovement;
27
- const state = entity.topDownMovementState;
28
- const isMoving = normalizeDirection2DInto(input.moveX, input.moveY, _moveDirection);
29
- const velocity = entity.body.linvel();
30
- state.moving = isMoving;
31
- entity.transformStore.velocity.x = isMoving ? _moveDirection.x * movement.moveSpeed : 0;
32
- entity.transformStore.velocity.y = isMoving ? _moveDirection.y * movement.moveSpeed : 0;
33
- entity.transformStore.velocity.z = velocity.z;
34
- entity.transformStore.dirty.velocityX = true;
35
- entity.transformStore.dirty.velocityY = true;
36
- entity.transformStore.dirty.velocityZ = true;
37
- if (normalizeDirection2DInto(input.faceX, input.faceY, _facingDirection)) {
38
- state.facingAngle = angleFromDirection2D(_facingDirection);
39
- }
40
- entity.setRotationZ?.(state.facingAngle);
41
- }
42
- };
43
-
44
16
  // src/lib/behaviors/top-down-movement/top-down-movement.descriptor.ts
45
17
  var defaultOptions = {
46
- moveSpeed: 8
18
+ moveSpeed: 8,
19
+ faceMovement: false
47
20
  };
48
21
  var TOP_DOWN_MOVEMENT_BEHAVIOR_KEY = /* @__PURE__ */ Symbol.for(
49
22
  "zylem:behavior:top-down-movement"
@@ -60,9 +33,8 @@ var TopDownMovementBehaviorSystem = class {
60
33
  }
61
34
  wasmStage;
62
35
  getBehaviorLinks;
63
- behavior = new TopDownMovementSystem();
64
36
  attachedRuntimeSlots = /* @__PURE__ */ new Set();
65
- update(_ecs, delta) {
37
+ update(_ecs, _delta) {
66
38
  const links = this.getBehaviorLinks?.(TOP_DOWN_MOVEMENT_BEHAVIOR_KEY);
67
39
  if (!links) return;
68
40
  for (const link of links) {
@@ -79,16 +51,25 @@ var TopDownMovementBehaviorSystem = class {
79
51
  }
80
52
  ref.__entity = entity;
81
53
  const handle = entity.runtimeHandle ?? -1;
82
- if (this.wasmStage && handle >= 0) {
83
- this.ensureWasmAttached(handle, ref.options);
84
- const input = entity.$topDownMovement;
85
- if (input) {
86
- this.wasmStage.writeInputAxes(handle, input.moveX ?? 0, input.moveY ?? input.moveZ ?? 0);
54
+ if (!this.wasmStage || handle < 0) continue;
55
+ this.ensureWasmAttached(handle, ref.options);
56
+ const input = entity.$topDownMovement;
57
+ const state = entity.topDownMovementState;
58
+ if (input) {
59
+ const moveX = input.moveX ?? 0;
60
+ const moveY = input.moveY ?? input.moveZ ?? 0;
61
+ this.wasmStage.writeInputAxes(handle, moveX, moveY);
62
+ state.moving = Math.hypot(moveX, moveY) > 1e-3;
63
+ const aim = resolveAim2D(
64
+ input.faceX ?? 0,
65
+ input.faceY ?? 0,
66
+ state.facingAngle
67
+ );
68
+ if (aim.hasAimInput) {
69
+ state.facingAngle = aim.angle;
70
+ entity.setRotationZ?.(aim.angle);
87
71
  }
88
- continue;
89
72
  }
90
- if (!entity.body || !entity.transformStore) continue;
91
- this.behavior.updateEntity(entity, delta);
92
73
  }
93
74
  }
94
75
  ensureWasmAttached(handle, options) {
@@ -96,7 +77,7 @@ var TopDownMovementBehaviorSystem = class {
96
77
  this.wasmStage.attachTopDown(handle, {
97
78
  plane: StageTopDownPlane.Xy,
98
79
  speed: options.moveSpeed ?? defaultOptions.moveSpeed,
99
- faceMovement: true
80
+ faceMovement: options.faceMovement ?? defaultOptions.faceMovement
100
81
  });
101
82
  this.attachedRuntimeSlots.add(handle);
102
83
  }
@@ -112,7 +93,6 @@ var TopDownMovementBehavior = defineBehavior({
112
93
  });
113
94
 
114
95
  export {
115
- TopDownMovementSystem,
116
96
  TopDownMovementBehavior
117
97
  };
118
- //# sourceMappingURL=chunk-A4CA35DQ.js.map
98
+ //# sourceMappingURL=chunk-5EBIIYZD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/behaviors/top-down-movement/top-down-movement.descriptor.ts"],"sourcesContent":["import { defineBehavior, type BehaviorRef } from '../behavior-descriptor';\nimport type { BehaviorEntityLink, BehaviorSystem } from '../behavior-system';\nimport {\n\tcreateTopDownMovementComponent,\n\tcreateTopDownMovementInputComponent,\n\tcreateTopDownMovementStateComponent,\n} from './components';\nimport type {\n\tTopDownMovementComponent,\n\tTopDownMovementInputComponent,\n\tTopDownMovementStateComponent,\n} from './components';\nimport {\n\tStageTopDownPlane,\n\ttype BehaviorRuntime,\n} from '../../runtime/wasm-stage-runtime';\nimport { resolveAim2D } from '../../coordinators/shared/top-down-aim';\n\n/**\n * Entity with top-down movement components (input written by the game,\n * simulation owned by the wasm runtime).\n */\nexport interface TopDownMovementEntity {\n\ttopDownMovement: TopDownMovementComponent;\n\t$topDownMovement: TopDownMovementInputComponent;\n\ttopDownMovementState: TopDownMovementStateComponent;\n}\n\nexport interface TopDownMovementBehaviorOptions {\n\tmoveSpeed?: number;\n\t/** When true, WASM rotates toward velocity; default false for twin-stick face axes. */\n\tfaceMovement?: boolean;\n}\n\nexport interface TopDownMovementHandle {\n\tgetFacingAngle(): number;\n}\n\nconst defaultOptions: TopDownMovementBehaviorOptions = {\n\tmoveSpeed: 8,\n\tfaceMovement: false,\n};\n\nconst TOP_DOWN_MOVEMENT_BEHAVIOR_KEY = Symbol.for(\n\t'zylem:behavior:top-down-movement',\n);\n\nfunction createTopDownMovementHandle(\n\tref: BehaviorRef<TopDownMovementBehaviorOptions>,\n): TopDownMovementHandle {\n\treturn {\n\t\tgetFacingAngle: () =>\n\t\t\t(ref as any).__entity?.topDownMovementState?.facingAngle ?? 0,\n\t};\n}\n\nclass TopDownMovementBehaviorSystem implements BehaviorSystem {\n\tprivate attachedRuntimeSlots = new Set<number>();\n\n\tconstructor(\n\t\tprivate wasmStage: BehaviorRuntime | null,\n\t\tprivate getBehaviorLinks?: (key: symbol) => Iterable<BehaviorEntityLink>,\n\t) {}\n\n\tupdate(_ecs: unknown, _delta: number): void {\n\t\tconst links = this.getBehaviorLinks?.(TOP_DOWN_MOVEMENT_BEHAVIOR_KEY);\n\t\tif (!links) return;\n\n\t\tfor (const link of links) {\n\t\t\tconst entity = link.entity as any;\n\t\t\tconst ref = link.ref as any;\n\n\t\t\tif (!entity.topDownMovement) {\n\t\t\t\tentity.topDownMovement = createTopDownMovementComponent(ref.options);\n\t\t\t}\n\t\t\tif (!entity.$topDownMovement) {\n\t\t\t\tentity.$topDownMovement = createTopDownMovementInputComponent();\n\t\t\t}\n\t\t\tif (!entity.topDownMovementState) {\n\t\t\t\tentity.topDownMovementState = createTopDownMovementStateComponent();\n\t\t\t}\n\n\t\t\tref.__entity = entity;\n\n\t\t\tconst handle = (entity.runtimeHandle ?? -1) as number;\n\t\t\tif (!this.wasmStage || handle < 0) continue;\n\n\t\t\tthis.ensureWasmAttached(handle, ref.options as TopDownMovementBehaviorOptions);\n\t\t\tconst input = entity.$topDownMovement;\n\t\t\tconst state = entity.topDownMovementState;\n\t\t\tif (input) {\n\t\t\t\tconst moveX = input.moveX ?? 0;\n\t\t\t\tconst moveY = input.moveY ?? input.moveZ ?? 0;\n\t\t\t\tthis.wasmStage.writeInputAxes(handle, moveX, moveY);\n\t\t\t\tstate.moving = Math.hypot(moveX, moveY) > 0.001;\n\n\t\t\t\tconst aim = resolveAim2D(\n\t\t\t\t\tinput.faceX ?? 0,\n\t\t\t\t\tinput.faceY ?? 0,\n\t\t\t\t\tstate.facingAngle,\n\t\t\t\t);\n\t\t\t\tif (aim.hasAimInput) {\n\t\t\t\t\tstate.facingAngle = aim.angle;\n\t\t\t\t\tentity.setRotationZ?.(aim.angle);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate ensureWasmAttached(handle: number, options: TopDownMovementBehaviorOptions): void {\n\t\tif (!this.wasmStage || this.attachedRuntimeSlots.has(handle)) return;\n\t\tthis.wasmStage.attachTopDown(handle, {\n\t\t\tplane: StageTopDownPlane.Xy,\n\t\t\tspeed: options.moveSpeed ?? defaultOptions.moveSpeed!,\n\t\t\tfaceMovement: options.faceMovement ?? defaultOptions.faceMovement!,\n\t\t});\n\t\tthis.attachedRuntimeSlots.add(handle);\n\t}\n\n\tdestroy(_ecs: unknown): void {\n\t\tthis.attachedRuntimeSlots.clear();\n\t}\n}\n\nexport const TopDownMovementBehavior = defineBehavior<\n\tTopDownMovementBehaviorOptions,\n\tTopDownMovementHandle,\n\tTopDownMovementEntity\n>({\n\tname: 'top-down-movement',\n\tdefaultOptions,\n\tsystemFactory: (ctx) =>\n\t\tnew TopDownMovementBehaviorSystem(ctx.wasmStage ?? null, ctx.getBehaviorLinks),\n\tcreateHandle: createTopDownMovementHandle,\n});\n"],"mappings":";;;;;;;;;;;;;;;;AAsCA,IAAM,iBAAiD;AAAA,EACtD,WAAW;AAAA,EACX,cAAc;AACf;AAEA,IAAM,iCAAiC,uBAAO;AAAA,EAC7C;AACD;AAEA,SAAS,4BACR,KACwB;AACxB,SAAO;AAAA,IACN,gBAAgB,MACd,IAAY,UAAU,sBAAsB,eAAe;AAAA,EAC9D;AACD;AAEA,IAAM,gCAAN,MAA8D;AAAA,EAG7D,YACS,WACA,kBACP;AAFO;AACA;AAAA,EACN;AAAA,EAFM;AAAA,EACA;AAAA,EAJD,uBAAuB,oBAAI,IAAY;AAAA,EAO/C,OAAO,MAAe,QAAsB;AAC3C,UAAM,QAAQ,KAAK,mBAAmB,8BAA8B;AACpE,QAAI,CAAC,MAAO;AAEZ,eAAW,QAAQ,OAAO;AACzB,YAAM,SAAS,KAAK;AACpB,YAAM,MAAM,KAAK;AAEjB,UAAI,CAAC,OAAO,iBAAiB;AAC5B,eAAO,kBAAkB,+BAA+B,IAAI,OAAO;AAAA,MACpE;AACA,UAAI,CAAC,OAAO,kBAAkB;AAC7B,eAAO,mBAAmB,oCAAoC;AAAA,MAC/D;AACA,UAAI,CAAC,OAAO,sBAAsB;AACjC,eAAO,uBAAuB,oCAAoC;AAAA,MACnE;AAEA,UAAI,WAAW;AAEf,YAAM,SAAU,OAAO,iBAAiB;AACxC,UAAI,CAAC,KAAK,aAAa,SAAS,EAAG;AAEnC,WAAK,mBAAmB,QAAQ,IAAI,OAAyC;AAC7E,YAAM,QAAQ,OAAO;AACrB,YAAM,QAAQ,OAAO;AACrB,UAAI,OAAO;AACV,cAAM,QAAQ,MAAM,SAAS;AAC7B,cAAM,QAAQ,MAAM,SAAS,MAAM,SAAS;AAC5C,aAAK,UAAU,eAAe,QAAQ,OAAO,KAAK;AAClD,cAAM,SAAS,KAAK,MAAM,OAAO,KAAK,IAAI;AAE1C,cAAM,MAAM;AAAA,UACX,MAAM,SAAS;AAAA,UACf,MAAM,SAAS;AAAA,UACf,MAAM;AAAA,QACP;AACA,YAAI,IAAI,aAAa;AACpB,gBAAM,cAAc,IAAI;AACxB,iBAAO,eAAe,IAAI,KAAK;AAAA,QAChC;AAAA,MACD;AAAA,IACD;AAAA,EACD;AAAA,EAEQ,mBAAmB,QAAgB,SAA+C;AACzF,QAAI,CAAC,KAAK,aAAa,KAAK,qBAAqB,IAAI,MAAM,EAAG;AAC9D,SAAK,UAAU,cAAc,QAAQ;AAAA,MACpC,OAAO,kBAAkB;AAAA,MACzB,OAAO,QAAQ,aAAa,eAAe;AAAA,MAC3C,cAAc,QAAQ,gBAAgB,eAAe;AAAA,IACtD,CAAC;AACD,SAAK,qBAAqB,IAAI,MAAM;AAAA,EACrC;AAAA,EAEA,QAAQ,MAAqB;AAC5B,SAAK,qBAAqB,MAAM;AAAA,EACjC;AACD;AAEO,IAAM,0BAA0B,eAIrC;AAAA,EACD,MAAM;AAAA,EACN;AAAA,EACA,eAAe,CAAC,QACf,IAAI,8BAA8B,IAAI,aAAa,MAAM,IAAI,gBAAgB;AAAA,EAC9E,cAAc;AACf,CAAC;","names":[]}
@@ -6,18 +6,15 @@ import {
6
6
  import {
7
7
  VEC3_ZERO,
8
8
  toThreeVector3
9
- } from "./chunk-WWVD5DR5.js";
10
- import {
11
- setVelocityIntent
12
- } from "./chunk-OTLSUYQQ.js";
9
+ } from "./chunk-M3QDKKDF.js";
13
10
  import {
14
11
  SyncStateMachine,
15
- t
16
- } from "./chunk-4URF65BR.js";
12
+ import_typescript_fsm
13
+ } from "./chunk-AS3HR7AS.js";
17
14
  import {
18
15
  STAGE_INPUT_BUTTON_RUN,
19
16
  readBehaviorSnapshot
20
- } from "./chunk-Q75LKHDF.js";
17
+ } from "./chunk-E4X4YJZB.js";
21
18
  import {
22
19
  defineBehavior
23
20
  } from "./chunk-EZNCCO4L.js";
@@ -42,17 +39,17 @@ var FirstPersonFSM = class {
42
39
  "idle" /* Idle */,
43
40
  [
44
41
  // Idle transitions
45
- t("idle" /* Idle */, "walk" /* Walk */, "walking" /* Walking */),
46
- t("idle" /* Idle */, "run" /* Run */, "running" /* Running */),
47
- t("idle" /* Idle */, "stop" /* Stop */, "idle" /* Idle */),
42
+ (0, import_typescript_fsm.t)("idle" /* Idle */, "walk" /* Walk */, "walking" /* Walking */),
43
+ (0, import_typescript_fsm.t)("idle" /* Idle */, "run" /* Run */, "running" /* Running */),
44
+ (0, import_typescript_fsm.t)("idle" /* Idle */, "stop" /* Stop */, "idle" /* Idle */),
48
45
  // Walking transitions
49
- t("walking" /* Walking */, "run" /* Run */, "running" /* Running */),
50
- t("walking" /* Walking */, "stop" /* Stop */, "idle" /* Idle */),
51
- t("walking" /* Walking */, "walk" /* Walk */, "walking" /* Walking */),
46
+ (0, import_typescript_fsm.t)("walking" /* Walking */, "run" /* Run */, "running" /* Running */),
47
+ (0, import_typescript_fsm.t)("walking" /* Walking */, "stop" /* Stop */, "idle" /* Idle */),
48
+ (0, import_typescript_fsm.t)("walking" /* Walking */, "walk" /* Walk */, "walking" /* Walking */),
52
49
  // Running transitions
53
- t("running" /* Running */, "walk" /* Walk */, "walking" /* Walking */),
54
- t("running" /* Running */, "stop" /* Stop */, "idle" /* Idle */),
55
- t("running" /* Running */, "run" /* Run */, "running" /* Running */)
50
+ (0, import_typescript_fsm.t)("running" /* Running */, "walk" /* Walk */, "walking" /* Walking */),
51
+ (0, import_typescript_fsm.t)("running" /* Running */, "stop" /* Stop */, "idle" /* Idle */),
52
+ (0, import_typescript_fsm.t)("running" /* Running */, "run" /* Run */, "running" /* Running */)
56
53
  ]
57
54
  );
58
55
  }
@@ -97,20 +94,15 @@ var FirstPersonFSM = class {
97
94
 
98
95
  // src/lib/behaviors/first-person/first-person.system.ts
99
96
  import { Vector3, Quaternion, Euler } from "three";
100
- var _forward = new Vector3();
101
- var _right = new Vector3();
102
- var _up = new Vector3(0, 1, 0);
103
97
  var _viewmodelQuat = new Quaternion();
104
98
  var _viewmodelLocalQuat = new Quaternion();
105
99
  var _viewmodelEuler = new Euler();
106
100
  var _offset = new Vector3();
107
- var FirstPersonSystem = class {
108
- world;
101
+ var _pose = { position: [0, 0, 0], rotation: [0, 0, 0, 1] };
102
+ var _snapshot = {};
103
+ var FirstPersonPresentation = class {
109
104
  perspectives = /* @__PURE__ */ new Map();
110
105
  viewmodels = /* @__PURE__ */ new Map();
111
- constructor(world) {
112
- this.world = world;
113
- }
114
106
  /**
115
107
  * Associate a FirstPersonPerspective with an entity (by uuid).
116
108
  * Called once by the descriptor system when the behavior is initialized.
@@ -132,78 +124,36 @@ var FirstPersonSystem = class {
132
124
  return this.viewmodels.has(entityUuid);
133
125
  }
134
126
  /**
135
- * Update one first-person entity.
127
+ * Sync camera + viewmodel for one entity from the runtime state.
136
128
  */
137
- updateEntity(entity, _delta) {
138
- const fpEntity = entity;
139
- if (!fpEntity.firstPerson || !fpEntity.$fps || !fpEntity.firstPersonState) {
140
- return;
129
+ syncEntity(entityUuid, runtime, handle, movement, state) {
130
+ const perspective = this.perspectives.get(entityUuid);
131
+ const pose = runtime.getPose(handle, _pose);
132
+ const snapshot = runtime.readSnapshot(handle, _snapshot);
133
+ if (perspective && snapshot) {
134
+ perspective.setLook?.(snapshot.yaw, snapshot.pitch);
135
+ if (state) {
136
+ state.yaw = snapshot.yaw;
137
+ state.pitch = snapshot.pitch;
138
+ state.currentSpeed = snapshot.speed;
139
+ }
141
140
  }
142
- const input = fpEntity.$fps;
143
- const movement = fpEntity.firstPerson;
144
- const state = fpEntity.firstPersonState;
145
- const perspective = this.perspectives.get(fpEntity.uuid);
146
- if (perspective?.initialPosition && fpEntity.body) {
147
- const pos = fpEntity.body.translation();
141
+ if (perspective?.initialPosition && pose) {
148
142
  perspective.initialPosition.set(
149
- pos.x,
150
- pos.y + movement.eyeHeight,
151
- pos.z
152
- );
153
- }
154
- if (perspective) {
155
- perspective.look(
156
- -input.lookX * movement.lookSensitivity,
157
- -input.lookY * movement.lookSensitivity
143
+ pose.position[0],
144
+ pose.position[1] + movement.eyeHeight,
145
+ pose.position[2]
158
146
  );
159
- state.yaw = perspective.yaw;
160
- state.pitch = perspective.pitch;
161
- }
162
- const speed = input.sprint ? movement.runSpeed : movement.walkSpeed;
163
- state.currentSpeed = speed;
164
- const hasMovement = Math.abs(input.moveX) > 0.1 || Math.abs(input.moveZ) > 0.1;
165
- if (fpEntity.transformStore) {
166
- if (hasMovement) {
167
- _forward.set(0, 0, -1).applyAxisAngle(_up, state.yaw);
168
- _right.set(1, 0, 0).applyAxisAngle(_up, state.yaw);
169
- const vx = _right.x * input.moveX * speed + _forward.x * -input.moveZ * speed;
170
- const vz = _right.z * input.moveX * speed + _forward.z * -input.moveZ * speed;
171
- setVelocityIntent(
172
- fpEntity.transformStore,
173
- "first-person",
174
- { x: vx, z: vz },
175
- { mode: "replace", priority: 10 }
176
- );
177
- } else {
178
- setVelocityIntent(
179
- fpEntity.transformStore,
180
- "first-person",
181
- { x: 0, z: 0 },
182
- { mode: "replace", priority: 10 }
183
- );
184
- }
185
- }
186
- const vm = this.viewmodels.get(fpEntity.uuid);
187
- if (vm && perspective?.initialPosition) {
188
- this.positionViewmodel(vm, perspective, state);
189
147
  }
190
- }
191
- /**
192
- * Update all first-person entities.
193
- */
194
- update(delta) {
195
- if (!this.world?.collisionMap) return;
196
- for (const [, entity] of this.world.collisionMap) {
197
- this.updateEntity(entity, delta);
148
+ const vm = this.viewmodels.get(entityUuid);
149
+ if (vm && perspective?.initialPosition && state) {
150
+ this.positionViewmodel(vm, perspective, state, runtime);
198
151
  }
199
152
  }
200
153
  /**
201
154
  * Position a viewmodel entity relative to the camera's eye position and rotation.
202
- *
203
- * The physics transform system overwrites group.position from the rigid body
204
- * each frame, so we must also move the body to keep them in sync.
205
155
  */
206
- positionViewmodel(vm, perspective, state) {
156
+ positionViewmodel(vm, perspective, state, runtime) {
207
157
  const eyePos = perspective.initialPosition;
208
158
  if (!eyePos) return;
209
159
  _viewmodelQuat.setFromEuler(
@@ -218,11 +168,15 @@ var FirstPersonSystem = class {
218
168
  const wy = eyePos.y + _offset.y;
219
169
  const wz = eyePos.z + _offset.z;
220
170
  const entity = vm.entity;
221
- if (entity?.body) {
222
- entity.body.setTranslation({ x: wx, y: wy, z: wz }, true);
223
- entity.body.setRotation(
224
- { x: _viewmodelQuat.x, y: _viewmodelQuat.y, z: _viewmodelQuat.z, w: _viewmodelQuat.w },
225
- true
171
+ const handle = entity?.runtimeHandle ?? -1;
172
+ if (handle >= 0) {
173
+ runtime.setPosition(handle, wx, wy, wz);
174
+ runtime.setRotation(
175
+ handle,
176
+ _viewmodelQuat.x,
177
+ _viewmodelQuat.y,
178
+ _viewmodelQuat.z,
179
+ _viewmodelQuat.w
226
180
  );
227
181
  }
228
182
  if (entity?.group) {
@@ -248,35 +202,21 @@ var FIRST_PERSON_BEHAVIOR_KEY = /* @__PURE__ */ Symbol.for(
248
202
  "zylem:behavior:first-person-controller"
249
203
  );
250
204
  var FirstPersonBehaviorSystem = class {
251
- constructor(world, wasmStage, getBehaviorLinks) {
205
+ constructor(wasmStage, getBehaviorLinks) {
252
206
  this.wasmStage = wasmStage;
253
207
  this.getBehaviorLinks = getBehaviorLinks;
254
- this.behavior = new FirstPersonSystem(world);
255
208
  }
256
209
  wasmStage;
257
210
  getBehaviorLinks;
258
- behavior;
211
+ presentation = new FirstPersonPresentation();
259
212
  attachedRuntimeSlots = /* @__PURE__ */ new Set();
260
- update(_ecs, delta) {
213
+ update(_ecs, _delta) {
261
214
  const links = this.getBehaviorLinks?.(FIRST_PERSON_BEHAVIOR_KEY);
262
215
  if (!links) return;
263
216
  for (const link of links) {
264
217
  const gameEntity = link.entity;
265
218
  const fpsRef = link.ref;
266
219
  const options = fpsRef.options;
267
- const handle = gameEntity.runtimeHandle ?? -1;
268
- if (this.wasmStage && handle >= 0) {
269
- this.ensureWasmAttached(handle, options);
270
- fpsRef.__wasmStage = this.wasmStage;
271
- fpsRef.__entity = gameEntity;
272
- const input = gameEntity.$fps;
273
- if (input) {
274
- this.wasmStage.writeInputAxes(handle, input.moveX ?? 0, input.moveZ ?? 0);
275
- this.wasmStage.writeInputLook(handle, input.lookX ?? 0, input.lookY ?? 0);
276
- this.wasmStage.writeInputButton(handle, STAGE_INPUT_BUTTON_RUN, !!input.sprint);
277
- }
278
- continue;
279
- }
280
220
  if (!gameEntity.firstPerson) {
281
221
  gameEntity.firstPerson = createFirstPersonMovementComponent(options);
282
222
  }
@@ -286,25 +226,33 @@ var FirstPersonBehaviorSystem = class {
286
226
  if (!gameEntity.firstPersonState) {
287
227
  gameEntity.firstPersonState = createFirstPersonStateComponent();
288
228
  }
289
- if (options.perspective && !this.behavior.hasPerspective(gameEntity.uuid)) {
290
- this.behavior.setPerspective(gameEntity.uuid, options.perspective);
229
+ if (options.perspective && !this.presentation.hasPerspective(gameEntity.uuid)) {
230
+ this.presentation.setPerspective(gameEntity.uuid, options.perspective);
291
231
  }
292
- if (options.viewmodel && !this.behavior.hasViewmodel(gameEntity.uuid)) {
293
- this.behavior.setViewmodel(gameEntity.uuid, {
232
+ if (options.viewmodel && !this.presentation.hasViewmodel(gameEntity.uuid)) {
233
+ this.presentation.setViewmodel(gameEntity.uuid, {
294
234
  ...options.viewmodel,
295
235
  offset: toThreeVector3(options.viewmodel.offset, VEC3_ZERO)
296
236
  });
297
237
  }
298
- if (!fpsRef.fsm && gameEntity.$fps && gameEntity.firstPersonState) {
299
- fpsRef.fsm = new FirstPersonFSM({
300
- input: gameEntity.$fps,
301
- state: gameEntity.firstPersonState
302
- });
303
- }
304
- if (fpsRef.fsm && gameEntity.$fps && gameEntity.firstPersonState) {
305
- fpsRef.fsm.update(gameEntity.$fps, gameEntity.firstPersonState);
238
+ const handle = gameEntity.runtimeHandle ?? -1;
239
+ if (!this.wasmStage || handle < 0) continue;
240
+ this.ensureWasmAttached(handle, options);
241
+ fpsRef.__wasmStage = this.wasmStage;
242
+ fpsRef.__entity = gameEntity;
243
+ const input = gameEntity.$fps;
244
+ if (input) {
245
+ this.wasmStage.writeInputAxes(handle, input.moveX ?? 0, input.moveZ ?? 0);
246
+ this.wasmStage.writeInputLook(handle, input.lookX ?? 0, input.lookY ?? 0);
247
+ this.wasmStage.writeInputButton(handle, STAGE_INPUT_BUTTON_RUN, !!input.sprint);
306
248
  }
307
- this.behavior.updateEntity(gameEntity, delta);
249
+ this.presentation.syncEntity(
250
+ gameEntity.uuid,
251
+ this.wasmStage,
252
+ handle,
253
+ gameEntity.firstPerson,
254
+ gameEntity.firstPersonState
255
+ );
308
256
  }
309
257
  }
310
258
  ensureWasmAttached(handle, options) {
@@ -318,28 +266,26 @@ var FirstPersonBehaviorSystem = class {
318
266
  this.attachedRuntimeSlots.add(handle);
319
267
  }
320
268
  destroy(_ecs) {
321
- this.behavior.destroy();
269
+ this.presentation.destroy();
322
270
  this.attachedRuntimeSlots.clear();
323
271
  }
324
272
  };
325
273
  var FirstPersonBehavior = defineBehavior({
326
274
  name: "first-person-controller",
327
275
  defaultOptions,
328
- systemFactory: (ctx) => new FirstPersonBehaviorSystem(ctx.world, ctx.wasmStage ?? null, ctx.getBehaviorLinks),
329
- // On the wasm fast path the handle reads the runtime snapshot buffer
330
- // (previous-step values); the TS FSM only backs the no-wasm fallback.
276
+ systemFactory: (ctx) => new FirstPersonBehaviorSystem(ctx.wasmStage ?? null, ctx.getBehaviorLinks),
277
+ // Handles read the runtime snapshot buffer (previous-step values). The
278
+ // Rust behavior is the single source of truth.
331
279
  createHandle: (ref) => ({
332
280
  getState: () => {
333
281
  const snapshot = readBehaviorSnapshot(ref);
334
- if (snapshot) {
335
- if (snapshot.speed <= 0.01) return "idle" /* Idle */;
336
- const walkSpeed = ref.options.walkSpeed ?? defaultOptions.walkSpeed;
337
- return snapshot.speed > walkSpeed ? "running" /* Running */ : "walking" /* Walking */;
338
- }
339
- return ref.fsm?.getState() ?? "idle" /* Idle */;
282
+ if (!snapshot) return "idle" /* Idle */;
283
+ if (snapshot.speed <= 0.01) return "idle" /* Idle */;
284
+ const walkSpeed = ref.options.walkSpeed ?? defaultOptions.walkSpeed;
285
+ return snapshot.speed > walkSpeed ? "running" /* Running */ : "walking" /* Walking */;
340
286
  },
341
- getYaw: () => readBehaviorSnapshot(ref)?.yaw ?? ref.fsm?.getYaw() ?? 0,
342
- getPitch: () => readBehaviorSnapshot(ref)?.pitch ?? ref.fsm?.getPitch() ?? 0,
287
+ getYaw: () => readBehaviorSnapshot(ref)?.yaw ?? 0,
288
+ getPitch: () => readBehaviorSnapshot(ref)?.pitch ?? 0,
343
289
  attachViewmodel: (entity, offset) => {
344
290
  if (ref.options) {
345
291
  ref.options.viewmodel = { entity, offset };
@@ -352,7 +298,7 @@ export {
352
298
  FirstPersonState,
353
299
  FirstPersonEvent,
354
300
  FirstPersonFSM,
355
- FirstPersonSystem,
301
+ FirstPersonPresentation,
356
302
  FirstPersonBehavior
357
303
  };
358
- //# sourceMappingURL=chunk-4L36OPY2.js.map
304
+ //# sourceMappingURL=chunk-5NJGASUJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/behaviors/first-person/first-person-fsm.ts","../src/lib/behaviors/first-person/first-person.system.ts","../src/lib/behaviors/first-person/first-person.descriptor.ts"],"sourcesContent":["/**\n * First Person Controller Finite State Machine\n *\n * Manages state transitions for first-person movement:\n * - Idle: Standing still\n * - Walking: Moving at walk speed\n * - Running: Moving at run/sprint speed\n */\n\nimport { SyncStateMachine, t } from '../../core/utility/sync-state-machine';\nimport type { FirstPersonInputComponent, FirstPersonStateComponent } from './components';\n\n/**\n * First-person movement states\n */\nexport enum FirstPersonState {\n\tIdle = 'idle',\n\tWalking = 'walking',\n\tRunning = 'running',\n}\n\n/**\n * Events that trigger state transitions\n */\nexport enum FirstPersonEvent {\n\tWalk = 'walk',\n\tRun = 'run',\n\tStop = 'stop',\n}\n\n/**\n * Context for the FSM\n */\nexport interface FirstPersonContext {\n\tinput: FirstPersonInputComponent;\n\tstate: FirstPersonStateComponent;\n}\n\n/**\n * First Person Controller FSM\n */\nexport class FirstPersonFSM {\n\tmachine: SyncStateMachine<FirstPersonState, FirstPersonEvent, never>;\n\n\tconstructor(private ctx: FirstPersonContext) {\n\t\tthis.machine = new SyncStateMachine<FirstPersonState, FirstPersonEvent, never>(\n\t\t\tFirstPersonState.Idle,\n\t\t\t[\n\t\t\t\t// Idle transitions\n\t\t\t\tt(FirstPersonState.Idle, FirstPersonEvent.Walk, FirstPersonState.Walking),\n\t\t\t\tt(FirstPersonState.Idle, FirstPersonEvent.Run, FirstPersonState.Running),\n\t\t\t\tt(FirstPersonState.Idle, FirstPersonEvent.Stop, FirstPersonState.Idle),\n\n\t\t\t\t// Walking transitions\n\t\t\t\tt(FirstPersonState.Walking, FirstPersonEvent.Run, FirstPersonState.Running),\n\t\t\t\tt(FirstPersonState.Walking, FirstPersonEvent.Stop, FirstPersonState.Idle),\n\t\t\t\tt(FirstPersonState.Walking, FirstPersonEvent.Walk, FirstPersonState.Walking),\n\n\t\t\t\t// Running transitions\n\t\t\t\tt(FirstPersonState.Running, FirstPersonEvent.Walk, FirstPersonState.Walking),\n\t\t\t\tt(FirstPersonState.Running, FirstPersonEvent.Stop, FirstPersonState.Idle),\n\t\t\t\tt(FirstPersonState.Running, FirstPersonEvent.Run, FirstPersonState.Running),\n\t\t\t],\n\t\t);\n\t}\n\n\t/** Get the current state */\n\tgetState(): FirstPersonState {\n\t\treturn this.machine.getState();\n\t}\n\n\t/** Dispatch an event to the FSM */\n\tdispatch(event: FirstPersonEvent): void {\n\t\tif (this.machine.can(event)) {\n\t\t\tthis.machine.syncDispatch(event);\n\t\t}\n\t}\n\n\t/** Get the current yaw from context */\n\tgetYaw(): number {\n\t\treturn this.ctx.state.yaw;\n\t}\n\n\t/** Get the current pitch from context */\n\tgetPitch(): number {\n\t\treturn this.ctx.state.pitch;\n\t}\n\n\t/**\n\t * Update FSM based on current input and state.\n\t */\n\tupdate(input: FirstPersonInputComponent, state: FirstPersonStateComponent): void {\n\t\tthis.ctx.input = input;\n\t\tthis.ctx.state = state;\n\n\t\tconst hasMovement = Math.abs(input.moveX) > 0.1 || Math.abs(input.moveZ) > 0.1;\n\n\t\tif (hasMovement) {\n\t\t\tif (input.sprint) {\n\t\t\t\tthis.dispatch(FirstPersonEvent.Run);\n\t\t\t} else {\n\t\t\t\tthis.dispatch(FirstPersonEvent.Walk);\n\t\t\t}\n\t\t} else {\n\t\t\tthis.dispatch(FirstPersonEvent.Stop);\n\t\t}\n\t}\n}\n","/**\n * First Person Presentation System\n *\n * Presentation-only companion to the wasm `first_person` Stage behavior:\n * - Syncs the FirstPersonPerspective eye position from the runtime pose\n * - Applies the runtime's yaw/pitch (from the behavior snapshot) to the\n * perspective so the camera matches the simulated look state\n * - Positions an optional viewmodel entity relative to the camera\n *\n * All movement/look simulation happens in the wasm runtime; this system only\n * reads poses and snapshots.\n */\n\nimport { Vector3, Quaternion, Euler } from 'three';\nimport type { FirstPersonPerspective } from '../../camera/perspectives/first-person-perspective';\nimport type {\n\tBehaviorRuntime,\n\tStagePose,\n\tStageBehaviorSnapshot,\n} from '../../runtime/wasm-stage-runtime';\nimport type {\n\tFirstPersonMovementComponent,\n\tFirstPersonInputComponent,\n\tFirstPersonStateComponent,\n} from './components';\nimport type { TransformState } from '../../capabilities/transform-store';\n\n/**\n * Entity interface for the first-person controller.\n * Entities with this behavior have these components attached.\n */\nexport interface FirstPersonEntity {\n\tuuid: string;\n\ttransformStore: TransformState;\n\tfirstPerson: FirstPersonMovementComponent;\n\t$fps: FirstPersonInputComponent;\n\tfirstPersonState: FirstPersonStateComponent;\n}\n\n/**\n * Viewmodel configuration for an entity that follows the camera.\n */\nexport interface ViewmodelConfig {\n\t/** The entity whose visual group should be positioned relative to the camera. */\n\tentity: any;\n\t/** Offset from the eye position in camera-local space (right, down, forward). */\n\toffset: Vector3;\n\t/** Additional rotation applied to the viewmodel in local space (Euler angles in radians). */\n\trotation?: Euler;\n}\n\n// Reusable temporaries (avoid per-frame allocations)\nconst _viewmodelQuat = new Quaternion();\nconst _viewmodelLocalQuat = new Quaternion();\nconst _viewmodelEuler = new Euler();\nconst _offset = new Vector3();\nconst _pose: StagePose = { position: [0, 0, 0], rotation: [0, 0, 0, 1] };\nconst _snapshot = {} as StageBehaviorSnapshot;\n\n/**\n * Presentation system driven by runtime pose + snapshot reads.\n */\nexport class FirstPersonPresentation {\n\tprivate perspectives = new Map<string, FirstPersonPerspective>();\n\tprivate viewmodels = new Map<string, ViewmodelConfig>();\n\n\t/**\n\t * Associate a FirstPersonPerspective with an entity (by uuid).\n\t * Called once by the descriptor system when the behavior is initialized.\n\t */\n\tsetPerspective(entityUuid: string, perspective: FirstPersonPerspective): void {\n\t\tthis.perspectives.set(entityUuid, perspective);\n\t}\n\n\thasPerspective(entityUuid: string): boolean {\n\t\treturn this.perspectives.has(entityUuid);\n\t}\n\n\t/**\n\t * Attach a viewmodel (weapon/item) to an entity.\n\t * The viewmodel entity's group will be positioned relative to the camera each frame.\n\t */\n\tsetViewmodel(entityUuid: string, config: ViewmodelConfig): void {\n\t\tthis.viewmodels.set(entityUuid, config);\n\t}\n\n\thasViewmodel(entityUuid: string): boolean {\n\t\treturn this.viewmodels.has(entityUuid);\n\t}\n\n\t/**\n\t * Sync camera + viewmodel for one entity from the runtime state.\n\t */\n\tsyncEntity(\n\t\tentityUuid: string,\n\t\truntime: BehaviorRuntime,\n\t\thandle: number,\n\t\tmovement: FirstPersonMovementComponent,\n\t\tstate: FirstPersonStateComponent | undefined,\n\t): void {\n\t\tconst perspective = this.perspectives.get(entityUuid);\n\t\tconst pose = runtime.getPose(handle, _pose);\n\t\tconst snapshot = runtime.readSnapshot(handle, _snapshot);\n\n\t\tif (perspective && snapshot) {\n\t\t\tperspective.setLook?.(snapshot.yaw, snapshot.pitch);\n\t\t\tif (state) {\n\t\t\t\tstate.yaw = snapshot.yaw;\n\t\t\t\tstate.pitch = snapshot.pitch;\n\t\t\t\tstate.currentSpeed = snapshot.speed;\n\t\t\t}\n\t\t}\n\n\t\tif (perspective?.initialPosition && pose) {\n\t\t\tperspective.initialPosition.set(\n\t\t\t\tpose.position[0],\n\t\t\t\tpose.position[1] + movement.eyeHeight,\n\t\t\t\tpose.position[2],\n\t\t\t);\n\t\t}\n\n\t\tconst vm = this.viewmodels.get(entityUuid);\n\t\tif (vm && perspective?.initialPosition && state) {\n\t\t\tthis.positionViewmodel(vm, perspective, state, runtime);\n\t\t}\n\t}\n\n\t/**\n\t * Position a viewmodel entity relative to the camera's eye position and rotation.\n\t */\n\tprivate positionViewmodel(\n\t\tvm: ViewmodelConfig,\n\t\tperspective: FirstPersonPerspective,\n\t\tstate: FirstPersonStateComponent,\n\t\truntime: BehaviorRuntime,\n\t): void {\n\t\tconst eyePos = perspective.initialPosition;\n\t\tif (!eyePos) return;\n\n\t\t// Build camera rotation quaternion\n\t\t_viewmodelQuat.setFromEuler(\n\t\t\t_viewmodelEuler.set(state.pitch, state.yaw, 0, 'YXZ'),\n\t\t);\n\n\t\t// Compose with the viewmodel's local rotation (model orientation fix)\n\t\tif (vm.rotation) {\n\t\t\t_viewmodelLocalQuat.setFromEuler(vm.rotation);\n\t\t\t_viewmodelQuat.multiply(_viewmodelLocalQuat);\n\t\t}\n\n\t\t// Transform the local offset into world space\n\t\t_offset.copy(vm.offset).applyQuaternion(_viewmodelQuat);\n\n\t\tconst wx = eyePos.x + _offset.x;\n\t\tconst wy = eyePos.y + _offset.y;\n\t\tconst wz = eyePos.z + _offset.z;\n\n\t\tconst entity = vm.entity;\n\n\t\t// Move the runtime body (if registered) so physics stays in sync\n\t\tconst handle = (entity?.runtimeHandle ?? -1) as number;\n\t\tif (handle >= 0) {\n\t\t\truntime.setPosition(handle, wx, wy, wz);\n\t\t\truntime.setRotation(\n\t\t\t\thandle,\n\t\t\t\t_viewmodelQuat.x, _viewmodelQuat.y, _viewmodelQuat.z, _viewmodelQuat.w,\n\t\t\t);\n\t\t}\n\n\t\t// Also set the group directly for immediate visual update\n\t\tif (entity?.group) {\n\t\t\tentity.group.position.set(wx, wy, wz);\n\t\t\tentity.group.quaternion.copy(_viewmodelQuat);\n\t\t}\n\t}\n\n\t/** Cleanup */\n\tdestroy(): void {\n\t\tthis.perspectives.clear();\n\t\tthis.viewmodels.clear();\n\t}\n}\n","/**\n * First Person Controller Behavior Descriptor\n *\n * Type-safe descriptor for the first-person controller behavior system.\n * Provides entity.use() API for FPS movement and camera control.\n */\n\nimport { Euler } from 'three';\nimport { defineBehavior } from '../behavior-descriptor';\nimport type { BehaviorEntityLink, BehaviorSystem } from '../behavior-system';\nimport type { FirstPersonPerspective } from '../../camera/perspectives/first-person-perspective';\nimport { Vec3Input, VEC3_ZERO, toThreeVector3 } from '../../core/vector';\nimport {\n\tFirstPersonPresentation,\n\ttype FirstPersonEntity,\n\ttype ViewmodelConfig,\n} from './first-person.system';\nimport { FirstPersonState } from './first-person-fsm';\nimport {\n\tcreateFirstPersonMovementComponent,\n\tcreateFirstPersonInputComponent,\n\tcreateFirstPersonStateComponent,\n} from './components';\nimport {\n\tSTAGE_INPUT_BUTTON_RUN,\n\treadBehaviorSnapshot,\n\ttype BehaviorRuntime,\n} from '../../runtime/wasm-stage-runtime';\n\n/**\n * First-person controller behavior options (typed for entity.use() autocomplete).\n */\nexport interface FirstPersonBehaviorOptions {\n\t/** Base walking speed (default: 8) */\n\twalkSpeed?: number;\n\t/** Sprint/run speed (default: 16) */\n\trunSpeed?: number;\n\t/** Look sensitivity multiplier (default: 2) */\n\tlookSensitivity?: number;\n\t/** Eye height offset above entity position (default: 1.7) */\n\teyeHeight?: number;\n\t/** The FirstPersonPerspective to drive (from camera.getPerspective()) */\n\tperspective?: FirstPersonPerspective;\n\t/** Optional viewmodel (weapon/item) to position relative to the camera */\n\tviewmodel?: { entity: any; offset: Vec3Input; rotation?: Euler };\n}\n\nconst defaultOptions: FirstPersonBehaviorOptions = {\n\twalkSpeed: 8,\n\trunSpeed: 16,\n\tlookSensitivity: 2,\n\teyeHeight: 1.7,\n};\n\nconst FIRST_PERSON_BEHAVIOR_KEY = Symbol.for(\n\t'zylem:behavior:first-person-controller',\n);\n\n/**\n * Forwards look/move input into the wasm runtime and syncs the camera\n * perspective + optional viewmodel from the runtime's pose/snapshot\n * (presentation only — the Rust `first_person` behavior owns simulation).\n */\nclass FirstPersonBehaviorSystem implements BehaviorSystem {\n\tprivate presentation = new FirstPersonPresentation();\n\tprivate attachedRuntimeSlots = new Set<number>();\n\n\tconstructor(\n\t\tprivate wasmStage: BehaviorRuntime | null,\n\t\tprivate getBehaviorLinks?: (key: symbol) => Iterable<BehaviorEntityLink>,\n\t) {}\n\n\tupdate(_ecs: unknown, _delta: number): void {\n\t\tconst links = this.getBehaviorLinks?.(FIRST_PERSON_BEHAVIOR_KEY);\n\t\tif (!links) return;\n\n\t\tfor (const link of links) {\n\t\t\tconst gameEntity = link.entity as any;\n\t\t\tconst fpsRef = link.ref as any;\n\n\t\t\tconst options = fpsRef.options as FirstPersonBehaviorOptions;\n\n\t\t\t// Initialize components (once)\n\t\t\tif (!gameEntity.firstPerson) {\n\t\t\t\tgameEntity.firstPerson = createFirstPersonMovementComponent(options);\n\t\t\t}\n\t\t\tif (!gameEntity.$fps) {\n\t\t\t\tgameEntity.$fps = createFirstPersonInputComponent();\n\t\t\t}\n\t\t\tif (!gameEntity.firstPersonState) {\n\t\t\t\tgameEntity.firstPersonState = createFirstPersonStateComponent();\n\t\t\t}\n\n\t\t\t// Wire up perspective (once)\n\t\t\tif (options.perspective && !this.presentation.hasPerspective(gameEntity.uuid)) {\n\t\t\t\tthis.presentation.setPerspective(gameEntity.uuid, options.perspective);\n\t\t\t}\n\n\t\t\t// Wire up viewmodel (once)\n\t\t\tif (options.viewmodel && !this.presentation.hasViewmodel(gameEntity.uuid)) {\n\t\t\t\tthis.presentation.setViewmodel(gameEntity.uuid, {\n\t\t\t\t\t...options.viewmodel,\n\t\t\t\t\toffset: toThreeVector3(options.viewmodel.offset, VEC3_ZERO),\n\t\t\t\t} as ViewmodelConfig);\n\t\t\t}\n\n\t\t\tconst handle = (gameEntity.runtimeHandle ?? -1) as number;\n\t\t\tif (!this.wasmStage || handle < 0) continue;\n\n\t\t\tthis.ensureWasmAttached(handle, options);\n\t\t\tfpsRef.__wasmStage = this.wasmStage;\n\t\t\tfpsRef.__entity = gameEntity;\n\t\t\tconst input = gameEntity.$fps;\n\t\t\tif (input) {\n\t\t\t\tthis.wasmStage.writeInputAxes(handle, input.moveX ?? 0, input.moveZ ?? 0);\n\t\t\t\tthis.wasmStage.writeInputLook(handle, input.lookX ?? 0, input.lookY ?? 0);\n\t\t\t\tthis.wasmStage.writeInputButton(handle, STAGE_INPUT_BUTTON_RUN, !!input.sprint);\n\t\t\t}\n\n\t\t\tthis.presentation.syncEntity(\n\t\t\t\tgameEntity.uuid,\n\t\t\t\tthis.wasmStage,\n\t\t\t\thandle,\n\t\t\t\tgameEntity.firstPerson,\n\t\t\t\tgameEntity.firstPersonState,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate ensureWasmAttached(handle: number, options: FirstPersonBehaviorOptions): void {\n\t\tif (!this.wasmStage || this.attachedRuntimeSlots.has(handle)) return;\n\t\tthis.wasmStage.attachFirstPerson(handle, {\n\t\t\twalkSpeed: options.walkSpeed ?? defaultOptions.walkSpeed!,\n\t\t\trunSpeed: options.runSpeed ?? defaultOptions.runSpeed!,\n\t\t\teyeHeight: options.eyeHeight ?? defaultOptions.eyeHeight!,\n\t\t\tlookSensitivity: options.lookSensitivity ?? defaultOptions.lookSensitivity!,\n\t\t});\n\t\tthis.attachedRuntimeSlots.add(handle);\n\t}\n\n\tdestroy(_ecs: unknown): void {\n\t\tthis.presentation.destroy();\n\t\tthis.attachedRuntimeSlots.clear();\n\t}\n}\n\n/**\n * FirstPersonBehavior - typed descriptor for first-person movement and camera control.\n *\n * Provides complete first-person controller including:\n * - WASD movement relative to camera yaw\n * - Mouse look driving a FirstPersonPerspective\n * - Optional viewmodel positioning (weapon/item following camera)\n * - Walk/run state tracking via FSM\n *\n * @example\n * ```typescript\n * import { FirstPersonBehavior } from \"@zylem/game-lib/behavior\";\n * import { createCamera, Perspectives, FirstPersonPerspective } from \"@zylem/game-lib/core\";\n * import { createActor } from \"@zylem/game-lib/entity\";\n *\n * const fpsCamera = createCamera({ perspective: Perspectives.FirstPerson, ... });\n * const fps = fpsCamera.getPerspective<FirstPersonPerspective>();\n *\n * const player = createActor({ name: 'player', ... });\n * const controller = player.use(FirstPersonBehavior, {\n * perspective: fps,\n * walkSpeed: 8,\n * runSpeed: 16,\n * lookSensitivity: 2,\n * });\n *\n * // In update loop - write input, behavior handles the rest\n * player.onUpdate(({ inputs }) => {\n * player.$fps.moveX = inputs.p1.axes.Horizontal.value;\n * player.$fps.moveZ = inputs.p1.axes.Vertical.value;\n * player.$fps.lookX = inputs.p1.axes.SecondaryHorizontal.value;\n * player.$fps.lookY = inputs.p1.axes.SecondaryVertical.value;\n * player.$fps.sprint = inputs.p1.shoulders.LTrigger.held > 0;\n * });\n * ```\n */\nexport const FirstPersonBehavior = defineBehavior<\n\tFirstPersonBehaviorOptions,\n\t{\n\t\tgetState: () => FirstPersonState;\n\t\tgetYaw: () => number;\n\t\tgetPitch: () => number;\n\t\tattachViewmodel: (entity: any, offset: Vec3Input) => void;\n\t},\n\tFirstPersonEntity\n>({\n\tname: 'first-person-controller',\n\tdefaultOptions,\n\tsystemFactory: (ctx) =>\n\t\tnew FirstPersonBehaviorSystem(ctx.wasmStage ?? null, ctx.getBehaviorLinks),\n\t// Handles read the runtime snapshot buffer (previous-step values). The\n\t// Rust behavior is the single source of truth.\n\tcreateHandle: (ref) => ({\n\t\tgetState: () => {\n\t\t\tconst snapshot = readBehaviorSnapshot(ref);\n\t\t\tif (!snapshot) return FirstPersonState.Idle;\n\t\t\tif (snapshot.speed <= 0.01) return FirstPersonState.Idle;\n\t\t\tconst walkSpeed = ref.options.walkSpeed ?? defaultOptions.walkSpeed!;\n\t\t\treturn snapshot.speed > walkSpeed\n\t\t\t\t? FirstPersonState.Running\n\t\t\t\t: FirstPersonState.Walking;\n\t\t},\n\t\tgetYaw: () => readBehaviorSnapshot(ref)?.yaw ?? 0,\n\t\tgetPitch: () => readBehaviorSnapshot(ref)?.pitch ?? 0,\n\t\tattachViewmodel: (entity: any, offset: Vec3Input) => {\n\t\t\tif (ref.options) {\n\t\t\t\tref.options.viewmodel = { entity, offset };\n\t\t\t}\n\t\t},\n\t}),\n});\n"],"mappings":"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","names":["FirstPersonState","FirstPersonEvent"]}
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  SyncStateMachine,
3
- t
4
- } from "./chunk-4URF65BR.js";
3
+ import_typescript_fsm
4
+ } from "./chunk-AS3HR7AS.js";
5
5
  import {
6
6
  StageRicochetDim,
7
7
  StageRicochetReflection
8
- } from "./chunk-Q75LKHDF.js";
8
+ } from "./chunk-E4X4YJZB.js";
9
9
  import {
10
10
  defineBehavior
11
11
  } from "./chunk-EZNCCO4L.js";
@@ -21,6 +21,13 @@ var Ricochet3DEvent = /* @__PURE__ */ ((Ricochet3DEvent2) => {
21
21
  Ricochet3DEvent2["EndRicochet"] = "end-ricochet";
22
22
  return Ricochet3DEvent2;
23
23
  })(Ricochet3DEvent || {});
24
+ function bodyOf(entity) {
25
+ const body = entity?.body;
26
+ if (body && typeof body.linvel === "function" && typeof body.translation === "function") {
27
+ return body;
28
+ }
29
+ return null;
30
+ }
24
31
  function clamp(value, min, max) {
25
32
  return Math.max(min, Math.min(max, value));
26
33
  }
@@ -37,10 +44,10 @@ var Ricochet3DFSM = class {
37
44
  this.machine = new SyncStateMachine(
38
45
  "idle" /* Idle */,
39
46
  [
40
- t("idle" /* Idle */, "start-ricochet" /* StartRicochet */, "ricocheting" /* Ricocheting */),
41
- t("ricocheting" /* Ricocheting */, "end-ricochet" /* EndRicochet */, "idle" /* Idle */),
42
- t("idle" /* Idle */, "end-ricochet" /* EndRicochet */, "idle" /* Idle */),
43
- t("ricocheting" /* Ricocheting */, "start-ricochet" /* StartRicochet */, "ricocheting" /* Ricocheting */)
47
+ (0, import_typescript_fsm.t)("idle" /* Idle */, "start-ricochet" /* StartRicochet */, "ricocheting" /* Ricocheting */),
48
+ (0, import_typescript_fsm.t)("ricocheting" /* Ricocheting */, "end-ricochet" /* EndRicochet */, "idle" /* Idle */),
49
+ (0, import_typescript_fsm.t)("idle" /* Idle */, "end-ricochet" /* EndRicochet */, "idle" /* Idle */),
50
+ (0, import_typescript_fsm.t)("ricocheting" /* Ricocheting */, "start-ricochet" /* StartRicochet */, "ricocheting" /* Ricocheting */)
44
51
  ]
45
52
  );
46
53
  }
@@ -146,14 +153,16 @@ var Ricochet3DFSM = class {
146
153
  let selfPosition = ctx.selfPosition;
147
154
  let otherPosition = ctx.otherPosition;
148
155
  let otherSize = ctx.otherSize;
149
- if (ctx.entity?.body) {
150
- const velocity = ctx.entity.body.linvel();
156
+ const selfBody = bodyOf(ctx.entity);
157
+ if (selfBody) {
158
+ const velocity = selfBody.linvel();
151
159
  selfVelocity = selfVelocity ?? { x: velocity.x, y: velocity.y, z: velocity.z };
152
- const position = ctx.entity.body.translation();
160
+ const position = selfBody.translation();
153
161
  selfPosition = selfPosition ?? { x: position.x, y: position.y, z: position.z };
154
162
  }
155
- if (ctx.otherEntity?.body) {
156
- const position = ctx.otherEntity.body.translation();
163
+ const otherBody = bodyOf(ctx.otherEntity);
164
+ if (otherBody) {
165
+ const position = otherBody.translation();
157
166
  otherPosition = otherPosition ?? { x: position.x, y: position.y, z: position.z };
158
167
  }
159
168
  if (ctx.otherEntity && "size" in ctx.otherEntity) {
@@ -428,4 +437,4 @@ export {
428
437
  Ricochet3DFSM,
429
438
  Ricochet3DBehavior
430
439
  };
431
- //# sourceMappingURL=chunk-27UPCP5B.js.map
440
+ //# sourceMappingURL=chunk-5UMCWV5A.js.map