@zylem/behaviors 0.3.1 → 0.4.0

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 (138) hide show
  1. package/dist/{behavior-system-DYbx-x_-.d.ts → behavior-system-DSNek0W9.d.ts} +205 -29
  2. package/dist/{chunk-Y6UJOUE5.js → chunk-4V7TIRO2.js} +1 -1
  3. package/dist/chunk-4V7TIRO2.js.map +1 -0
  4. package/dist/{chunk-DYLH6K7B.js → chunk-4WH2ZN7P.js} +1 -1
  5. package/dist/{chunk-DYLH6K7B.js.map → chunk-4WH2ZN7P.js.map} +1 -1
  6. package/dist/{chunk-ZPSAXX73.js → chunk-5SM5SH5O.js} +26 -26
  7. package/dist/chunk-5SM5SH5O.js.map +1 -0
  8. package/dist/{chunk-STCCNLPC.js → chunk-5XZWTFGF.js} +5 -3
  9. package/dist/chunk-5XZWTFGF.js.map +1 -0
  10. package/dist/{chunk-OYWFPIHF.js → chunk-64HKCSXD.js} +19 -16
  11. package/dist/chunk-64HKCSXD.js.map +1 -0
  12. package/dist/{chunk-JAN34G6S.js → chunk-FRJDQM23.js} +108 -154
  13. package/dist/chunk-FRJDQM23.js.map +1 -0
  14. package/dist/{chunk-RTRVBEIB.js → chunk-FTDZAKZW.js} +5 -18
  15. package/dist/chunk-FTDZAKZW.js.map +1 -0
  16. package/dist/{chunk-XIJNE67Y.js → chunk-GFARAJV2.js} +3 -5
  17. package/dist/{chunk-XIJNE67Y.js.map → chunk-GFARAJV2.js.map} +1 -1
  18. package/dist/{chunk-SS7ELMJ7.js → chunk-GTLA6LOJ.js} +16 -22
  19. package/dist/chunk-GTLA6LOJ.js.map +1 -0
  20. package/dist/{chunk-VAN4WW3F.js → chunk-H4TJ7X4O.js} +4 -7
  21. package/dist/chunk-H4TJ7X4O.js.map +1 -0
  22. package/dist/{chunk-4I6E6DGV.js → chunk-HNESASBB.js} +4 -6
  23. package/dist/{chunk-4I6E6DGV.js.map → chunk-HNESASBB.js.map} +1 -1
  24. package/dist/{chunk-KS72RZHX.js → chunk-IZJFUAXB.js} +1 -1
  25. package/dist/{chunk-KS72RZHX.js.map → chunk-IZJFUAXB.js.map} +1 -1
  26. package/dist/{chunk-SVLNF434.js → chunk-JXMF4NUI.js} +51 -3
  27. package/dist/chunk-JXMF4NUI.js.map +1 -0
  28. package/dist/chunk-KFFETUAL.js +40 -0
  29. package/dist/chunk-KFFETUAL.js.map +1 -0
  30. package/dist/chunk-LL4FLHIY.js +12 -0
  31. package/dist/chunk-LL4FLHIY.js.map +1 -0
  32. package/dist/{chunk-2CLT36VB.js → chunk-MJFMNRMZ.js} +13 -14
  33. package/dist/chunk-MJFMNRMZ.js.map +1 -0
  34. package/dist/{chunk-RTVRUNYR.js → chunk-OTLSUYQQ.js} +2 -2
  35. package/dist/chunk-OTLSUYQQ.js.map +1 -0
  36. package/dist/{chunk-XG2YMCJW.js → chunk-PWF757B6.js} +4 -6
  37. package/dist/{chunk-XG2YMCJW.js.map → chunk-PWF757B6.js.map} +1 -1
  38. package/dist/{chunk-LD5SMIB3.js → chunk-QVCMJ4Q2.js} +1 -1
  39. package/dist/{chunk-LD5SMIB3.js.map → chunk-QVCMJ4Q2.js.map} +1 -1
  40. package/dist/{chunk-F3KRX2V4.js → chunk-RONH2KDI.js} +10 -12
  41. package/dist/chunk-RONH2KDI.js.map +1 -0
  42. package/dist/chunk-RTRFJVCZ.js +454 -0
  43. package/dist/chunk-RTRFJVCZ.js.map +1 -0
  44. package/dist/{chunk-6C6NXNEW.js → chunk-UL6Q5HDP.js} +25 -3
  45. package/dist/chunk-UL6Q5HDP.js.map +1 -0
  46. package/dist/{chunk-KOZUNIIE.js → chunk-V74N7D6Q.js} +7 -7
  47. package/dist/{chunk-KOZUNIIE.js.map → chunk-V74N7D6Q.js.map} +1 -1
  48. package/dist/{chunk-YUO6SKJD.js → chunk-WXXXJ22R.js} +11 -13
  49. package/dist/chunk-WXXXJ22R.js.map +1 -0
  50. package/dist/{chunk-ARJWUNRE.js → chunk-XLUJDKCT.js} +7 -7
  51. package/dist/{chunk-ARJWUNRE.js.map → chunk-XLUJDKCT.js.map} +1 -1
  52. package/dist/{chunk-GCNRXAKH.js → chunk-XP3ASVGJ.js} +13 -10
  53. package/dist/chunk-XP3ASVGJ.js.map +1 -0
  54. package/dist/{chunk-ID5QJYF5.js → chunk-Y7YIAWPW.js} +11 -1
  55. package/dist/chunk-Y7YIAWPW.js.map +1 -0
  56. package/dist/{chunk-FPTWVCPN.js → chunk-ZA43LPRM.js} +4 -24
  57. package/dist/chunk-ZA43LPRM.js.map +1 -0
  58. package/dist/{chunk-K3UNLQ7W.js → chunk-ZRQWSURL.js} +2 -2
  59. package/dist/chunk-ZRQWSURL.js.map +1 -0
  60. package/dist/cooldown.d.ts +2 -1
  61. package/dist/cooldown.js +1 -1
  62. package/dist/coordinators/boundary-ricochet-3d.d.ts +12 -5
  63. package/dist/coordinators/boundary-ricochet-3d.js +2 -1
  64. package/dist/coordinators/boundary-ricochet.d.ts +9 -7
  65. package/dist/coordinators/boundary-ricochet.js +2 -1
  66. package/dist/coordinators/first-person-shooter.d.ts +9 -8
  67. package/dist/coordinators/first-person-shooter.js +3 -3
  68. package/dist/coordinators/multidirectional-space-shooter.d.ts +12 -9
  69. package/dist/coordinators/multidirectional-space-shooter.js +4 -4
  70. package/dist/coordinators/top-down-shooter.d.ts +9 -8
  71. package/dist/coordinators/top-down-shooter.js +3 -3
  72. package/dist/core.d.ts +4 -4
  73. package/dist/core.js +4 -32
  74. package/dist/core.js.map +1 -1
  75. package/dist/destructible-3d.d.ts +2 -1
  76. package/dist/first-person.d.ts +12 -12
  77. package/dist/first-person.js +8 -8
  78. package/dist/index.d.ts +37 -14
  79. package/dist/index.js +73 -44
  80. package/dist/jumper-2d.d.ts +7 -6
  81. package/dist/jumper-2d.js +5 -5
  82. package/dist/jumper-3d.d.ts +12 -11
  83. package/dist/jumper-3d.js +6 -6
  84. package/dist/{moveable-BCRpjAQb.d.ts → moveable-BLTHO7Jq.d.ts} +2 -78
  85. package/dist/particle-emitter.d.ts +1 -1
  86. package/dist/particle-emitter.js +1 -1
  87. package/dist/platformer-2d.d.ts +306 -0
  88. package/dist/platformer-2d.js +23 -0
  89. package/dist/platformer-3d.d.ts +74 -87
  90. package/dist/platformer-3d.js +4 -4
  91. package/dist/ricochet-2d.d.ts +1 -1
  92. package/dist/ricochet-2d.js +2 -2
  93. package/dist/ricochet-3d.d.ts +1 -1
  94. package/dist/ricochet-3d.js +2 -2
  95. package/dist/runtime-2d.d.ts +93 -2
  96. package/dist/runtime-2d.js +145 -22
  97. package/dist/runtime-2d.js.map +1 -1
  98. package/dist/screen-visibility.d.ts +2 -1
  99. package/dist/screen-wrap.d.ts +2 -1
  100. package/dist/screen-wrap.js +1 -1
  101. package/dist/shooter-2d.d.ts +5 -3
  102. package/dist/shooter-2d.js +2 -2
  103. package/dist/shooter-fire-DGripl0z.d.ts +55 -0
  104. package/dist/thruster.d.ts +8 -18
  105. package/dist/thruster.js +6 -6
  106. package/dist/top-down-movement.d.ts +7 -4
  107. package/dist/top-down-movement.js +5 -5
  108. package/dist/{use-behavior-D_82jAL3.d.ts → use-behavior-h9-T5FSv.d.ts} +1 -1
  109. package/dist/world-boundary-2d.d.ts +2 -1
  110. package/dist/world-boundary-2d.js +2 -2
  111. package/dist/world-boundary-3d.d.ts +2 -1
  112. package/dist/world-boundary-3d.js +2 -2
  113. package/package.json +5 -5
  114. package/dist/chunk-2C3Z5GE7.js +0 -176
  115. package/dist/chunk-2C3Z5GE7.js.map +0 -1
  116. package/dist/chunk-2CLT36VB.js.map +0 -1
  117. package/dist/chunk-3PYGRHAL.js +0 -35
  118. package/dist/chunk-3PYGRHAL.js.map +0 -1
  119. package/dist/chunk-6C6NXNEW.js.map +0 -1
  120. package/dist/chunk-F3KRX2V4.js.map +0 -1
  121. package/dist/chunk-FPTWVCPN.js.map +0 -1
  122. package/dist/chunk-GCNRXAKH.js.map +0 -1
  123. package/dist/chunk-ID5QJYF5.js.map +0 -1
  124. package/dist/chunk-JAN34G6S.js.map +0 -1
  125. package/dist/chunk-K3UNLQ7W.js.map +0 -1
  126. package/dist/chunk-OYWFPIHF.js.map +0 -1
  127. package/dist/chunk-RTRVBEIB.js.map +0 -1
  128. package/dist/chunk-RTVRUNYR.js.map +0 -1
  129. package/dist/chunk-SS7ELMJ7.js.map +0 -1
  130. package/dist/chunk-STCCNLPC.js.map +0 -1
  131. package/dist/chunk-SVLNF434.js.map +0 -1
  132. package/dist/chunk-VAN4WW3F.js.map +0 -1
  133. package/dist/chunk-Y6UJOUE5.js.map +0 -1
  134. package/dist/chunk-YUO6SKJD.js.map +0 -1
  135. package/dist/chunk-ZPSAXX73.js.map +0 -1
  136. package/dist/runtime-pong.d.ts +0 -92
  137. package/dist/runtime-pong.js +0 -38
  138. /package/dist/{runtime-pong.js.map → platformer-2d.js.map} +0 -0
@@ -1,3 +1,5 @@
1
+ import { RigidBody, Collider, World } from '@dimforge/rapier3d-compat';
2
+
1
3
  /**
2
4
  * BehaviorDescriptor
3
5
  *
@@ -102,6 +104,74 @@ interface DefineBehaviorConfig<O extends Record<string, any>, H extends Record<s
102
104
  */
103
105
  declare function defineBehavior<O extends Record<string, any>, H extends Record<string, any> = Record<string, never>, I = unknown>(config: DefineBehaviorConfig<O, H, I>): BehaviorDescriptor<O, H, I>;
104
106
 
107
+ type VelocityIntentMode = 'replace' | 'add';
108
+ interface VelocityIntent {
109
+ x?: number;
110
+ y?: number;
111
+ z?: number;
112
+ mode: VelocityIntentMode;
113
+ priority?: number;
114
+ }
115
+ /**
116
+ * Transform state managed by Valtio for batched physics updates.
117
+ *
118
+ * This store accumulates transformation intents during the frame,
119
+ * then applies them all at once after onUpdate() callbacks complete.
120
+ *
121
+ * Design decisions:
122
+ * - Position: Accumulated deltas (additive)
123
+ * - Rotation: Last value wins (replacement)
124
+ * - Velocity: Accumulated deltas (additive)
125
+ * - Angular velocity: Accumulated deltas (additive)
126
+ */
127
+ interface TransformState {
128
+ /** Position deltas to be applied (accumulated) */
129
+ position: {
130
+ x: number;
131
+ y: number;
132
+ z: number;
133
+ };
134
+ /** Rotation quaternion (last value wins) */
135
+ rotation: {
136
+ x: number;
137
+ y: number;
138
+ z: number;
139
+ w: number;
140
+ };
141
+ /** Linear velocity (accumulated) */
142
+ velocity: {
143
+ x: number;
144
+ y: number;
145
+ z: number;
146
+ };
147
+ /** Angular velocity (accumulated) */
148
+ angularVelocity: {
149
+ x: number;
150
+ y: number;
151
+ z: number;
152
+ };
153
+ /** Per-source velocity intents composed once per frame */
154
+ velocityChannels: Record<string, VelocityIntent>;
155
+ /** Dirty flags to track what needs to be applied */
156
+ dirty: {
157
+ position: boolean;
158
+ rotation: boolean;
159
+ velocityX: boolean;
160
+ velocityY: boolean;
161
+ velocityZ: boolean;
162
+ velocityChannels: boolean;
163
+ angularVelocity: boolean;
164
+ };
165
+ }
166
+ /**
167
+ * Create a new transform store with optional initial values.
168
+ * The store is a Valtio proxy, making it reactive for debugging/visualization.
169
+ *
170
+ * @param initial Optional initial state values
171
+ * @returns Reactive transform state
172
+ */
173
+ declare function createTransformStore(initial?: Partial<TransformState>): TransformState;
174
+
105
175
  /**
106
176
  * Minimal, runtime-agnostic entity options contract.
107
177
  *
@@ -129,14 +199,14 @@ declare class GameEntity<TOptions = unknown> {
129
199
  group: any;
130
200
  mesh?: any;
131
201
  materials?: any[];
132
- body: any;
133
- collider: any;
202
+ body: RigidBody | null;
203
+ collider: Collider | null;
134
204
  colliderDesc?: any;
135
205
  colliderDescs: any[];
136
206
  bodyDesc?: any;
137
207
  physicsAttached: boolean;
138
208
  runtimeHandle?: number;
139
- transformStore?: any;
209
+ transformStore?: TransformState;
140
210
  nodeDestroy?: any;
141
211
  isInstanced?: boolean;
142
212
  private __behaviorRefs;
@@ -196,30 +266,122 @@ declare const StageEventType: {
196
266
  readonly Wrapped: 8;
197
267
  readonly CooldownFired: 9;
198
268
  };
269
+ type StageBoundaryDimValue = (typeof StageBoundaryDim)[keyof typeof StageBoundaryDim];
270
+ type StageRicochetDimValue = (typeof StageRicochetDim)[keyof typeof StageRicochetDim];
271
+ type StageRicochetReflectionValue = (typeof StageRicochetReflection)[keyof typeof StageRicochetReflection];
272
+ type StageTopDownPlaneValue = (typeof StageTopDownPlane)[keyof typeof StageTopDownPlane];
273
+ /** Position + rotation snapshot for one runtime slot. */
274
+ interface StagePose {
275
+ position: [number, number, number];
276
+ rotation: [number, number, number, number];
277
+ }
278
+ interface StageWorldBoundaryOptions {
279
+ top: number;
280
+ bottom: number;
281
+ left: number;
282
+ right: number;
283
+ front?: number;
284
+ back?: number;
285
+ padding?: number;
286
+ }
287
+ interface StageScreenWrapOptions {
288
+ width: number;
289
+ height: number;
290
+ centerX?: number;
291
+ centerY?: number;
292
+ edgeThreshold?: number;
293
+ }
294
+ interface StageRicochetOptions {
295
+ minSpeed: number;
296
+ maxSpeed: number;
297
+ speedMultiplier: number;
298
+ maxAngleDeg: number;
299
+ reflectionMode: StageRicochetReflectionValue;
300
+ }
301
+ interface StageThrusterOptions {
302
+ maxSpeed: number;
303
+ acceleration: number;
304
+ turnRateRadPerSec: number;
305
+ linearDamping: number;
306
+ }
307
+ interface StageTopDownOptions {
308
+ plane: StageTopDownPlaneValue;
309
+ speed: number;
310
+ faceMovement?: boolean;
311
+ }
312
+ interface StageShooter2DOptions {
313
+ fireRateHz: number;
314
+ muzzleOffsetX: number;
315
+ muzzleOffsetY: number;
316
+ muzzleSpeed: number;
317
+ }
318
+ interface StageJumper2DOptions {
319
+ jumpForce: number;
320
+ maxJumps: number;
321
+ jumpBufferTime: number;
322
+ coyoteTime: number;
323
+ gravity: number;
324
+ }
325
+ interface StageJumper3DOptions {
326
+ jumpForce: number;
327
+ maxJumps: number;
328
+ gravity: number;
329
+ }
330
+ interface StagePlatformer3DOptions {
331
+ halfHeight: number;
332
+ radius: number;
333
+ walkSpeed: number;
334
+ runSpeed: number;
335
+ jumpForce: number;
336
+ maxJumps: number;
337
+ gravity: number;
338
+ }
339
+ interface StageFirstPersonOptions {
340
+ walkSpeed: number;
341
+ runSpeed: number;
342
+ eyeHeight: number;
343
+ lookSensitivity: number;
344
+ }
345
+ interface StageFirstPersonInput {
346
+ moveX: number;
347
+ moveZ: number;
348
+ lookYawDelta: number;
349
+ lookPitchDelta: number;
350
+ run: boolean;
351
+ }
352
+ /**
353
+ * Typed FFI surface of the wasm unified Stage runtime that behaviors rely on.
354
+ *
355
+ * The concrete implementation lives in `@zylem/game-lib`
356
+ * (`wasm-stage-runtime.ts`); this contract mirrors the slice of its methods
357
+ * that behavior systems call so descriptors stay runtime-agnostic.
358
+ */
199
359
  interface WasmStageRuntime {
200
- getPose(handle: number): any;
201
- attachThruster(handle: number, opts: any): boolean;
360
+ getPose(handle: number, out?: StagePose): StagePose | null;
361
+ attachThruster(handle: number, opts: StageThrusterOptions): boolean;
202
362
  setThrusterInput(handle: number, thrust: number, rotate: number): void;
203
- attachTopDown(handle: number, opts: {
204
- plane: number;
205
- speed: number;
206
- faceMovement?: boolean;
207
- }): boolean;
363
+ attachTopDown(handle: number, opts: StageTopDownOptions): boolean;
208
364
  setTopDownInput(handle: number, moveX: number, moveY: number): void;
209
- attachShooter2D(handle: number, opts: any): boolean;
210
- attachScreenWrap(handle: number, opts: any): boolean;
211
- queryScreenWrap(handle: number): any;
212
- attachWorldBoundary(handle: number, dim: number, opts: any): boolean;
213
- queryWorldBoundary(handle: number): any;
214
- attachRicochet(handle: number, dim: number, opts: any): boolean;
215
- attachPlatformer3D(handle: number, opts: any): boolean;
365
+ attachShooter2D(handle: number, opts: StageShooter2DOptions): boolean;
366
+ attachScreenWrap(handle: number, opts: StageScreenWrapOptions): boolean;
367
+ queryScreenWrap(handle: number): {
368
+ fsm: number;
369
+ lastWrappedAxes: number;
370
+ } | null;
371
+ attachWorldBoundary(handle: number, dim: StageBoundaryDimValue, opts: StageWorldBoundaryOptions): boolean;
372
+ queryWorldBoundary(handle: number): {
373
+ hits: number;
374
+ clamped: [number, number, number];
375
+ } | null;
376
+ attachRicochet(handle: number, dim: StageRicochetDimValue, opts: StageRicochetOptions): boolean;
377
+ attachPlatformer3D(handle: number, opts: StagePlatformer3DOptions): boolean;
216
378
  setPlatformer3DInputAxes(handle: number, moveX: number, moveZ: number): void;
217
379
  setPlatformer3DInputButtons(handle: number, jump: boolean, run: boolean): void;
218
- attachFirstPerson(handle: number, opts: any): boolean;
219
- setFirstPersonInput(handle: number, input: any): void;
220
- attachJumper2D(handle: number, opts: any): boolean;
380
+ attachFirstPerson(handle: number, opts: StageFirstPersonOptions): boolean;
381
+ setFirstPersonInput(handle: number, input: StageFirstPersonInput): void;
382
+ attachJumper2D(handle: number, opts: StageJumper2DOptions): boolean;
221
383
  setJumper2DInput(handle: number, jumpPressed: boolean): void;
222
- attachJumper3D(handle: number, opts: any): boolean;
384
+ attachJumper3D(handle: number, opts: StageJumper3DOptions): boolean;
223
385
  setJumper3DInput(handle: number, jumpPressed: boolean): void;
224
386
  }
225
387
  /** Default options used when constructing a wasm-backed Stage runtime. */
@@ -229,21 +391,34 @@ interface WasmStageRuntimeOptions {
229
391
  imports?: WebAssembly.Imports;
230
392
  }
231
393
 
394
+ /**
395
+ * Structural stand-in for the engine world wrapper. The engine's world
396
+ * delegate exposes the raw Rapier world plus an entity lookup map; behaviors
397
+ * only rely on this slice.
398
+ *
399
+ * The map value is intentionally `unknown` (not the `GameEntity` stub class):
400
+ * the engine's concrete entity class is a different nominal type, and systems
401
+ * that iterate the map narrow entities themselves.
402
+ */
403
+ interface ZylemWorld {
404
+ /** Raw Rapier physics world (present when the Rapier path is active). */
405
+ world?: World;
406
+ collisionMap?: Map<string, unknown>;
407
+ }
408
+
232
409
  /**
233
410
  * BehaviorSystem Interface
234
411
  *
235
412
  * Legacy behavior systems reserved the first `update` argument for an ECS world
236
413
  * handle; it is always `undefined` now.
237
414
  */
238
- /** @deprecated Retained only for the first argument slot of `BehaviorSystem.update`. */
239
- type BehaviorEcsHandle = unknown;
240
415
  /**
241
416
  * A behavior system that processes entities with specific components.
242
417
  *
243
418
  * @example
244
419
  * ```typescript
245
- * class ThrusterMovementSystem implements BehaviorSystem {
246
- * update(_ecs: BehaviorEcsHandle | undefined, delta: number): void {
420
+ * class ThrusterSystem implements BehaviorSystem {
421
+ * update(_ecs: unknown, delta: number): void {
247
422
  * // Iterate behavior links, push intent into wasm runtime, etc.
248
423
  * }
249
424
  * }
@@ -253,11 +428,11 @@ interface BehaviorSystem {
253
428
  /** Optional eager initialization when a behavior link is registered on a live entity */
254
429
  attach?(link: BehaviorEntityLink): void;
255
430
  /** Called once per frame. The first argument is always `undefined` (reserved legacy slot). */
256
- update(ecs: BehaviorEcsHandle | undefined, delta: number): void;
431
+ update(ecs: unknown, delta: number): void;
257
432
  /** Optional cleanup when a behavior link is detached from a live entity */
258
433
  detach?(link: BehaviorEntityLink): void;
259
434
  /** Optional cleanup when stage is destroyed */
260
- destroy?(ecs?: BehaviorEcsHandle): void;
435
+ destroy?(ecs?: unknown): void;
261
436
  }
262
437
  /**
263
438
  * Runtime link between a spawned entity and one of its behavior refs.
@@ -277,7 +452,8 @@ interface BehaviorEntityLink {
277
452
  * unmigrated descriptors.
278
453
  */
279
454
  interface BehaviorSystemContext {
280
- world: any;
455
+ world: ZylemWorld | null;
456
+ /** Host scene wrapper (engine-specific); behaviors only call `add`/`remove`-style methods on it. */
281
457
  scene: any;
282
458
  /** Wasm-driven Stage runtime; omitted when the stage has no wasm module. */
283
459
  wasmStage?: WasmStageRuntime | null;
@@ -304,4 +480,4 @@ interface BehaviorSystemContext {
304
480
  */
305
481
  type BehaviorSystemFactory<T extends BehaviorSystem = BehaviorSystem> = (stage: BehaviorSystemContext) => T;
306
482
 
307
- export { type BaseBehaviorHandle as B, type CreateEntityFn as C, type DefineBehaviorConfig as D, GameEntity as G, StageBodyKind as S, type WasmStageRuntime as W, type BehaviorDescriptor as a, type BehaviorEcsHandle as b, type BehaviorEntityLink as c, type BehaviorHandle as d, type BehaviorRef as e, type BehaviorSystem as f, type BehaviorSystemContext as g, type BehaviorSystemFactory as h, type GameEntityOptions as i, StageBoundaryDim as j, StageEventType as k, StageRicochetDim as l, StageRicochetReflection as m, StageTopDownPlane as n, type WasmStageRuntimeOptions as o, defineBehavior as p };
483
+ export { StageTopDownPlane as A, type BaseBehaviorHandle as B, type CreateEntityFn as C, type DefineBehaviorConfig as D, type StageTopDownPlaneValue as E, type StageWorldBoundaryOptions as F, GameEntity as G, type VelocityIntentMode as H, type WasmStageRuntimeOptions as I, createTransformStore as J, defineBehavior as K, StageBodyKind as S, type TransformState as T, type VelocityIntent as V, type WasmStageRuntime as W, type ZylemWorld as Z, type BehaviorDescriptor as a, type BehaviorEntityLink as b, type BehaviorHandle as c, type BehaviorRef as d, type BehaviorSystem as e, type BehaviorSystemContext as f, type BehaviorSystemFactory as g, type GameEntityOptions as h, StageBoundaryDim as i, type StageBoundaryDimValue as j, StageEventType as k, type StageFirstPersonInput as l, type StageFirstPersonOptions as m, type StageJumper2DOptions as n, type StageJumper3DOptions as o, type StagePlatformer3DOptions as p, type StagePose as q, StageRicochetDim as r, type StageRicochetDimValue as s, type StageRicochetOptions as t, StageRicochetReflection as u, type StageRicochetReflectionValue as v, type StageScreenWrapOptions as w, type StageShooter2DOptions as x, type StageThrusterOptions as y, type StageTopDownOptions as z };
@@ -41,4 +41,4 @@ export {
41
41
  StageTopDownPlane,
42
42
  StageEventType
43
43
  };
44
- //# sourceMappingURL=chunk-Y6UJOUE5.js.map
44
+ //# sourceMappingURL=chunk-4V7TIRO2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/runtime/wasm-stage-runtime.ts"],"sourcesContent":["export const StageBodyKind = {\n\tDynamic: 0,\n\tStatic: 1,\n\tKinematicPosition: 2,\n\tKinematicVelocity: 3,\n} as const;\n\nexport const StageBoundaryDim = {\n\tTwo: 2,\n\tThree: 3,\n} as const;\n\nexport const StageRicochetDim = {\n\tTwo: 2,\n\tThree: 3,\n} as const;\n\nexport const StageRicochetReflection = {\n\tMirror: 0,\n\tAngled: 1,\n} as const;\n\nexport const StageTopDownPlane = {\n\tXy: 0,\n\tXz: 1,\n} as const;\n\nexport const StageEventType = {\n\tCollisionStarted: 1,\n\tCollisionStopped: 2,\n\tBoundaryHit: 3,\n\tRicochet: 4,\n\tRegionEntered: 5,\n\tJumpStarted: 6,\n\tLanded: 7,\n\tWrapped: 8,\n\tCooldownFired: 9,\n} as const;\n\nexport type StageBoundaryDimValue = (typeof StageBoundaryDim)[keyof typeof StageBoundaryDim];\nexport type StageRicochetDimValue = (typeof StageRicochetDim)[keyof typeof StageRicochetDim];\nexport type StageRicochetReflectionValue =\n\t(typeof StageRicochetReflection)[keyof typeof StageRicochetReflection];\nexport type StageTopDownPlaneValue = (typeof StageTopDownPlane)[keyof typeof StageTopDownPlane];\n\n/** Position + rotation snapshot for one runtime slot. */\nexport interface StagePose {\n\tposition: [number, number, number];\n\trotation: [number, number, number, number];\n}\n\nexport interface StageWorldBoundaryOptions {\n\ttop: number;\n\tbottom: number;\n\tleft: number;\n\tright: number;\n\tfront?: number;\n\tback?: number;\n\tpadding?: number;\n}\n\nexport interface StageScreenWrapOptions {\n\twidth: number;\n\theight: number;\n\tcenterX?: number;\n\tcenterY?: number;\n\tedgeThreshold?: number;\n}\n\nexport interface StageRicochetOptions {\n\tminSpeed: number;\n\tmaxSpeed: number;\n\tspeedMultiplier: number;\n\tmaxAngleDeg: number;\n\treflectionMode: StageRicochetReflectionValue;\n}\n\nexport interface StageThrusterOptions {\n\tmaxSpeed: number;\n\tacceleration: number;\n\tturnRateRadPerSec: number;\n\tlinearDamping: number;\n}\n\nexport interface StageTopDownOptions {\n\tplane: StageTopDownPlaneValue;\n\tspeed: number;\n\tfaceMovement?: boolean;\n}\n\nexport interface StageShooter2DOptions {\n\tfireRateHz: number;\n\tmuzzleOffsetX: number;\n\tmuzzleOffsetY: number;\n\tmuzzleSpeed: number;\n}\n\nexport interface StageJumper2DOptions {\n\tjumpForce: number;\n\tmaxJumps: number;\n\tjumpBufferTime: number;\n\tcoyoteTime: number;\n\tgravity: number;\n}\n\nexport interface StageJumper3DOptions {\n\tjumpForce: number;\n\tmaxJumps: number;\n\tgravity: number;\n}\n\nexport interface StagePlatformer3DOptions {\n\thalfHeight: number;\n\tradius: number;\n\twalkSpeed: number;\n\trunSpeed: number;\n\tjumpForce: number;\n\tmaxJumps: number;\n\tgravity: number;\n}\n\nexport interface StageFirstPersonOptions {\n\twalkSpeed: number;\n\trunSpeed: number;\n\teyeHeight: number;\n\tlookSensitivity: number;\n}\n\nexport interface StageFirstPersonInput {\n\tmoveX: number;\n\tmoveZ: number;\n\tlookYawDelta: number;\n\tlookPitchDelta: number;\n\trun: boolean;\n}\n\n/**\n * Typed FFI surface of the wasm unified Stage runtime that behaviors rely on.\n *\n * The concrete implementation lives in `@zylem/game-lib`\n * (`wasm-stage-runtime.ts`); this contract mirrors the slice of its methods\n * that behavior systems call so descriptors stay runtime-agnostic.\n */\nexport interface WasmStageRuntime {\n\tgetPose(handle: number, out?: StagePose): StagePose | null;\n\n\tattachThruster(handle: number, opts: StageThrusterOptions): boolean;\n\tsetThrusterInput(handle: number, thrust: number, rotate: number): void;\n\n\tattachTopDown(handle: number, opts: StageTopDownOptions): boolean;\n\tsetTopDownInput(handle: number, moveX: number, moveY: number): void;\n\n\tattachShooter2D(handle: number, opts: StageShooter2DOptions): boolean;\n\n\tattachScreenWrap(handle: number, opts: StageScreenWrapOptions): boolean;\n\tqueryScreenWrap(handle: number): { fsm: number; lastWrappedAxes: number } | null;\n\n\tattachWorldBoundary(\n\t\thandle: number,\n\t\tdim: StageBoundaryDimValue,\n\t\topts: StageWorldBoundaryOptions,\n\t): boolean;\n\tqueryWorldBoundary(handle: number): { hits: number; clamped: [number, number, number] } | null;\n\n\tattachRicochet(handle: number, dim: StageRicochetDimValue, opts: StageRicochetOptions): boolean;\n\n\tattachPlatformer3D(handle: number, opts: StagePlatformer3DOptions): boolean;\n\tsetPlatformer3DInputAxes(handle: number, moveX: number, moveZ: number): void;\n\tsetPlatformer3DInputButtons(handle: number, jump: boolean, run: boolean): void;\n\n\tattachFirstPerson(handle: number, opts: StageFirstPersonOptions): boolean;\n\tsetFirstPersonInput(handle: number, input: StageFirstPersonInput): void;\n\n\tattachJumper2D(handle: number, opts: StageJumper2DOptions): boolean;\n\tsetJumper2DInput(handle: number, jumpPressed: boolean): void;\n\n\tattachJumper3D(handle: number, opts: StageJumper3DOptions): boolean;\n\tsetJumper3DInput(handle: number, jumpPressed: boolean): void;\n}\n\n/** Default options used when constructing a wasm-backed Stage runtime. */\nexport interface WasmStageRuntimeOptions {\n\tinitialCapacity?: number;\n\tgravity?: readonly [number, number, number];\n\timports?: WebAssembly.Imports;\n}\n"],"mappings":";AAAO,IAAM,gBAAgB;AAAA,EAC5B,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,mBAAmB;AAAA,EACnB,mBAAmB;AACpB;AAEO,IAAM,mBAAmB;AAAA,EAC/B,KAAK;AAAA,EACL,OAAO;AACR;AAEO,IAAM,mBAAmB;AAAA,EAC/B,KAAK;AAAA,EACL,OAAO;AACR;AAEO,IAAM,0BAA0B;AAAA,EACtC,QAAQ;AAAA,EACR,QAAQ;AACT;AAEO,IAAM,oBAAoB;AAAA,EAChC,IAAI;AAAA,EACJ,IAAI;AACL;AAEO,IAAM,iBAAiB;AAAA,EAC7B,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,aAAa;AAAA,EACb,UAAU;AAAA,EACV,eAAe;AAAA,EACf,aAAa;AAAA,EACb,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,eAAe;AAChB;","names":[]}
@@ -41,4 +41,4 @@ export {
41
41
  createJumpInput3D,
42
42
  createJumpState3D
43
43
  };
44
- //# sourceMappingURL=chunk-DYLH6K7B.js.map
44
+ //# sourceMappingURL=chunk-4WH2ZN7P.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/behaviors/jumper-3d/components.ts"],"sourcesContent":["/**\n * Jumper 3D ECS Components\n *\n * Type definitions and factory functions for the 3D jump behavior system.\n *\n * - JumpConfig3D: tunable parameters (stored as the capability component)\n * - JumpInput3D: per-tick intent written by the player/AI controller\n * - JumpContext3D: per-tick snapshot provided by the system adapter\n * - JumpState3D: internal runtime state owned by the behavior\n */\n\nimport type { Vector3 } from 'three';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Vec3-like interface for cross-library compatibility\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface Vec3Like {\n\tx: number;\n\ty: number;\n\tz: number;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// JumpConfig3D (capability / configuration)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface JumpConfig3D {\n\t/** Desired peak height in world units */\n\tjumpHeight: number;\n\t/** Gravity magnitude (positive, e.g. 20) */\n\tgravity: number;\n\t/** Terminal fall speed magnitude (optional) */\n\tmaxFallSpeed?: number;\n\n\t/** 1 = single jump, 2 = double, etc. */\n\tmaxJumps: number;\n\t/** Reset jump counter when grounded (usually true) */\n\tresetJumpsOnGround: boolean;\n\t/** Reset jump counter on wall contact (future wall-jump support) */\n\tresetJumpsOnWall?: boolean;\n\n\t/** Grace period after leaving ground where first jump is still allowed (ms) */\n\tcoyoteTimeMs: number;\n\t/** Queue a jump press this many ms before landing (ms) */\n\tjumpBufferMs: number;\n\t/** Optional cooldown between consecutive jumps (ms) */\n\tminTimeBetweenJumpsMs?: number;\n\n\t/** Variable jump height via early release */\n\tvariableJump?: {\n\t\tenabled: boolean;\n\t\t/** Gravity multiplier applied when jump is released early while ascending */\n\t\tcutGravityMultiplier: number;\n\t\t/** Max hold duration before the cut triggers automatically (ms, optional) */\n\t\tmaxHoldMs?: number;\n\t};\n\n\t/** Optional planar (XZ) launch / air-control overrides */\n\tplanar?: {\n\t\t/** One-time planar speed applied at takeoff */\n\t\tlaunchPlanarSpeed?: number;\n\t\t/** If true, preserve current planar speed instead of replacing it */\n\t\tpreservePlanarSpeed?: boolean;\n\t\tairControl?: {\n\t\t\tmaxPlanarSpeed: number;\n\t\t\t/** Acceleration per second while airborne */\n\t\t\tacceleration: number;\n\t\t\t/** Deceleration per second when no input */\n\t\t\tdeceleration: number;\n\t\t};\n\t};\n\n\t/** Snappier descent tuning */\n\tfall?: {\n\t\t/** Gravity multiplier while falling (e.g. 1.5-2.5) */\n\t\tfallGravityMultiplier: number;\n\t\t/** Additional multiplier when fast-fall input is held */\n\t\tfastFallMultiplier?: number;\n\t};\n}\n\nexport function createJumpConfig3D(\n\toptions: Partial<JumpConfig3D> = {},\n): JumpConfig3D {\n\treturn {\n\t\tjumpHeight: options.jumpHeight ?? 2.5,\n\t\tgravity: options.gravity ?? 20,\n\t\tmaxFallSpeed: options.maxFallSpeed,\n\t\tmaxJumps: options.maxJumps ?? 1,\n\t\tresetJumpsOnGround: options.resetJumpsOnGround ?? true,\n\t\tresetJumpsOnWall: options.resetJumpsOnWall,\n\t\tcoyoteTimeMs: options.coyoteTimeMs ?? 100,\n\t\tjumpBufferMs: options.jumpBufferMs ?? 80,\n\t\tminTimeBetweenJumpsMs: options.minTimeBetweenJumpsMs,\n\t\tvariableJump: options.variableJump,\n\t\tplanar: options.planar,\n\t\tfall: options.fall,\n\t};\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// JumpInput3D (intent)\n// Written by: Player controller, AI controller\n// Read by: Jumper3DBehavior\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface JumpInput3D {\n\t/** True on the frame the jump button is first pressed (rising edge) */\n\tjumpPressed: boolean;\n\t/** True while the jump button is held down */\n\tjumpHeld: boolean;\n\t/** True on the frame the jump button is released (falling edge) */\n\tjumpReleased: boolean;\n\t/** Desired planar direction in world space (camera-relative), optional */\n\tmoveDirWorld?: Vec3Like;\n\t/** Hold to drop faster (e.g. crouch / down input) */\n\tfastFall?: boolean;\n}\n\nexport function createJumpInput3D(): JumpInput3D {\n\treturn {\n\t\tjumpPressed: false,\n\t\tjumpHeld: false,\n\t\tjumpReleased: false,\n\t\tmoveDirWorld: undefined,\n\t\tfastFall: false,\n\t};\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// JumpContext3D (per-tick snapshot from entity / controller)\n// Built by the system adapter — never written to by user code.\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface JumpContext3D {\n\t/** Frame delta in seconds */\n\tdt: number;\n\t/** World up direction (usually 0,1,0) */\n\tup: Vec3Like;\n\t/** Current vertical (Y) velocity of the entity */\n\tvelocityY: number;\n\t/** Current horizontal velocity of the entity (read from body) */\n\thorizontalVelocity: { x: number; z: number };\n\t/** Whether the entity is on the ground */\n\tisGrounded: boolean;\n\t/** Surface normal at ground contact (optional) */\n\tgroundNormal?: Vec3Like;\n\t/** Time since entity was last grounded (ms) */\n\ttimeSinceGroundedMs: number;\n\n\t/** Set only the vertical (Y) component of velocity */\n\tsetVerticalVelocity(y: number): void;\n\t/** Set only the horizontal (X/Z) components of velocity */\n\tsetHorizontalVelocity(x: number, z: number): void;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// JumpState3D (internal runtime state)\n// Owned and mutated exclusively by Jumper3DBehavior.tick()\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface JumpState3D {\n\t/** Number of jumps consumed since last reset */\n\tjumpsUsed: number;\n\t/** Timestamp (ms) of the most recent jump execution */\n\tlastJumpTimeMs: number;\n\t/** Remaining ms in the jump-buffer window (counts down) */\n\tbufferedJumpMs: number;\n\t/** Remaining ms in the coyote-time window (counts down) */\n\tcoyoteMs: number;\n\t/** True while the entity is in a jump arc (ascending phase) */\n\tisJumping: boolean;\n\t/** Elapsed ms since jump button was first held (for variable jump) */\n\tjumpHoldMs: number;\n\t/** Whether the variable-jump cut has already been applied this jump */\n\tjumpCutApplied: boolean;\n}\n\nexport function createJumpState3D(): JumpState3D {\n\treturn {\n\t\tjumpsUsed: 0,\n\t\tlastJumpTimeMs: 0,\n\t\tbufferedJumpMs: 0,\n\t\tcoyoteMs: 0,\n\t\tisJumping: false,\n\t\tjumpHoldMs: 0,\n\t\tjumpCutApplied: false,\n\t};\n}\n"],"mappings":";AAkFO,SAAS,mBACf,UAAiC,CAAC,GACnB;AACf,SAAO;AAAA,IACN,YAAY,QAAQ,cAAc;AAAA,IAClC,SAAS,QAAQ,WAAW;AAAA,IAC5B,cAAc,QAAQ;AAAA,IACtB,UAAU,QAAQ,YAAY;AAAA,IAC9B,oBAAoB,QAAQ,sBAAsB;AAAA,IAClD,kBAAkB,QAAQ;AAAA,IAC1B,cAAc,QAAQ,gBAAgB;AAAA,IACtC,cAAc,QAAQ,gBAAgB;AAAA,IACtC,uBAAuB,QAAQ;AAAA,IAC/B,cAAc,QAAQ;AAAA,IACtB,QAAQ,QAAQ;AAAA,IAChB,MAAM,QAAQ;AAAA,EACf;AACD;AAqBO,SAAS,oBAAiC;AAChD,SAAO;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,cAAc;AAAA,IACd,cAAc;AAAA,IACd,UAAU;AAAA,EACX;AACD;AAmDO,SAAS,oBAAiC;AAChD,SAAO;AAAA,IACN,WAAW;AAAA,IACX,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,IAChB,UAAU;AAAA,IACV,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,gBAAgB;AAAA,EACjB;AACD;","names":[]}
1
+ {"version":3,"sources":["../src/lib/behaviors/jumper-3d/components.ts"],"sourcesContent":["/**\n * Jumper 3D ECS Components\n *\n * Type definitions and factory functions for the 3D jump behavior system.\n *\n * - JumpConfig3D: tunable parameters (stored as the capability component)\n * - JumpInput3D: per-tick intent written by the player/AI controller\n * - JumpContext3D: per-tick snapshot provided by the system adapter\n * - JumpState3D: internal runtime state owned by the behavior\n */\n\nimport type { Vector3 } from 'three';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Vec3-like interface for cross-library compatibility\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface Vec3Like {\n\tx: number;\n\ty: number;\n\tz: number;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// JumpConfig3D (capability / configuration)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface JumpConfig3D {\n\t/** Desired peak height in world units */\n\tjumpHeight: number;\n\t/** Gravity magnitude (positive, e.g. 20) */\n\tgravity: number;\n\t/** Terminal fall speed magnitude (optional) */\n\tmaxFallSpeed?: number;\n\n\t/** 1 = single jump, 2 = double, etc. */\n\tmaxJumps: number;\n\t/** Reset jump counter when grounded (usually true) */\n\tresetJumpsOnGround: boolean;\n\t/** Reset jump counter on wall contact (future wall-jump support) */\n\tresetJumpsOnWall?: boolean;\n\n\t/** Grace period after leaving ground where first jump is still allowed (ms) */\n\tcoyoteTimeMs: number;\n\t/** Queue a jump press this many ms before landing (ms) */\n\tjumpBufferMs: number;\n\t/** Optional cooldown between consecutive jumps (ms) */\n\tminTimeBetweenJumpsMs?: number;\n\n\t/** Variable jump height via early release */\n\tvariableJump?: {\n\t\tenabled: boolean;\n\t\t/** Gravity multiplier applied when jump is released early while ascending */\n\t\tcutGravityMultiplier: number;\n\t\t/** Max hold duration before the cut triggers automatically (ms, optional) */\n\t\tmaxHoldMs?: number;\n\t};\n\n\t/** Optional planar (XZ) launch / air-control overrides */\n\tplanar?: {\n\t\t/** One-time planar speed applied at takeoff */\n\t\tlaunchPlanarSpeed?: number;\n\t\t/** If true, preserve current planar speed instead of replacing it */\n\t\tpreservePlanarSpeed?: boolean;\n\t\tairControl?: {\n\t\t\tmaxPlanarSpeed: number;\n\t\t\t/** Acceleration per second while airborne */\n\t\t\tacceleration: number;\n\t\t\t/** Deceleration per second when no input */\n\t\t\tdeceleration: number;\n\t\t};\n\t};\n\n\t/** Snappier descent tuning */\n\tfall?: {\n\t\t/** Gravity multiplier while falling (e.g. 1.5-2.5) */\n\t\tfallGravityMultiplier: number;\n\t\t/** Additional multiplier when fast-fall input is held */\n\t\tfastFallMultiplier?: number;\n\t};\n}\n\nexport function createJumpConfig3D(\n\toptions: Partial<JumpConfig3D> = {},\n): JumpConfig3D {\n\treturn {\n\t\tjumpHeight: options.jumpHeight ?? 2.5,\n\t\tgravity: options.gravity ?? 20,\n\t\tmaxFallSpeed: options.maxFallSpeed,\n\t\tmaxJumps: options.maxJumps ?? 1,\n\t\tresetJumpsOnGround: options.resetJumpsOnGround ?? true,\n\t\tresetJumpsOnWall: options.resetJumpsOnWall,\n\t\tcoyoteTimeMs: options.coyoteTimeMs ?? 100,\n\t\tjumpBufferMs: options.jumpBufferMs ?? 80,\n\t\tminTimeBetweenJumpsMs: options.minTimeBetweenJumpsMs,\n\t\tvariableJump: options.variableJump,\n\t\tplanar: options.planar,\n\t\tfall: options.fall,\n\t};\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// JumpInput3D (intent)\n// Written by: Player controller, AI controller\n// Read by: Jumper3DSystem\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface JumpInput3D {\n\t/** True on the frame the jump button is first pressed (rising edge) */\n\tjumpPressed: boolean;\n\t/** True while the jump button is held down */\n\tjumpHeld: boolean;\n\t/** True on the frame the jump button is released (falling edge) */\n\tjumpReleased: boolean;\n\t/** Desired planar direction in world space (camera-relative), optional */\n\tmoveDirWorld?: Vec3Like;\n\t/** Hold to drop faster (e.g. crouch / down input) */\n\tfastFall?: boolean;\n}\n\nexport function createJumpInput3D(): JumpInput3D {\n\treturn {\n\t\tjumpPressed: false,\n\t\tjumpHeld: false,\n\t\tjumpReleased: false,\n\t\tmoveDirWorld: undefined,\n\t\tfastFall: false,\n\t};\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// JumpContext3D (per-tick snapshot from entity / controller)\n// Built by the system adapter — never written to by user code.\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface JumpContext3D {\n\t/** Frame delta in seconds */\n\tdt: number;\n\t/** World up direction (usually 0,1,0) */\n\tup: Vec3Like;\n\t/** Current vertical (Y) velocity of the entity */\n\tvelocityY: number;\n\t/** Current horizontal velocity of the entity (read from body) */\n\thorizontalVelocity: { x: number; z: number };\n\t/** Whether the entity is on the ground */\n\tisGrounded: boolean;\n\t/** Surface normal at ground contact (optional) */\n\tgroundNormal?: Vec3Like;\n\t/** Time since entity was last grounded (ms) */\n\ttimeSinceGroundedMs: number;\n\n\t/** Set only the vertical (Y) component of velocity */\n\tsetVerticalVelocity(y: number): void;\n\t/** Set only the horizontal (X/Z) components of velocity */\n\tsetHorizontalVelocity(x: number, z: number): void;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// JumpState3D (internal runtime state)\n// Owned and mutated exclusively by Jumper3DSystem.tick()\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface JumpState3D {\n\t/** Number of jumps consumed since last reset */\n\tjumpsUsed: number;\n\t/** Timestamp (ms) of the most recent jump execution */\n\tlastJumpTimeMs: number;\n\t/** Remaining ms in the jump-buffer window (counts down) */\n\tbufferedJumpMs: number;\n\t/** Remaining ms in the coyote-time window (counts down) */\n\tcoyoteMs: number;\n\t/** True while the entity is in a jump arc (ascending phase) */\n\tisJumping: boolean;\n\t/** Elapsed ms since jump button was first held (for variable jump) */\n\tjumpHoldMs: number;\n\t/** Whether the variable-jump cut has already been applied this jump */\n\tjumpCutApplied: boolean;\n}\n\nexport function createJumpState3D(): JumpState3D {\n\treturn {\n\t\tjumpsUsed: 0,\n\t\tlastJumpTimeMs: 0,\n\t\tbufferedJumpMs: 0,\n\t\tcoyoteMs: 0,\n\t\tisJumping: false,\n\t\tjumpHoldMs: 0,\n\t\tjumpCutApplied: false,\n\t};\n}\n"],"mappings":";AAkFO,SAAS,mBACf,UAAiC,CAAC,GACnB;AACf,SAAO;AAAA,IACN,YAAY,QAAQ,cAAc;AAAA,IAClC,SAAS,QAAQ,WAAW;AAAA,IAC5B,cAAc,QAAQ;AAAA,IACtB,UAAU,QAAQ,YAAY;AAAA,IAC9B,oBAAoB,QAAQ,sBAAsB;AAAA,IAClD,kBAAkB,QAAQ;AAAA,IAC1B,cAAc,QAAQ,gBAAgB;AAAA,IACtC,cAAc,QAAQ,gBAAgB;AAAA,IACtC,uBAAuB,QAAQ;AAAA,IAC/B,cAAc,QAAQ;AAAA,IACtB,QAAQ,QAAQ;AAAA,IAChB,MAAM,QAAQ;AAAA,EACf;AACD;AAqBO,SAAS,oBAAiC;AAChD,SAAO;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,cAAc;AAAA,IACd,cAAc;AAAA,IACd,UAAU;AAAA,EACX;AACD;AAmDO,SAAS,oBAAiC;AAChD,SAAO;AAAA,IACN,WAAW;AAAA,IACX,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,IAChB,UAAU;AAAA,IACV,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,gBAAgB;AAAA,EACjB;AACD;","names":[]}
@@ -9,8 +9,7 @@ import {
9
9
  } from "./chunk-7JJEIADR.js";
10
10
  import {
11
11
  VEC3_ZERO,
12
- normalizeVec3,
13
- toThreeVector3
12
+ normalizeVec3
14
13
  } from "./chunk-WWVD5DR5.js";
15
14
  import {
16
15
  defineBehavior
@@ -80,43 +79,44 @@ function getParticleControlState(ref) {
80
79
  function shouldCreateRuntime(state) {
81
80
  return state.runtime == null && (state.desiredState !== "stopped" || state.pendingRestart);
82
81
  }
82
+ var _scratchLocalOffset = new Vector3();
83
+ var _hostTransform = {
84
+ position: new Vector3(),
85
+ quaternion: new Quaternion()
86
+ };
83
87
  function getLocalOffset(options) {
84
- return toThreeVector3(options.localOffset, VEC3_ZERO);
88
+ const offset = normalizeVec3(options.localOffset, VEC3_ZERO);
89
+ return _scratchLocalOffset.set(offset.x, offset.y, offset.z);
85
90
  }
86
91
  function readHostTransform(entity) {
87
92
  const wasmPose = entity.getPose?.();
88
93
  if (wasmPose && entity.runtimeHandle >= 0) {
89
- return {
90
- position: new Vector3(wasmPose.position.x, wasmPose.position.y, wasmPose.position.z),
91
- quaternion: new Quaternion(
92
- wasmPose.rotation.x,
93
- wasmPose.rotation.y,
94
- wasmPose.rotation.z,
95
- wasmPose.rotation.w
96
- )
97
- };
94
+ _hostTransform.position.set(wasmPose.position.x, wasmPose.position.y, wasmPose.position.z);
95
+ _hostTransform.quaternion.set(
96
+ wasmPose.rotation.x,
97
+ wasmPose.rotation.y,
98
+ wasmPose.rotation.z,
99
+ wasmPose.rotation.w
100
+ );
101
+ return _hostTransform;
98
102
  }
99
103
  if (entity.body?.translation) {
100
104
  const position2 = entity.body.translation();
101
105
  const rotation = entity.body.rotation?.() ?? { x: 0, y: 0, z: 0, w: 1 };
102
- return {
103
- position: new Vector3(position2.x, position2.y, position2.z),
104
- quaternion: new Quaternion(rotation.x, rotation.y, rotation.z, rotation.w)
105
- };
106
+ _hostTransform.position.set(position2.x, position2.y, position2.z);
107
+ _hostTransform.quaternion.set(rotation.x, rotation.y, rotation.z, rotation.w);
108
+ return _hostTransform;
106
109
  }
107
110
  const visualRoot = entity.group ?? entity.mesh;
108
111
  if (visualRoot) {
109
- const position2 = new Vector3();
110
- const quaternion = new Quaternion();
111
- visualRoot.getWorldPosition(position2);
112
- visualRoot.getWorldQuaternion(quaternion);
113
- return { position: position2, quaternion };
112
+ visualRoot.getWorldPosition(_hostTransform.position);
113
+ visualRoot.getWorldQuaternion(_hostTransform.quaternion);
114
+ return _hostTransform;
114
115
  }
115
116
  const position = normalizeVec3(entity.options?.position, VEC3_ZERO);
116
- return {
117
- position: new Vector3(position.x, position.y, position.z),
118
- quaternion: new Quaternion()
119
- };
117
+ _hostTransform.position.set(position.x, position.y, position.z);
118
+ _hostTransform.quaternion.identity();
119
+ return _hostTransform;
120
120
  }
121
121
  function syncRuntimeTransform(entity, ref, runtime) {
122
122
  const current = readHostTransform(entity);
@@ -410,4 +410,4 @@ export {
410
410
  ParticleEmitterBehavior,
411
411
  particlePresets
412
412
  };
413
- //# sourceMappingURL=chunk-ZPSAXX73.js.map
413
+ //# sourceMappingURL=chunk-5SM5SH5O.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/behaviors/particle-emitter/particle-emitter.descriptor.ts","../src/lib/behaviors/particle-emitter/particle-effect.ts"],"sourcesContent":["import { Group, Object3D, Quaternion, Vector3 } from 'three';\n\nimport { Particles } from './gpu';\nimport { defineBehavior, type BehaviorRef } from '../behavior-descriptor';\nimport type { BehaviorEntityLink, BehaviorSystem } from '../behavior-system';\nimport { VEC3_ZERO, type Vec3Input, normalizeVec3 } from '../../core/vector';\nfunction getRefGlobals(\n\tref: BehaviorRef<ParticleEmitterBehaviorOptions>,\n): (() => any) | null {\n\treturn ((ref as any).__getGlobals ?? null) as (() => any) | null;\n}\nimport type { ParticleEffectDefinition } from './particle-effect';\n\nexport interface ParticleEmitterBehaviorOptions {\n\teffect: ParticleEffectDefinition;\n\tautoplay: boolean;\n\tlocalOffset?: Vec3Input;\n\tfollowPosition: boolean;\n\tfollowRotation: boolean;\n\tautoDestroy: boolean;\n}\n\nexport interface ParticleEmitterHandle {\n\tplay(): void;\n\tstop(): void;\n\tpause(): void;\n\trestart(): void;\n\tburst(): void;\n\tisPlaying(): boolean;\n\tgetSystem(): Particles | null;\n}\n\ntype DesiredParticleState = 'playing' | 'paused' | 'stopped';\n\ninterface ParticleEmitterControlState {\n\tdesiredState: DesiredParticleState;\n\tpendingRestart: boolean;\n\truntime: ParticleEmitterRuntime | null;\n}\n\ninterface ParticleEmitterRuntime {\n\tsystem: Particles;\n\tanchor: Group;\n\tservice: ParticleRendererService;\n\tinitialPosition: Vector3;\n\tinitialQuaternion: Quaternion;\n\t/** Whether the system has been started at least once in this lifecycle. */\n\tstarted: boolean;\n}\n\ninterface ParticleEmitterHostEntity {\n\tuuid: string;\n\tname?: string;\n\tbody?: {\n\t\ttranslation(): { x: number; y: number; z: number };\n\t\trotation?(): { x: number; y: number; z: number; w: number };\n\t} | null;\n\tgroup?: Object3D | null;\n\tmesh?: Object3D | null;\n\toptions?: {\n\t\tposition?: Vec3Input;\n\t};\n\tmarkedForRemoval?: boolean;\n\tnodeDestroy?(params: { me: unknown; globals: unknown }): void;\n\t__zylemParticleSystemEntity?: boolean;\n}\n\nconst PARTICLE_EMITTER_BEHAVIOR_KEY = Symbol.for(\n\t'zylem:behavior:particle-emitter',\n);\n\nconst DEFAULT_PARTICLE_EFFECT: ParticleEffectDefinition = {\n\tcreate() {\n\t\tthrow new Error(\n\t\t\t'ParticleEmitterBehavior requires an effect definition created with particleEffect(...).',\n\t\t);\n\t},\n};\n\nconst defaultOptions: ParticleEmitterBehaviorOptions = {\n\teffect: DEFAULT_PARTICLE_EFFECT,\n\tautoplay: true,\n\tlocalOffset: undefined,\n\tfollowPosition: true,\n\tfollowRotation: true,\n\tautoDestroy: false,\n};\n\n/**\n * Ticks the GPU particle systems each frame.\n *\n * The previous backend (three.quarks) required a `BatchedRenderer` Object3D in\n * the scene graph; the WebGPU engine instead exposes `Particles` Object3Ds that\n * self-render and only need `update(delta)` called for CPU-side emission.\n */\nclass ParticleRendererService {\n\tprivate readonly systems = new Set<Particles>();\n\n\tconstructor(private readonly sceneRoot: Object3D) {}\n\n\t/** Scene node that emitter anchors are parented to. */\n\tget root(): Object3D {\n\t\treturn this.sceneRoot;\n\t}\n\n\tregister(system: Particles): void {\n\t\tthis.systems.add(system);\n\t}\n\n\tforget(system: Particles): void {\n\t\tthis.systems.delete(system);\n\t}\n\n\tupdate(delta: number): void {\n\t\tfor (const system of this.systems) {\n\t\t\tsystem.update(delta);\n\t\t}\n\t}\n\n\tdestroy(): void {\n\t\tthis.systems.clear();\n\t}\n}\n\nfunction getParticleControlState(\n\tref: BehaviorRef<ParticleEmitterBehaviorOptions>,\n): ParticleEmitterControlState {\n\tconst runtimeState = (ref as any).__particleEmitterControlState as\n\t\t| ParticleEmitterControlState\n\t\t| undefined;\n\tif (runtimeState) {\n\t\treturn runtimeState;\n\t}\n\n\tconst nextState: ParticleEmitterControlState = {\n\t\tdesiredState: ref.options.autoplay ? 'playing' : 'stopped',\n\t\tpendingRestart: false,\n\t\truntime: null,\n\t};\n\t(ref as any).__particleEmitterControlState = nextState;\n\treturn nextState;\n}\n\nfunction shouldCreateRuntime(\n\tstate: ParticleEmitterControlState,\n): boolean {\n\treturn state.runtime == null\n\t\t&& (state.desiredState !== 'stopped' || state.pendingRestart);\n}\n\n// Module-level scratch objects reused every frame by readHostTransform /\n// getLocalOffset. This runs per emitter per frame, so allocating fresh\n// Vector3/Quaternion instances here was the library's worst GC hotspot.\n// Callers must copy/clone the returned values if they retain them.\nconst _scratchLocalOffset = new Vector3();\nconst _hostTransform = {\n\tposition: new Vector3(),\n\tquaternion: new Quaternion(),\n};\n\nfunction getLocalOffset(options: ParticleEmitterBehaviorOptions): Vector3 {\n\tconst offset = normalizeVec3(options.localOffset, VEC3_ZERO);\n\treturn _scratchLocalOffset.set(offset.x, offset.y, offset.z);\n}\n\nfunction readHostTransform(entity: ParticleEmitterHostEntity): {\n\tposition: Vector3;\n\tquaternion: Quaternion;\n} {\n\tconst wasmPose = (entity as any).getPose?.();\n\tif (wasmPose && (entity as any).runtimeHandle >= 0) {\n\t\t_hostTransform.position.set(wasmPose.position.x, wasmPose.position.y, wasmPose.position.z);\n\t\t_hostTransform.quaternion.set(\n\t\t\twasmPose.rotation.x,\n\t\t\twasmPose.rotation.y,\n\t\t\twasmPose.rotation.z,\n\t\t\twasmPose.rotation.w,\n\t\t);\n\t\treturn _hostTransform;\n\t}\n\n\tif (entity.body?.translation) {\n\t\tconst position = entity.body.translation();\n\t\tconst rotation = entity.body.rotation?.() ?? { x: 0, y: 0, z: 0, w: 1 };\n\t\t_hostTransform.position.set(position.x, position.y, position.z);\n\t\t_hostTransform.quaternion.set(rotation.x, rotation.y, rotation.z, rotation.w);\n\t\treturn _hostTransform;\n\t}\n\n\tconst visualRoot = entity.group ?? entity.mesh;\n\tif (visualRoot) {\n\t\tvisualRoot.getWorldPosition(_hostTransform.position);\n\t\tvisualRoot.getWorldQuaternion(_hostTransform.quaternion);\n\t\treturn _hostTransform;\n\t}\n\n\tconst position = normalizeVec3(entity.options?.position, VEC3_ZERO);\n\t_hostTransform.position.set(position.x, position.y, position.z);\n\t_hostTransform.quaternion.identity();\n\treturn _hostTransform;\n}\n\nfunction syncRuntimeTransform(\n\tentity: ParticleEmitterHostEntity,\n\tref: BehaviorRef<ParticleEmitterBehaviorOptions>,\n\truntime: ParticleEmitterRuntime,\n): void {\n\tconst current = readHostTransform(entity);\n\tconst options = ref.options;\n\tconst basePosition = options.followPosition\n\t\t? current.position\n\t\t: runtime.initialPosition;\n\tconst baseQuaternion = options.followRotation\n\t\t? current.quaternion\n\t\t: runtime.initialQuaternion;\n\tconst offset = getLocalOffset(options);\n\n\tif (options.followRotation) {\n\t\toffset.applyQuaternion(baseQuaternion);\n\t}\n\n\truntime.anchor.position.copy(basePosition).add(offset);\n\truntime.anchor.quaternion.copy(baseQuaternion);\n}\n\nfunction applyRuntimeState(\n\tref: BehaviorRef<ParticleEmitterBehaviorOptions>,\n): void {\n\tconst state = getParticleControlState(ref);\n\tconst runtime = state.runtime;\n\tif (!runtime) {\n\t\treturn;\n\t}\n\n\tif (state.pendingRestart) {\n\t\truntime.system.restart();\n\t\truntime.started = true;\n\t\tstate.pendingRestart = false;\n\n\t\tif (state.desiredState === 'paused') {\n\t\t\truntime.system.pause();\n\t\t} else if (state.desiredState === 'stopped') {\n\t\t\truntime.system.stop();\n\t\t\truntime.started = false;\n\t\t}\n\t\treturn;\n\t}\n\n\tif (state.desiredState === 'paused') {\n\t\truntime.system.pause();\n\t\treturn;\n\t}\n\n\tif (state.desiredState === 'stopped') {\n\t\truntime.system.stop();\n\t\truntime.started = false;\n\t\treturn;\n\t}\n\n\t// playing\n\tif (!runtime.started) {\n\t\truntime.system.start();\n\t\truntime.started = true;\n\t} else {\n\t\truntime.system.resume();\n\t}\n}\n\nfunction releaseRuntime(\n\tref: BehaviorRef<ParticleEmitterBehaviorOptions>,\n\treason: 'manual' | 'system-destroyed',\n): void {\n\tconst state = getParticleControlState(ref);\n\tconst runtime = state.runtime;\n\tif (!runtime) {\n\t\treturn;\n\t}\n\n\tstate.runtime = null;\n\tstate.pendingRestart = false;\n\n\truntime.service.forget(runtime.system);\n\n\tif (reason === 'manual') {\n\t\truntime.system.dispose();\n\t}\n\n\truntime.anchor.removeFromParent();\n}\n\nfunction handleSystemDestroyed(\n\tentity: ParticleEmitterHostEntity,\n\tref: BehaviorRef<ParticleEmitterBehaviorOptions>,\n): void {\n\treleaseRuntime(ref, 'system-destroyed');\n\tconst state = getParticleControlState(ref);\n\tstate.desiredState = 'stopped';\n\n\tif (\n\t\tref.options.autoDestroy\n\t\t&& entity.__zylemParticleSystemEntity\n\t\t&& !entity.markedForRemoval\n\t) {\n\t\tentity.nodeDestroy?.({ me: entity, globals: getRefGlobals(ref)?.() ?? {} });\n\t}\n}\n\nfunction createRuntime(\n\tentity: ParticleEmitterHostEntity,\n\tref: BehaviorRef<ParticleEmitterBehaviorOptions>,\n\tservice: ParticleRendererService,\n): ParticleEmitterRuntime {\n\tconst system = ref.options.effect.create();\n\tconst anchor = new Group();\n\tconst initialTransform = readHostTransform(entity);\n\tconst runtime: ParticleEmitterRuntime = {\n\t\tsystem,\n\t\tanchor,\n\t\tservice,\n\t\tinitialPosition: initialTransform.position.clone(),\n\t\tinitialQuaternion: initialTransform.quaternion.clone(),\n\t\tstarted: false,\n\t};\n\n\tanchor.name = `${entity.name || entity.uuid}:particle-anchor`;\n\tanchor.add(system);\n\tservice.register(system);\n\tservice.root.add(anchor);\n\n\tsyncRuntimeTransform(entity, ref, runtime);\n\treturn runtime;\n}\n\nfunction createParticleEmitterHandle(\n\tref: BehaviorRef<ParticleEmitterBehaviorOptions>,\n): ParticleEmitterHandle {\n\treturn {\n\t\tplay: () => {\n\t\t\tconst state = getParticleControlState(ref);\n\t\t\tstate.desiredState = 'playing';\n\t\t\tapplyRuntimeState(ref);\n\t\t},\n\t\tstop: () => {\n\t\t\tconst state = getParticleControlState(ref);\n\t\t\tstate.desiredState = 'stopped';\n\t\t\tstate.pendingRestart = false;\n\t\t\tapplyRuntimeState(ref);\n\t\t},\n\t\tpause: () => {\n\t\t\tconst state = getParticleControlState(ref);\n\t\t\tstate.desiredState = 'paused';\n\t\t\tapplyRuntimeState(ref);\n\t\t},\n\t\trestart: () => {\n\t\t\tconst state = getParticleControlState(ref);\n\t\t\tstate.desiredState = 'playing';\n\t\t\tstate.pendingRestart = true;\n\t\t\tapplyRuntimeState(ref);\n\t\t},\n\t\tburst: () => {\n\t\t\tconst state = getParticleControlState(ref);\n\t\t\tstate.desiredState = 'playing';\n\t\t\tstate.pendingRestart = true;\n\t\t\tapplyRuntimeState(ref);\n\t\t},\n\t\tisPlaying: () => {\n\t\t\tconst runtime = getParticleControlState(ref).runtime;\n\t\t\tif (!runtime) {\n\t\t\t\treturn getParticleControlState(ref).desiredState === 'playing';\n\t\t\t}\n\t\t\treturn runtime.system.running;\n\t\t},\n\t\tgetSystem: () => getParticleControlState(ref).runtime?.system ?? null,\n\t};\n}\n\nclass ParticleEmitterBehaviorSystem implements BehaviorSystem {\n\tprivate rendererService: ParticleRendererService | null = null;\n\n\tconstructor(\n\t\tprivate readonly scene: { scene: Object3D },\n\t\tprivate readonly getBehaviorLinks?: (key: symbol) => Iterable<BehaviorEntityLink>,\n\t\tprivate readonly getGlobals?: () => any,\n\t) {}\n\n\tprivate getRendererService(): ParticleRendererService {\n\t\tif (!this.rendererService) {\n\t\t\tthis.rendererService = new ParticleRendererService(this.scene.scene);\n\t\t}\n\t\treturn this.rendererService;\n\t}\n\n\tupdate(_ecs: unknown, delta: number): void {\n\t\tconst links = this.getBehaviorLinks?.(PARTICLE_EMITTER_BEHAVIOR_KEY);\n\t\tif (!links) {\n\t\t\treturn;\n\t\t}\n\n\t\tlet hasRuntime = false;\n\t\tlet rendererService = this.rendererService;\n\n\t\tfor (const link of links) {\n\t\t\tconst entity = link.entity as ParticleEmitterHostEntity;\n\t\t\tconst ref = link.ref as BehaviorRef<ParticleEmitterBehaviorOptions>;\n\t\t\t(ref as any).__getGlobals = this.getGlobals;\n\t\t\tconst state = getParticleControlState(ref);\n\n\t\t\tif (shouldCreateRuntime(state)) {\n\t\t\t\ttry {\n\t\t\t\t\trendererService ??= this.getRendererService();\n\t\t\t\t\tstate.runtime = createRuntime(entity, ref, rendererService);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tstate.desiredState = 'stopped';\n\t\t\t\t\tstate.pendingRestart = false;\n\t\t\t\t\tconsole.error('Failed to create particle runtime:', error);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!state.runtime) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\thasRuntime = true;\n\t\t\tsyncRuntimeTransform(entity, ref, state.runtime);\n\t\t\tapplyRuntimeState(ref);\n\n\t\t\tif (\n\t\t\t\tref.options.autoDestroy\n\t\t\t\t&& state.runtime.started\n\t\t\t\t&& state.desiredState === 'playing'\n\t\t\t\t&& state.runtime.system.isComplete()\n\t\t\t) {\n\t\t\t\thandleSystemDestroyed(entity, ref);\n\t\t\t}\n\t\t}\n\n\t\tif (hasRuntime && rendererService) {\n\t\t\trendererService.update(delta);\n\t\t}\n\t}\n\n\tdetach(link: BehaviorEntityLink): void {\n\t\treleaseRuntime(\n\t\t\tlink.ref as BehaviorRef<ParticleEmitterBehaviorOptions>,\n\t\t\t'manual',\n\t\t);\n\t}\n\n\tdestroy(_ecs: unknown): void {\n\t\tconst links = this.getBehaviorLinks?.(PARTICLE_EMITTER_BEHAVIOR_KEY);\n\t\tif (links) {\n\t\t\tfor (const link of links) {\n\t\t\t\treleaseRuntime(\n\t\t\t\t\tlink.ref as BehaviorRef<ParticleEmitterBehaviorOptions>,\n\t\t\t\t\t'manual',\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tthis.rendererService?.destroy();\n\t\tthis.rendererService = null;\n\t}\n}\n\nexport const ParticleEmitterBehavior = defineBehavior<\n\tParticleEmitterBehaviorOptions,\n\tParticleEmitterHandle\n>({\n\tname: 'particle-emitter',\n\tdefaultOptions,\n\tsystemFactory: (ctx) =>\n\t\tnew ParticleEmitterBehaviorSystem(ctx.scene, ctx.getBehaviorLinks, ctx.getGlobals),\n\tcreateHandle: createParticleEmitterHandle,\n});\n","import { AdditiveBlending, Color, type ColorRepresentation } from 'three';\n\nimport { Particles } from './gpu';\nimport type { ParticleSettings, ParticlesConfig, RenderSettings } from './gpu';\nimport {\n\tparticleEffect,\n\ttype ParticleEffectDefinition,\n\ttype ParticlePresetMaterialOptions,\n} from './preset-builder';\nimport { electricityPresets, firePresets, gasPresets, magicPresets, waterPresets } from './presets';\n\nexport type {\n\tParticleEffectDefinition,\n\tParticleMagicAgency,\n\tParticleMagicAlignment,\n\tParticleMagicModifierInput,\n\tParticleMagicModifierOptions,\n\tParticleMagicOrder,\n\tParticleMagicRealityEffect,\n\tParticleMagicTemperament,\n\tParticlePresetMaterialOptions,\n\tSemanticParticlePresetOptions,\n} from './preset-builder';\n\nexport interface BurstParticlePresetOptions extends ParticlePresetMaterialOptions {\n\tcolor?: ColorRepresentation;\n\tcount?: number;\n\tduration?: number;\n\tlife?: number | readonly [number, number];\n\tsize?: number | readonly [number, number];\n\tspeed?: number | readonly [number, number];\n\t/**\n\t * Kept for API compatibility. Emitter follow behavior is controlled by the\n\t * particle-emitter behavior (followPosition/followRotation), so this flag is\n\t * not used by the GPU engine.\n\t */\n\tworldSpace?: boolean;\n}\n\ninterface BurstDefaults {\n\tduration: number;\n\tlife: [number, number];\n\tspeed: [number, number];\n\tsize: [number, number];\n\tcolor: ColorRepresentation;\n\tcount: number;\n\tintensity: number;\n}\n\nfunction toRange(\n\tvalue: number | readonly [number, number] | undefined,\n\tfallback: [number, number],\n): [number, number] {\n\tif (value === undefined) return fallback;\n\tif (Array.isArray(value)) {\n\t\treturn [value[0] ?? fallback[0], value[1] ?? fallback[1]];\n\t}\n\tconst single = value as number;\n\treturn [single, single];\n}\n\nfunction toHexColor(color: ColorRepresentation): string {\n\treturn `#${new Color(color).getHexString()}`;\n}\n\nfunction makeBurstConfig(\n\toptions: BurstParticlePresetOptions,\n\tdefaults: BurstDefaults,\n): ParticlesConfig {\n\tconst count = Math.max(1, Math.round(options.count ?? defaults.count));\n\tconst color = toHexColor(options.color ?? defaults.color);\n\tconst blending = (options.blending ?? AdditiveBlending) as RenderSettings['blendingMode'];\n\n\tconst particles: Partial<ParticleSettings> & Pick<ParticleSettings, 'count'> = {\n\t\tcount,\n\t\tintensity: options.opacity ?? defaults.intensity,\n\t\tcolors: [\n\t\t\t[\n\t\t\t\t{ color, stop: 0 },\n\t\t\t\t{ color, stop: 1 },\n\t\t\t],\n\t\t],\n\t\tfadeAlpha: [0.05, 0.7],\n\t\tfadeSize: [0.1, 0.85],\n\t};\n\tif (options.texture) {\n\t\tparticles.alphaMapsStart = [options.texture] as unknown as ParticleSettings['alphaMapsStart'];\n\t}\n\n\treturn {\n\t\tparticles,\n\t\temitter: {\n\t\t\tduration: options.duration ?? defaults.duration,\n\t\t\tloop: false,\n\t\t\tspawnMode: 'burst',\n\t\t\trate: count,\n\t\t\tstatic: 0,\n\t\t\tlifetime: toRange(options.life, defaults.life),\n\t\t\tspeed: toRange(options.speed, defaults.speed),\n\t\t\tsize: toRange(options.size, defaults.size),\n\t\t\tdirectionMin: [-1, -1, -1],\n\t\t\tdirectionMax: [1, 1, 1],\n\t\t},\n\t\trender: { blendingMode: blending },\n\t};\n}\n\nexport const particlePresets = {\n\tburst(options: BurstParticlePresetOptions = {}): ParticleEffectDefinition {\n\t\tconst config = makeBurstConfig(options, {\n\t\t\tduration: 0.25,\n\t\t\tlife: [0.3, 0.7],\n\t\t\tspeed: [1.5, 4],\n\t\t\tsize: [0.08, 0.18],\n\t\t\tcolor: '#ffffff',\n\t\t\tcount: 18,\n\t\t\tintensity: 1,\n\t\t});\n\t\treturn particleEffect(() => new Particles(config));\n\t},\n\tsmoke(options: BurstParticlePresetOptions = {}): ParticleEffectDefinition {\n\t\tconst config = makeBurstConfig(options, {\n\t\t\tduration: 1.1,\n\t\t\tlife: [0.8, 1.6],\n\t\t\tspeed: [0.2, 0.8],\n\t\t\tsize: [0.2, 0.5],\n\t\t\tcolor: '#999999',\n\t\t\tcount: 10,\n\t\t\tintensity: 0.85,\n\t\t});\n\t\treturn particleEffect(() => new Particles(config));\n\t},\n\tfire: firePresets,\n\twater: waterPresets,\n\tgas: gasPresets,\n\telectricity: electricityPresets,\n\tmagic: magicPresets,\n} as const;\n\nexport { particleEffect } from './preset-builder';\n"],"mappings":"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","names":["position"]}
@@ -4,7 +4,7 @@ import {
4
4
  getRotationAngle2D,
5
5
  normalizeDirection2D,
6
6
  rotateLocalOffset2D
7
- } from "./chunk-ID5QJYF5.js";
7
+ } from "./chunk-Y7YIAWPW.js";
8
8
  import {
9
9
  normalizeVec3
10
10
  } from "./chunk-WWVD5DR5.js";
@@ -96,7 +96,9 @@ function createShooter2DHandle(ref) {
96
96
  me.transformStore.velocity.x = direction.x * options.projectileSpeed;
97
97
  me.transformStore.velocity.y = direction.y * options.projectileSpeed;
98
98
  me.transformStore.velocity.z = 0;
99
- me.transformStore.dirty.velocity = true;
99
+ me.transformStore.dirty.velocityX = true;
100
+ me.transformStore.dirty.velocityY = true;
101
+ me.transformStore.dirty.velocityZ = true;
100
102
  });
101
103
  stage.add(projectile);
102
104
  state.cooldownRemainingMs = Math.max(0, options.cooldownMs ?? 0);
@@ -157,4 +159,4 @@ export {
157
159
  createShooter2DStateComponent,
158
160
  Shooter2DBehavior
159
161
  };
160
- //# sourceMappingURL=chunk-STCCNLPC.js.map
162
+ //# sourceMappingURL=chunk-5XZWTFGF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/behaviors/shooter-2d/components.ts","../src/lib/behaviors/shooter-2d/shooter-2d.descriptor.ts"],"sourcesContent":["export interface Shooter2DStateComponent {\n\tcooldownRemainingMs: number;\n}\n\nexport function createShooter2DStateComponent(): Shooter2DStateComponent {\n\treturn {\n\t\tcooldownRemainingMs: 0,\n\t};\n}\n","import { defineBehavior, type BehaviorRef } from '../behavior-descriptor';\nimport type { BehaviorEntityLink, BehaviorSystem } from '../behavior-system';\nimport { normalizeVec3, type Vec3Input } from '../../core/vector';\nimport type { WasmStageRuntime } from '../../runtime/wasm-stage-runtime';\nimport {\n\tangleFromDirection2D,\n\tdirectionFromAngle2D,\n\tgetRotationAngle2D,\n\tnormalizeDirection2D,\n\trotateLocalOffset2D,\n} from '../shared/direction-2d';\nimport {\n\tcreateShooter2DStateComponent,\n\ttype Shooter2DStateComponent,\n} from './components';\n\nexport interface Shooter2DTarget {\n\tx: number;\n\ty: number;\n}\n\nexport interface Shooter2DStageLike {\n\tadd(...inputs: any[]): void;\n}\n\nexport interface Shooter2DProjectileEntity {\n\ttransformStore: {\n\t\tvelocity: { x: number; y: number; z: number };\n\t\tdirty: { velocityX: boolean; velocityY: boolean; velocityZ: boolean };\n\t};\n\tprependSetup(\n\t\tcallback: (params: { me: Shooter2DProjectileEntity }) => void,\n\t): unknown;\n\tsetPosition(x: number, y: number, z: number): void;\n\tsetRotationZ(angle: number): void;\n}\n\nexport type Shooter2DProjectileFactory = () =>\n\t| Shooter2DProjectileEntity\n\t| Promise<Shooter2DProjectileEntity>;\n\nexport interface Shooter2DBehaviorOptions {\n\tprojectileFactory: Shooter2DProjectileFactory;\n\tprojectileSpeed: number;\n\tcooldownMs?: number;\n\tspawnOffset?: Shooter2DTarget;\n\trotateProjectile?: boolean;\n}\n\nexport interface Shooter2DSourceEntity {\n\tbody?: {\n\t\ttranslation(): { x: number; y: number; z: number };\n\t\trotation?: () => { x: number; y: number; z: number; w: number };\n\t} | null;\n\toptions?: {\n\t\tposition?: Vec3Input;\n\t};\n\ttopDownMovementState?: {\n\t\tfacingAngle: number;\n\t};\n\tshooter2dState?: Shooter2DStateComponent;\n\t_rotation2DAngle?: number;\n}\n\nexport interface Shooter2DFireArgs {\n\tsource: Shooter2DSourceEntity;\n\tstage: Shooter2DStageLike;\n\ttarget: Shooter2DTarget;\n}\n\nexport interface Shooter2DHandle {\n\tisReady(source: Shooter2DSourceEntity): boolean;\n\tfire(args: Shooter2DFireArgs): Promise<Shooter2DProjectileEntity | null>;\n}\n\nconst defaultOptions: Shooter2DBehaviorOptions = {\n\tprojectileFactory: () => {\n\t\tthrow new Error('Shooter2DBehavior requires a projectileFactory.');\n\t},\n\tprojectileSpeed: 12,\n\tcooldownMs: 0,\n\tspawnOffset: { x: 0, y: 1 },\n\trotateProjectile: true,\n};\n\nconst SHOOTER_2D_BEHAVIOR_KEY = Symbol.for('zylem:behavior:shooter-2d');\n\nfunction ensureShooterState(entity: Shooter2DSourceEntity): Shooter2DStateComponent {\n\tif (!entity.shooter2dState) {\n\t\tentity.shooter2dState = createShooter2DStateComponent();\n\t}\n\treturn entity.shooter2dState;\n}\n\nfunction getEntityPosition(\n\tentity: Shooter2DSourceEntity,\n): { x: number; y: number; z: number } | null {\n\tif (entity.body?.translation) {\n\t\treturn entity.body.translation();\n\t}\n\n\tconst position = entity.options?.position;\n\tif (!position) {\n\t\treturn null;\n\t}\n\n\treturn normalizeVec3(position);\n}\n\nfunction getEntityFacingAngle(entity: Shooter2DSourceEntity): number {\n\tif (entity.topDownMovementState) {\n\t\treturn entity.topDownMovementState.facingAngle;\n\t}\n\n\tif (entity.body?.rotation) {\n\t\treturn getRotationAngle2D(entity.body.rotation());\n\t}\n\n\treturn entity._rotation2DAngle ?? 0;\n}\n\nfunction createShooter2DHandle(\n\tref: BehaviorRef<Shooter2DBehaviorOptions>,\n): Shooter2DHandle {\n\treturn {\n\t\tisReady: (source: Shooter2DSourceEntity) =>\n\t\t\tensureShooterState(source).cooldownRemainingMs <= 0,\n\t\tfire: async ({\n\t\t\tsource,\n\t\t\tstage,\n\t\t\ttarget,\n\t\t}: Shooter2DFireArgs): Promise<Shooter2DProjectileEntity | null> => {\n\t\t\tconst state = ensureShooterState(source);\n\t\t\tif (state.cooldownRemainingMs > 0) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst sourcePosition = getEntityPosition(source);\n\t\t\tif (!sourcePosition) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst options = ref.options;\n\t\t\tconst aimDirection = normalizeDirection2D(\n\t\t\t\ttarget.x - sourcePosition.x,\n\t\t\t\ttarget.y - sourcePosition.y,\n\t\t\t);\n\t\t\tconst angle = aimDirection\n\t\t\t\t? angleFromDirection2D(aimDirection)\n\t\t\t\t: getEntityFacingAngle(source);\n\t\t\tconst direction = aimDirection ?? directionFromAngle2D(angle);\n\t\t\tconst offset = rotateLocalOffset2D(\n\t\t\t\toptions.spawnOffset ?? defaultOptions.spawnOffset!,\n\t\t\t\tangle,\n\t\t\t);\n\t\t\tconst spawnPosition = {\n\t\t\t\tx: sourcePosition.x + offset.x,\n\t\t\t\ty: sourcePosition.y + offset.y,\n\t\t\t\tz: sourcePosition.z,\n\t\t\t};\n\t\t\tconst projectile = await Promise.resolve(options.projectileFactory());\n\t\t\tprojectile.prependSetup(({ me }) => {\n\t\t\t\tme.setPosition(spawnPosition.x, spawnPosition.y, spawnPosition.z);\n\t\t\t\tif (options.rotateProjectile ?? true) {\n\t\t\t\t\tme.setRotationZ(angle);\n\t\t\t\t}\n\t\t\t\tme.transformStore.velocity.x = direction.x * options.projectileSpeed;\n\t\t\t\tme.transformStore.velocity.y = direction.y * options.projectileSpeed;\n\t\t\t\tme.transformStore.velocity.z = 0;\n\t\t\t\tme.transformStore.dirty.velocityX = true;\n\t\t\t\tme.transformStore.dirty.velocityY = true;\n\t\t\t\tme.transformStore.dirty.velocityZ = true;\n\t\t\t});\n\n\t\t\tstage.add(projectile);\n\t\t\tstate.cooldownRemainingMs = Math.max(0, options.cooldownMs ?? 0);\n\t\t\treturn projectile;\n\t\t},\n\t};\n}\n\nclass Shooter2DBehaviorSystem implements BehaviorSystem {\n\tprivate attachedRuntimeSlots = new Set<number>();\n\n\tconstructor(\n\t\tprivate wasmStage: WasmStageRuntime | 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?.(SHOOTER_2D_BEHAVIOR_KEY);\n\t\tif (!links) return;\n\n\t\tfor (const link of links) {\n\t\t\tconst entity = link.entity as Shooter2DSourceEntity & { runtimeHandle?: number };\n\t\t\tconst ref = link.ref as any;\n\t\t\tconst state = ensureShooterState(entity);\n\t\t\tstate.cooldownRemainingMs = Math.max(\n\t\t\t\t0,\n\t\t\t\tstate.cooldownRemainingMs - delta * 1000,\n\t\t\t);\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) {\n\t\t\t\tthis.ensureWasmAttached(handle, ref.options as Shooter2DBehaviorOptions);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate ensureWasmAttached(handle: number, options: Shooter2DBehaviorOptions): void {\n\t\tif (!this.wasmStage || this.attachedRuntimeSlots.has(handle)) return;\n\t\tconst offset = options.spawnOffset ?? defaultOptions.spawnOffset!;\n\t\tconst fireRateHz = options.cooldownMs && options.cooldownMs > 0\n\t\t\t? 1000 / options.cooldownMs\n\t\t\t: 0;\n\t\tthis.wasmStage.attachShooter2D(handle, {\n\t\t\tfireRateHz,\n\t\t\tmuzzleOffsetX: offset.x,\n\t\t\tmuzzleOffsetY: offset.y,\n\t\t\tmuzzleSpeed: options.projectileSpeed,\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 Shooter2DBehavior = defineBehavior<\n\tShooter2DBehaviorOptions,\n\tShooter2DHandle,\n\tShooter2DSourceEntity\n>({\n\tname: 'shooter-2d',\n\tdefaultOptions,\n\tsystemFactory: (ctx) => new Shooter2DBehaviorSystem(ctx.wasmStage ?? null, ctx.getBehaviorLinks),\n\tcreateHandle: createShooter2DHandle,\n});\n"],"mappings":"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","names":[]}