@zylem/behaviors 0.3.0 → 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 (169) hide show
  1. package/LICENSE +21 -0
  2. package/dist/{behavior-system-DYbx-x_-.d.ts → behavior-system-DSNek0W9.d.ts} +205 -29
  3. package/dist/{chunk-RER2IDPW.js → chunk-4URF65BR.js} +1 -0
  4. package/dist/chunk-4URF65BR.js.map +1 -0
  5. package/dist/{chunk-YYJP62QD.js → chunk-4V7TIRO2.js} +1 -0
  6. package/dist/chunk-4V7TIRO2.js.map +1 -0
  7. package/dist/{chunk-F7E23E26.js → chunk-4WH2ZN7P.js} +1 -0
  8. package/dist/chunk-4WH2ZN7P.js.map +1 -0
  9. package/dist/{chunk-VPZ32CCD.js → chunk-5SM5SH5O.js} +29 -28
  10. package/dist/chunk-5SM5SH5O.js.map +1 -0
  11. package/dist/{chunk-L76JG73P.js → chunk-5XZWTFGF.js} +7 -4
  12. package/dist/chunk-5XZWTFGF.js.map +1 -0
  13. package/dist/{chunk-HIBJDA5Z.js → chunk-64HKCSXD.js} +21 -17
  14. package/dist/chunk-64HKCSXD.js.map +1 -0
  15. package/dist/{chunk-WSKD7GY3.js → chunk-7JJEIADR.js} +1 -0
  16. package/dist/chunk-7JJEIADR.js.map +1 -0
  17. package/dist/{chunk-F6LFX4V3.js → chunk-BXP4K7RF.js} +3 -2
  18. package/dist/chunk-BXP4K7RF.js.map +1 -0
  19. package/dist/{chunk-2HKY6WF7.js → chunk-EIWQM6U3.js} +1 -0
  20. package/dist/chunk-EIWQM6U3.js.map +1 -0
  21. package/dist/{chunk-YDLO5M7S.js → chunk-EZNCCO4L.js} +1 -0
  22. package/dist/chunk-EZNCCO4L.js.map +1 -0
  23. package/dist/{chunk-T2P4JTQN.js → chunk-FRJDQM23.js} +110 -155
  24. package/dist/chunk-FRJDQM23.js.map +1 -0
  25. package/dist/{chunk-FHDYJ5WP.js → chunk-FTDZAKZW.js} +5 -17
  26. package/dist/chunk-FTDZAKZW.js.map +1 -0
  27. package/dist/{chunk-D53GNZXP.js → chunk-GFARAJV2.js} +6 -7
  28. package/dist/chunk-GFARAJV2.js.map +1 -0
  29. package/dist/{chunk-IQRG5BAZ.js → chunk-GTLA6LOJ.js} +18 -23
  30. package/dist/chunk-GTLA6LOJ.js.map +1 -0
  31. package/dist/{chunk-VO5J64DG.js → chunk-H4TJ7X4O.js} +4 -6
  32. package/dist/chunk-H4TJ7X4O.js.map +1 -0
  33. package/dist/{chunk-6UMC6BB5.js → chunk-HNESASBB.js} +7 -8
  34. package/dist/chunk-HNESASBB.js.map +1 -0
  35. package/dist/{chunk-4DVDFCVY.js → chunk-HZMY3TT4.js} +1 -0
  36. package/dist/chunk-HZMY3TT4.js.map +1 -0
  37. package/dist/{chunk-2HLZBWGD.js → chunk-IZJFUAXB.js} +1 -0
  38. package/dist/chunk-IZJFUAXB.js.map +1 -0
  39. package/dist/{chunk-UFHYR2LV.js → chunk-JXMF4NUI.js} +51 -2
  40. package/dist/chunk-JXMF4NUI.js.map +1 -0
  41. package/dist/{chunk-ZHBD65AZ.js → chunk-KDY5N6J4.js} +1 -0
  42. package/dist/chunk-KDY5N6J4.js.map +1 -0
  43. package/dist/chunk-KFFETUAL.js +40 -0
  44. package/dist/chunk-KFFETUAL.js.map +1 -0
  45. package/dist/chunk-LL4FLHIY.js +12 -0
  46. package/dist/chunk-LL4FLHIY.js.map +1 -0
  47. package/dist/{chunk-BC3IJYS7.js → chunk-MJFMNRMZ.js} +15 -15
  48. package/dist/chunk-MJFMNRMZ.js.map +1 -0
  49. package/dist/{chunk-WHKPWIQO.js → chunk-OHCPV2NT.js} +1 -0
  50. package/dist/chunk-OHCPV2NT.js.map +1 -0
  51. package/dist/{chunk-RQWWUML4.js → chunk-OTLSUYQQ.js} +2 -1
  52. package/dist/chunk-OTLSUYQQ.js.map +1 -0
  53. package/dist/{chunk-E4HTCQPK.js → chunk-PWF757B6.js} +7 -8
  54. package/dist/chunk-PWF757B6.js.map +1 -0
  55. package/dist/{chunk-4EWASZHB.js → chunk-QVCMJ4Q2.js} +1 -0
  56. package/dist/chunk-QVCMJ4Q2.js.map +1 -0
  57. package/dist/{chunk-FOOM42MK.js → chunk-RONH2KDI.js} +12 -13
  58. package/dist/chunk-RONH2KDI.js.map +1 -0
  59. package/dist/chunk-RTRFJVCZ.js +454 -0
  60. package/dist/chunk-RTRFJVCZ.js.map +1 -0
  61. package/dist/{chunk-IEA7F73P.js → chunk-UL6Q5HDP.js} +25 -2
  62. package/dist/chunk-UL6Q5HDP.js.map +1 -0
  63. package/dist/{chunk-K6CWKFHL.js → chunk-UYFOLI2X.js} +1 -0
  64. package/dist/chunk-UYFOLI2X.js.map +1 -0
  65. package/dist/{chunk-RD2YTTXY.js → chunk-V74N7D6Q.js} +9 -8
  66. package/dist/chunk-V74N7D6Q.js.map +1 -0
  67. package/dist/{chunk-ERS6SNA5.js → chunk-WOMHL23C.js} +2 -1
  68. package/dist/chunk-WOMHL23C.js.map +1 -0
  69. package/dist/{chunk-RHW3IDQX.js → chunk-WWVD5DR5.js} +1 -0
  70. package/dist/chunk-WWVD5DR5.js.map +1 -0
  71. package/dist/{chunk-SBACY5SU.js → chunk-WXXXJ22R.js} +14 -15
  72. package/dist/chunk-WXXXJ22R.js.map +1 -0
  73. package/dist/{chunk-6Y3GB76J.js → chunk-XLUJDKCT.js} +9 -8
  74. package/dist/chunk-XLUJDKCT.js.map +1 -0
  75. package/dist/{chunk-7Y6JVZ5H.js → chunk-XP3ASVGJ.js} +14 -10
  76. package/dist/chunk-XP3ASVGJ.js.map +1 -0
  77. package/dist/{chunk-7U4WFBWM.js → chunk-Y7YIAWPW.js} +11 -0
  78. package/dist/chunk-Y7YIAWPW.js.map +1 -0
  79. package/dist/{chunk-7IZ36Y5G.js → chunk-ZA43LPRM.js} +5 -24
  80. package/dist/chunk-ZA43LPRM.js.map +1 -0
  81. package/dist/{chunk-J5XLB24H.js → chunk-ZRQWSURL.js} +3 -2
  82. package/dist/chunk-ZRQWSURL.js.map +1 -0
  83. package/dist/cooldown.d.ts +2 -1
  84. package/dist/cooldown.js +3 -2
  85. package/dist/cooldown.js.map +1 -0
  86. package/dist/coordinators/boundary-ricochet-3d.d.ts +12 -5
  87. package/dist/coordinators/boundary-ricochet-3d.js +4 -2
  88. package/dist/coordinators/boundary-ricochet-3d.js.map +1 -0
  89. package/dist/coordinators/boundary-ricochet.d.ts +9 -7
  90. package/dist/coordinators/boundary-ricochet.js +4 -2
  91. package/dist/coordinators/boundary-ricochet.js.map +1 -0
  92. package/dist/coordinators/first-person-shooter.d.ts +9 -8
  93. package/dist/coordinators/first-person-shooter.js +4 -3
  94. package/dist/coordinators/first-person-shooter.js.map +1 -0
  95. package/dist/coordinators/multidirectional-space-shooter.d.ts +12 -9
  96. package/dist/coordinators/multidirectional-space-shooter.js +5 -4
  97. package/dist/coordinators/multidirectional-space-shooter.js.map +1 -0
  98. package/dist/coordinators/top-down-shooter.d.ts +9 -8
  99. package/dist/coordinators/top-down-shooter.js +5 -4
  100. package/dist/coordinators/top-down-shooter.js.map +1 -0
  101. package/dist/core.d.ts +4 -4
  102. package/dist/core.js +9 -36
  103. package/dist/core.js.map +1 -0
  104. package/dist/destructible-3d.d.ts +2 -1
  105. package/dist/destructible-3d.js +4 -3
  106. package/dist/destructible-3d.js.map +1 -0
  107. package/dist/destructible-prebake.js +2 -1
  108. package/dist/destructible-prebake.js.map +1 -0
  109. package/dist/first-person.d.ts +12 -12
  110. package/dist/first-person.js +12 -11
  111. package/dist/first-person.js.map +1 -0
  112. package/dist/index.d.ts +37 -14
  113. package/dist/index.js +85 -55
  114. package/dist/index.js.map +1 -0
  115. package/dist/jumper-2d.d.ts +7 -6
  116. package/dist/jumper-2d.js +8 -7
  117. package/dist/jumper-2d.js.map +1 -0
  118. package/dist/jumper-3d.d.ts +12 -11
  119. package/dist/jumper-3d.js +9 -8
  120. package/dist/jumper-3d.js.map +1 -0
  121. package/dist/{moveable-BCRpjAQb.d.ts → moveable-BLTHO7Jq.d.ts} +2 -78
  122. package/dist/particle-emitter/presets.js +2 -1
  123. package/dist/particle-emitter/presets.js.map +1 -0
  124. package/dist/particle-emitter.d.ts +1 -1
  125. package/dist/particle-emitter.js +5 -4
  126. package/dist/particle-emitter.js.map +1 -0
  127. package/dist/platformer-2d.d.ts +306 -0
  128. package/dist/platformer-2d.js +23 -0
  129. package/dist/platformer-2d.js.map +1 -0
  130. package/dist/platformer-3d.d.ts +74 -87
  131. package/dist/platformer-3d.js +7 -6
  132. package/dist/platformer-3d.js.map +1 -0
  133. package/dist/ricochet-2d.d.ts +1 -1
  134. package/dist/ricochet-2d.js +5 -4
  135. package/dist/ricochet-2d.js.map +1 -0
  136. package/dist/ricochet-3d.d.ts +1 -1
  137. package/dist/ricochet-3d.js +5 -4
  138. package/dist/ricochet-3d.js.map +1 -0
  139. package/dist/runtime-2d.d.ts +93 -2
  140. package/dist/runtime-2d.js +146 -22
  141. package/dist/runtime-2d.js.map +1 -0
  142. package/dist/screen-visibility.d.ts +2 -1
  143. package/dist/screen-visibility.js +3 -2
  144. package/dist/screen-visibility.js.map +1 -0
  145. package/dist/screen-wrap.d.ts +2 -1
  146. package/dist/screen-wrap.js +5 -4
  147. package/dist/screen-wrap.js.map +1 -0
  148. package/dist/shooter-2d.d.ts +5 -3
  149. package/dist/shooter-2d.js +5 -4
  150. package/dist/shooter-2d.js.map +1 -0
  151. package/dist/shooter-fire-DGripl0z.d.ts +55 -0
  152. package/dist/thruster.d.ts +8 -18
  153. package/dist/thruster.js +9 -8
  154. package/dist/thruster.js.map +1 -0
  155. package/dist/top-down-movement.d.ts +7 -4
  156. package/dist/top-down-movement.js +8 -7
  157. package/dist/top-down-movement.js.map +1 -0
  158. package/dist/{use-behavior-D_82jAL3.d.ts → use-behavior-h9-T5FSv.d.ts} +1 -1
  159. package/dist/world-boundary-2d.d.ts +2 -1
  160. package/dist/world-boundary-2d.js +6 -5
  161. package/dist/world-boundary-2d.js.map +1 -0
  162. package/dist/world-boundary-3d.d.ts +2 -1
  163. package/dist/world-boundary-3d.js +6 -5
  164. package/dist/world-boundary-3d.js.map +1 -0
  165. package/package.json +8 -7
  166. package/dist/chunk-23HQH3GJ.js +0 -34
  167. package/dist/chunk-K4UBXSZL.js +0 -175
  168. package/dist/runtime-pong.d.ts +0 -92
  169. package/dist/runtime-pong.js +0 -37
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/behaviors/platformer-3d/components.ts","../src/lib/behaviors/platformer-3d/platformer-3d-fsm.ts","../src/lib/behaviors/platformer-3d/platformer-3d.system.ts","../src/lib/behaviors/platformer-3d/platformer-3d.descriptor.ts"],"sourcesContent":["/**\n * Platformer 3D ECS Components\n * \n * Components for 3D platformer movement system including walking, running,\n * jumping with multi-jump support, and falling/landing states.\n */\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Platformer3DMovementComponent (capability / configuration)\n// Defines the movement capabilities and physics parameters\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface Platformer3DMovementComponent {\n\t/** Base walking speed */\n\twalkSpeed: number;\n\t/** Sprint/run speed */\n\trunSpeed: number;\n\t/** Initial jump force/velocity */\n\tjumpForce: number;\n\t/** Maximum number of jumps (1 = single jump, 2 = double jump, etc.) */\n\tmaxJumps: number;\n\t/** Gravity force (optional override) */\n\tgravity: number;\n\t/** Coyote time in seconds (grace period after leaving ground) */\n\tcoyoteTime: number;\n\t/** Jump buffer time in seconds (queue jump input before landing) */\n\tjumpBufferTime: number;\n\t/** Velocity multiplier when releasing jump button early (0-1) */\n\tjumpCutMultiplier: number;\n\t/** Time in seconds before multi-jump becomes available (0 = after button release) */\n\tmultiJumpWindowTime: number;\n\t/** Maximum slope angle (degrees) the character can walk up */\n\tslopeClimbAngle: number;\n\t/** Slope angle (degrees) past which the character slides down */\n\tslopeSlideAngle: number;\n\t/** Autostep configuration for small ledges, or false to disable */\n\tautostep: { maxHeight: number; minWidth: number } | false;\n\t/** Snap-to-ground distance (keeps the character glued going downhill) */\n\tsnapToGroundDistance: number;\n\t/** Character controller offset (small gap kept between collider and environment) */\n\tcharacterOffset: number;\n}\n\nexport function createPlatformer3DMovementComponent(\n\toptions: Partial<Platformer3DMovementComponent> = {}\n): Platformer3DMovementComponent {\n\treturn {\n\t\twalkSpeed: options.walkSpeed ?? 12,\n\t\trunSpeed: options.runSpeed ?? 24,\n\t\tjumpForce: options.jumpForce ?? 12,\n\t\tmaxJumps: options.maxJumps ?? 1,\n\t\tgravity: options.gravity ?? 9.82,\n\t\tcoyoteTime: options.coyoteTime ?? 0.1,\n\t\tjumpBufferTime: options.jumpBufferTime ?? 0.1,\n\t\tjumpCutMultiplier: options.jumpCutMultiplier ?? 0.5,\n\t\tmultiJumpWindowTime: options.multiJumpWindowTime ?? 0.15, // 150ms default\n\t\tslopeClimbAngle: options.slopeClimbAngle ?? 50,\n\t\tslopeSlideAngle: options.slopeSlideAngle ?? 40,\n\t\tautostep: options.autostep ?? { maxHeight: 0.3, minWidth: 0.1 },\n\t\tsnapToGroundDistance: options.snapToGroundDistance ?? 0.3,\n\t\tcharacterOffset: options.characterOffset ?? 0.05,\n\t};\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Platformer3DInputComponent (intent)\n// Written by: Player controller, AI controller\n// Read by: Platformer3DBehavior\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface Platformer3DInputComponent {\n\t/** Horizontal movement input (-1 to 1) */\n\tmoveX: number;\n\t/** Forward/backward movement input (-1 to 1) */\n\tmoveZ: number;\n\t/** Jump button pressed */\n\tjump: boolean;\n\t/** Run/sprint button held */\n\trun: boolean;\n}\n\nexport function createPlatformer3DInputComponent(): Platformer3DInputComponent {\n\treturn {\n\t\tmoveX: 0,\n\t\tmoveZ: 0,\n\t\tjump: false,\n\t\trun: false,\n\t};\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Platformer3DStateComponent (runtime state)\n// Tracks the current state of the platformer entity\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface Platformer3DStateComponent {\n\t/** Is the entity on the ground */\n\tgrounded: boolean;\n\t/** Is currently in a jump arc */\n\tjumping: boolean;\n\t/** Is falling (not jumping) */\n\tfalling: boolean;\n\t/** Current jump count (for multi-jump) */\n\tjumpCount: number;\n\t/** Y position when jump started */\n\tjumpStartHeight: number;\n\t/** Current movement speed being applied */\n\tcurrentSpeed: number;\n\t/** Last known ground Y position */\n\tlastGroundedY: number;\n\t/** Was jump button pressed last frame (for edge detection) */\n\tjumpPressedLastFrame: boolean;\n\t\n\t// -- New fields --\n\t/** Is currently colliding with ground (from physics collision callback) */\n\tcollisionGrounded: boolean;\n\t/** Timestamp of last ground collision */\n\tgroundedCollisionTime: number;\n\t/** Time in seconds since last grounded */\n\ttimeSinceGrounded: number;\n\t/** Is a jump allowed due to buffer? */\n\tjumpBuffered: boolean;\n\t/** Time remaining in jump buffer */\n\tjumpBufferTimer: number;\n\t/** Is jump button currently held */\n\tjumpHeld: boolean;\n\t/** Has the jump cut been applied for this jump? */\n\tjumpCutApplied: boolean;\n\t/** Has jump button been released since last jump (required for multi-jump) */\n\tjumpReleasedSinceLastJump: boolean;\n\t/** Time since current jump started (for multi-jump window) */\n\ttimeSinceJump: number;\n\t/** True for the single frame on which a jump impulse was applied; prevents gravity from clobbering jump velocity. */\n\tjumpedThisFrame: boolean;\n\t/** Remaining seconds of jump-cut velocity ramp; while > 0, verticalVelocity lerps toward verticalVelocity * jumpCutMultiplier. */\n\tjumpCutRampTimer: number;\n\t/**\n\t * Controller-owned vertical velocity (units/second).\n\t * The body is kinematic, so `linvel()` is meaningless — this field is the\n\t * single source of truth for jump/gravity motion on the Y axis.\n\t */\n\tverticalVelocity: number;\n}\n\nexport function createPlatformer3DStateComponent(): Platformer3DStateComponent {\n\treturn {\n\t\tgrounded: false,\n\t\tjumping: false,\n\t\tfalling: false,\n\t\tjumpCount: 0,\n\t\tjumpStartHeight: 0,\n\t\tcurrentSpeed: 0,\n\t\tlastGroundedY: 0,\n\t\tjumpPressedLastFrame: false,\n\t\tcollisionGrounded: false,\n\t\tgroundedCollisionTime: 0,\n\t\ttimeSinceGrounded: 0,\n\t\tjumpBuffered: false,\n\t\tjumpBufferTimer: 0,\n\t\tjumpHeld: false,\n\t\tjumpCutApplied: false,\n\t\tjumpReleasedSinceLastJump: true,\n\t\ttimeSinceJump: 0,\n\t\tjumpedThisFrame: false,\n\t\tjumpCutRampTimer: 0,\n\t\tverticalVelocity: 0,\n\t};\n}\n","/**\n * Platformer 3D Finite State Machine\n * \n * Manages state transitions for 3D platformer movement:\n * - Idle: Standing still on ground\n * - Walking: Moving on ground (slow)\n * - Running: Moving on ground (fast)\n * - Jumping: Ascending from jump\n * - Falling: Descending (gravity)\n * - Landing: Just touched ground\n */\n\nimport { SyncStateMachine, t } from '../../core/utility/sync-state-machine';\nimport type { Platformer3DInputComponent, Platformer3DStateComponent } from './components';\n\n/**\n * Simplified Collision Context interface\n */\nexport interface PlatformerCollisionContext {\n\tcontact: {\n\t\tnormal: { x: number; y: number; z?: number };\n\t};\n}\n\n/**\n * Platformer states\n */\nexport enum Platformer3DState {\n\tIdle = 'idle',\n\tWalking = 'walking',\n\tRunning = 'running',\n\tJumping = 'jumping',\n\tFalling = 'falling',\n\tLanding = 'landing',\n}\n\n/**\n * Events that trigger state transitions\n */\nexport enum Platformer3DEvent {\n\tWalk = 'walk',\n\tRun = 'run',\n\tJump = 'jump',\n\tFall = 'fall',\n\tLand = 'land',\n\tStop = 'stop',\n}\n\n/**\n * Context for the FSM\n */\nexport interface Platformer3DContext {\n\tinput: Platformer3DInputComponent;\n\tstate: Platformer3DStateComponent;\n}\n\n/**\n * Platformer 3D FSM\n */\nexport class Platformer3DFSM {\n\tmachine: SyncStateMachine<Platformer3DState, Platformer3DEvent, never>;\n\n\tconstructor(private ctx: Platformer3DContext) {\n\t\tthis.machine = new SyncStateMachine<Platformer3DState, Platformer3DEvent, never>(\n\t\t\tPlatformer3DState.Idle,\n\t\t\t[\n\t\t\t\t// Idle transitions\n\t\t\t\tt(Platformer3DState.Idle, Platformer3DEvent.Walk, Platformer3DState.Walking),\n\t\t\t\tt(Platformer3DState.Idle, Platformer3DEvent.Run, Platformer3DState.Running),\n\t\t\t\tt(Platformer3DState.Idle, Platformer3DEvent.Jump, Platformer3DState.Jumping),\n\t\t\t\tt(Platformer3DState.Idle, Platformer3DEvent.Fall, Platformer3DState.Falling),\n\t\t\t\t\n\t\t\t\t// Walking transitions\n\t\t\t\tt(Platformer3DState.Walking, Platformer3DEvent.Run, Platformer3DState.Running),\n\t\t\t\tt(Platformer3DState.Walking, Platformer3DEvent.Jump, Platformer3DState.Jumping),\n\t\t\t\tt(Platformer3DState.Walking, Platformer3DEvent.Stop, Platformer3DState.Idle),\n\t\t\t\tt(Platformer3DState.Walking, Platformer3DEvent.Fall, Platformer3DState.Falling),\n\t\t\t\t\n\t\t\t\t// Running transitions\n\t\t\t\tt(Platformer3DState.Running, Platformer3DEvent.Walk, Platformer3DState.Walking),\n\t\t\t\tt(Platformer3DState.Running, Platformer3DEvent.Jump, Platformer3DState.Jumping),\n\t\t\t\tt(Platformer3DState.Running, Platformer3DEvent.Stop, Platformer3DState.Idle),\n\t\t\t\tt(Platformer3DState.Running, Platformer3DEvent.Fall, Platformer3DState.Falling),\n\t\t\t\t\n\t\t\t\t// Jumping transitions\n\t\t\t\tt(Platformer3DState.Jumping, Platformer3DEvent.Fall, Platformer3DState.Falling),\n\t\t\t\tt(Platformer3DState.Jumping, Platformer3DEvent.Land, Platformer3DState.Landing),\n\t\t\t\tt(Platformer3DState.Jumping, Platformer3DEvent.Jump, Platformer3DState.Jumping), // Multi-jump\n\t\t\t\t\n\t\t\t\t// Falling transitions\n\t\t\t\tt(Platformer3DState.Falling, Platformer3DEvent.Land, Platformer3DState.Landing),\n\t\t\t\t\n\t\t\t\t// Landing transitions\n\t\t\t\tt(Platformer3DState.Landing, Platformer3DEvent.Walk, Platformer3DState.Walking),\n\t\t\t\tt(Platformer3DState.Landing, Platformer3DEvent.Run, Platformer3DState.Running),\n\t\t\t\tt(Platformer3DState.Landing, Platformer3DEvent.Stop, Platformer3DState.Idle),\n\t\t\t\t\n\t\t\t\t// Self-transitions (no-ops)\n\t\t\t\tt(Platformer3DState.Idle, Platformer3DEvent.Stop, Platformer3DState.Idle),\n\t\t\t]\n\t\t);\n\t}\n\n\t/**\n\t * Get the current state\n\t */\n\tgetState(): Platformer3DState {\n\t\treturn this.machine.getState();\n\t}\n\n\t/**\n\t * Dispatch an event to the FSM\n\t */\n\tdispatch(event: Platformer3DEvent): void {\n\t\tif (this.machine.can(event)) {\n\t\t\tthis.machine.syncDispatch(event);\n\t\t}\n\t}\n\n\t/**\n\t * Check if grounded\n\t */\n\tisGrounded(): boolean {\n\t\tconst state = this.getState();\n\t\treturn state === Platformer3DState.Idle || \n\t\t state === Platformer3DState.Walking || \n\t\t state === Platformer3DState.Running || \n\t\t state === Platformer3DState.Landing;\n\t}\n\n\t/**\n\t * Get current jump count from context\n\t */\n\tgetJumpCount(): number {\n\t\treturn this.ctx.state.jumpCount;\n\t}\n\n\t/**\n\t * Handle collision event to update ground state\n\t */\n\thandleCollision(ctx: PlatformerCollisionContext): void {\n\t\t// Check if collision is ground (normal pointing up)\n\t\t// Y normal > 0.5 means roughly within 60 degrees of up\n\t\tif (ctx.contact.normal.y > 0.5) {\n\t\t\tthis.ctx.state.collisionGrounded = true;\n\t\t\tthis.ctx.state.groundedCollisionTime = performance.now();\n\t\t}\n\t}\n\n\t/**\n\t * Update FSM based on current state\n\t */\n\tupdate(input: Platformer3DInputComponent, state: Platformer3DStateComponent): void {\n\t\t// Update context\n\t\tthis.ctx.input = input;\n\t\tthis.ctx.state = state;\n\n\t\t// Get current FSM state\n\t\tconst currentState = this.getState();\n\n\t\t// Determine movement state\n\t\tconst hasInput = Math.abs(input.moveX) > 0.1 || Math.abs(input.moveZ) > 0.1;\n\t\tconst isRunning = input.run;\n\n\t\t// Handle landing: if we're in falling state and now grounded, land\n\t\tif (currentState === Platformer3DState.Falling && state.grounded) {\n\t\t\tthis.dispatch(Platformer3DEvent.Land);\n\t\t\t// Don't return - allow immediate transition to movement state\n\t\t}\n\n\t\t// Send appropriate events based on state\n\t\tif (state.grounded) {\n\t\t\tif (hasInput) {\n\t\t\t\tif (isRunning) {\n\t\t\t\t\tthis.dispatch(Platformer3DEvent.Run);\n\t\t\t\t} else {\n\t\t\t\t\tthis.dispatch(Platformer3DEvent.Walk);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthis.dispatch(Platformer3DEvent.Stop);\n\t\t\t}\n\t\t} else {\n\t\t\t// Not grounded - check if we should be falling\n\t\t\t// Transition to falling if: We're in jumping state and now falling, \n\t\t\t// OR we walked off a ledge (idle/walking/running and now falling)\n\t\t\tif (state.falling) {\n\t\t\t\tthis.dispatch(Platformer3DEvent.Fall);\n\t\t\t}\n\t\t}\n\n\t\t// Handle jump input (edge detection handled in behavior)\n\t\tif (input.jump && !state.jumpPressedLastFrame) {\n\t\t\tthis.dispatch(Platformer3DEvent.Jump);\n\t\t}\n\t}\n}\n","/**\n * Platformer 3D System\n *\n * Kinematic character controller (N64-style): the controller owns all motion\n * and physics is only a collision database.\n *\n * - The entity's body is converted to `KinematicPositionBased` on first\n * update; Rapier never applies forces to it.\n * - Each tick, desired displacement (horizontal input + integrated vertical\n * velocity) is run through Rapier's `KinematicCharacterController`\n * (collide-and-slide with autostep, slope limits, and snap-to-ground) and\n * the corrected movement is applied via `setNextKinematicTranslation`.\n * - Grounded comes from `controller.computedGrounded()` — no contact-pair or\n * raycast probing.\n * - Vertical velocity lives in `state.verticalVelocity` (`linvel()` is\n * meaningless on kinematic bodies). Jump buffering, coyote time,\n * multi-jump, and the jump-cut ramp all operate on that field.\n */\n\nimport { RigidBodyType, QueryFilterFlags } from '@dimforge/rapier3d-compat';\nimport type { Collider, KinematicCharacterController, RigidBody } from '@dimforge/rapier3d-compat';\nimport type { ZylemWorld } from '../../collision/world';\nimport type {\n\tPlatformer3DMovementComponent,\n\tPlatformer3DInputComponent,\n\tPlatformer3DStateComponent,\n} from './components';\n\n/**\n * Duration of the jump-cut velocity ramp in seconds.\n * Instead of halving vertical velocity in a single frame (which produces a\n * visible camera pop), we lerp toward the cut velocity across roughly 3\n * physics steps at 60 Hz.\n */\nconst JUMP_CUT_RAMP_DURATION = 0.05;\n\n/**\n * Constant downward speed applied while grounded (units/second). Keeps the\n * controller pressed into the ground so `computedGrounded()` stays engaged on\n * flat ground and going downhill (snap-to-ground handles larger drops).\n */\nconst GROUND_STICK_SPEED = 2;\n\n/** Vertical speed below which the airborne state flips from jumping to falling. */\nconst FALL_VELOCITY_THRESHOLD = -0.1;\n\nconst DEG_TO_RAD = Math.PI / 180;\n\n/**\n * Entity with platformer components\n */\nexport interface Platformer3DEntity {\n\tuuid: string;\n\tbody: RigidBody;\n\tcollider?: Collider | null;\n\tplatformer: Platformer3DMovementComponent;\n\t$platformer: Platformer3DInputComponent;\n\tplatformerState: Platformer3DStateComponent;\n}\n\n/** Scratch vector reused for the desired per-tick displacement. */\nconst _desired = { x: 0, y: 0, z: 0 };\n\n/**\n * Platformer 3D movement system.\n *\n * Core kinematic controller for 3D platformer movement. Requires a live\n * `ZylemWorld` with the raw Rapier `World` exposed on `.world` — without it\n * the system is a no-op (the WASM fast path handles that case upstream).\n */\nexport class Platformer3DSystem {\n\tprivate world: ZylemWorld | null;\n\t/** Per-entity character controllers, keyed by entity uuid. */\n\tprivate controllers = new Map<string, KinematicCharacterController>();\n\n\tconstructor(world: ZylemWorld | null) {\n\t\tthis.world = world;\n\t}\n\n\t/**\n\t * One-time per-entity physics setup: convert the body to a\n\t * position-based kinematic body and create its character controller.\n\t * Done lazily on the first update because the body isn't always\n\t * available at construction time.\n\t */\n\tprivate ensurePhysicsConfigured(entity: Platformer3DEntity): KinematicCharacterController | null {\n\t\tconst existing = this.controllers.get(entity.uuid);\n\t\tif (existing) return existing;\n\n\t\tconst physicsWorld = this.world?.world;\n\t\tif (!physicsWorld || !entity.body) return null;\n\n\t\tif (entity.body.bodyType() !== RigidBodyType.KinematicPositionBased) {\n\t\t\tentity.body.setBodyType(RigidBodyType.KinematicPositionBased, true);\n\t\t}\n\n\t\tconst movement = entity.platformer;\n\t\tconst controller = physicsWorld.createCharacterController(movement.characterOffset);\n\t\tcontroller.setUp({ x: 0, y: 1, z: 0 });\n\t\tcontroller.setMaxSlopeClimbAngle(movement.slopeClimbAngle * DEG_TO_RAD);\n\t\tcontroller.setMinSlopeSlideAngle(movement.slopeSlideAngle * DEG_TO_RAD);\n\t\tif (movement.autostep) {\n\t\t\tcontroller.enableAutostep(movement.autostep.maxHeight, movement.autostep.minWidth, true);\n\t\t} else {\n\t\t\tcontroller.disableAutostep();\n\t\t}\n\t\tcontroller.enableSnapToGround(movement.snapToGroundDistance);\n\t\tcontroller.setApplyImpulsesToDynamicBodies(true);\n\n\t\tthis.controllers.set(entity.uuid, controller);\n\t\treturn controller;\n\t}\n\n\t/**\n\t * Handle jump logic with multi-jump, coyote time, buffering, and jump-cut\n\t * ramp. All vertical motion is written to `state.verticalVelocity`.\n\t */\n\tprivate handleJump(entity: Platformer3DEntity, delta: number): void {\n\t\tconst input = entity.$platformer;\n\t\tconst movement = entity.platformer;\n\t\tconst state = entity.platformerState;\n\n\t\t// Track time since last jump (for multi-jump window)\n\t\tif (state.jumping || state.falling) {\n\t\t\tstate.timeSinceJump += delta;\n\t\t}\n\n\t\t// Track jump button release (required for multi-jump)\n\t\tif (!input.jump && state.jumpHeld) {\n\t\t\tstate.jumpReleasedSinceLastJump = true;\n\t\t}\n\n\t\t// 1. Jump Buffering - queue on new press\n\t\tif (input.jump && !state.jumpPressedLastFrame) {\n\t\t\tstate.jumpBuffered = true;\n\t\t\tstate.jumpBufferTimer = movement.jumpBufferTime;\n\t\t}\n\n\t\tstate.jumpPressedLastFrame = input.jump;\n\t\tstate.jumpHeld = input.jump;\n\n\t\t// Decrement buffer timer\n\t\tif (state.jumpBuffered) {\n\t\t\tstate.jumpBufferTimer -= delta;\n\t\t\tif (state.jumpBufferTimer <= 0) {\n\t\t\t\tstate.jumpBuffered = false;\n\t\t\t}\n\t\t}\n\n\t\t// 2. Variable Jump Height (Jump Cut)\n\t\t// Only trigger a cut if we've been jumping for a bit (prevents cutting multi-jump immediately).\n\t\tconst minTimeBeforeCut = 0.1;\n\t\tconst canApplyCut = state.timeSinceJump >= minTimeBeforeCut;\n\t\tif (!input.jump && state.jumping && !state.jumpCutApplied && canApplyCut) {\n\t\t\tstate.jumpCutApplied = true;\n\t\t\tstate.jumpCutRampTimer = JUMP_CUT_RAMP_DURATION;\n\t\t}\n\n\t\t// 2a. Ramp verticalVelocity toward verticalVelocity * jumpCutMultiplier\n\t\t// across the ramp window so the change isn't a single-frame discontinuity.\n\t\tif (state.jumpCutRampTimer > 0) {\n\t\t\tif (state.verticalVelocity > 0) {\n\t\t\t\tconst stepT = Math.min(delta / state.jumpCutRampTimer, 1);\n\t\t\t\tconst targetY = state.verticalVelocity * movement.jumpCutMultiplier;\n\t\t\t\tstate.verticalVelocity += (targetY - state.verticalVelocity) * stepT;\n\t\t\t}\n\t\t\tstate.jumpCutRampTimer = Math.max(0, state.jumpCutRampTimer - delta);\n\t\t}\n\n\t\t// Execute Jump (if buffered input exists)\n\t\tif (!state.jumpBuffered) return;\n\n\t\t// 3. Jump eligibility check\n\t\tconst inCoyoteWindow = !state.grounded && state.timeSinceGrounded <= movement.coyoteTime;\n\t\tconst isFirstJump = state.grounded || (inCoyoteWindow && state.jumpCount === 0);\n\n\t\t// Multi-jump requirements:\n\t\t// 1. Not grounded\n\t\t// 2. Have jumps remaining\n\t\t// 3. Button was released since last jump (no holding for double jump)\n\t\t// 4. Within the jump window (after multiJumpWindowTime has passed)\n\t\tconst hasJumpsRemaining = state.jumpCount < movement.maxJumps;\n\t\tconst buttonReleased = state.jumpReleasedSinceLastJump;\n\t\tconst inMultiJumpWindow = state.timeSinceJump >= movement.multiJumpWindowTime;\n\t\tconst canMultiJump = !state.grounded && hasJumpsRemaining && buttonReleased && inMultiJumpWindow;\n\n\t\tif (isFirstJump || canMultiJump) {\n\t\t\t// Consume buffered input\n\t\t\tstate.jumpBuffered = false;\n\n\t\t\t// Increment jump count and reset tracking\n\t\t\tstate.jumpCount++;\n\t\t\tstate.jumpReleasedSinceLastJump = false; // Must release again for next jump\n\t\t\tstate.timeSinceJump = 0; // Reset window timer\n\n\t\t\t// Record jump start height\n\t\t\tstate.jumpStartHeight = entity.body.translation().y;\n\n\t\t\t// Apply jump force\n\t\t\tstate.jumping = true;\n\t\t\tstate.falling = false;\n\t\t\tstate.jumpCutApplied = false;\n\t\t\tstate.jumpCutRampTimer = 0;\n\n\t\t\tstate.verticalVelocity = movement.jumpForce;\n\n\t\t\t// Flag this frame so applyGravity skips exactly one grounded\n\t\t\t// clamp/gravity step (the jump velocity is authoritative).\n\t\t\tstate.jumpedThisFrame = true;\n\t\t}\n\t}\n\n\t/**\n\t * Integrate gravity into `state.verticalVelocity`.\n\t *\n\t * While grounded (and not on the jump frame) vertical velocity is held at\n\t * a small constant downward speed so the controller stays pressed into\n\t * the ground. Skipped entirely on the frame a jump was applied — the\n\t * grounded flag is still stale-true then and would clobber the jump.\n\t */\n\tprivate applyGravity(entity: Platformer3DEntity, delta: number): void {\n\t\tconst movement = entity.platformer;\n\t\tconst state = entity.platformerState;\n\n\t\tif (state.jumpedThisFrame) {\n\t\t\tstate.jumpedThisFrame = false;\n\t\t\treturn;\n\t\t}\n\n\t\tif (state.grounded) {\n\t\t\tstate.verticalVelocity = -GROUND_STICK_SPEED;\n\t\t\treturn;\n\t\t}\n\n\t\tstate.verticalVelocity -= movement.gravity * delta;\n\t}\n\n\t/**\n\t * Update entity state from the controller's grounded result.\n\t */\n\tprivate updateState(entity: Platformer3DEntity, delta: number, computedGrounded: boolean): void {\n\t\tconst state = entity.platformerState;\n\t\tconst wasGrounded = state.grounded;\n\n\t\tconst isAirborne = state.jumping || state.falling;\n\t\tlet isGrounded: boolean;\n\t\tif (isAirborne) {\n\t\t\t// Never land while still moving upward in a jump arc.\n\t\t\tconst canLand = state.falling && !state.jumping;\n\t\t\tisGrounded = computedGrounded && canLand;\n\t\t} else {\n\t\t\tisGrounded = computedGrounded;\n\t\t}\n\n\t\tstate.grounded = isGrounded;\n\n\t\tif (state.grounded) {\n\t\t\tstate.timeSinceGrounded = 0;\n\t\t\tstate.lastGroundedY = entity.body.translation().y;\n\t\t} else {\n\t\t\tstate.timeSinceGrounded += delta;\n\t\t}\n\n\t\tif (!wasGrounded && state.grounded) {\n\t\t\tstate.jumpCount = 0;\n\t\t\tstate.jumping = false;\n\t\t\tstate.falling = false;\n\t\t\tstate.jumpCutApplied = false;\n\t\t\tstate.jumpCutRampTimer = 0;\n\t\t\tif (state.verticalVelocity < 0) {\n\t\t\t\tstate.verticalVelocity = 0;\n\t\t\t}\n\t\t}\n\n\t\tif (state.verticalVelocity < FALL_VELOCITY_THRESHOLD && !state.grounded) {\n\t\t\tif (state.jumping) {\n\t\t\t\tstate.jumping = false;\n\t\t\t\tstate.falling = true;\n\t\t\t} else if (!state.jumping) {\n\t\t\t\tstate.falling = true;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Update one platformer entity. Invoked per linked entity by the\n\t * Platformer3DBehaviorSystem adapter.\n\t */\n\tupdateEntity(entity: Platformer3DEntity, delta: number): void {\n\t\tif (!entity.platformer || !entity.$platformer || !entity.platformerState) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst controller = this.ensurePhysicsConfigured(entity);\n\t\tconst collider = entity.collider;\n\t\tif (!controller || !collider) return;\n\n\t\tconst input = entity.$platformer;\n\t\tconst movement = entity.platformer;\n\t\tconst state = entity.platformerState;\n\n\t\t// 1. Jump logic (may set verticalVelocity = jumpForce this frame)\n\t\tthis.handleJump(entity, delta);\n\n\t\t// 2. Gravity / grounded stick on verticalVelocity\n\t\tthis.applyGravity(entity, delta);\n\n\t\t// 3. Desired displacement for this tick\n\t\tconst speed = input.run ? movement.runSpeed : movement.walkSpeed;\n\t\tstate.currentSpeed = speed;\n\t\t_desired.x = input.moveX * speed * delta;\n\t\t_desired.y = state.verticalVelocity * delta;\n\t\t_desired.z = input.moveZ * speed * delta;\n\n\t\t// 4. Collide-and-slide\n\t\tcontroller.computeColliderMovement(collider, _desired, QueryFilterFlags.EXCLUDE_SENSORS);\n\t\tconst corrected = controller.computedMovement();\n\n\t\t// Head bump: upward motion was blocked, so kill the remaining ascent\n\t\t// instead of hanging under the ceiling for the rest of the arc.\n\t\tif (_desired.y > 1e-9 && corrected.y < _desired.y * 0.5) {\n\t\t\tstate.verticalVelocity = 0;\n\t\t}\n\n\t\tconst translation = entity.body.translation();\n\t\tentity.body.setNextKinematicTranslation({\n\t\t\tx: translation.x + corrected.x,\n\t\t\ty: translation.y + corrected.y,\n\t\t\tz: translation.z + corrected.z,\n\t\t});\n\n\t\t// 5. State transitions from the controller's grounded result\n\t\tthis.updateState(entity, delta, controller.computedGrounded());\n\t}\n\n\tdestroy(): void {\n\t\tconst physicsWorld = this.world?.world;\n\t\tif (physicsWorld) {\n\t\t\tfor (const controller of this.controllers.values()) {\n\t\t\t\tphysicsWorld.removeCharacterController(controller);\n\t\t\t}\n\t\t}\n\t\tthis.controllers.clear();\n\t}\n}\n","/**\n * Platformer 3D Behavior Descriptor\n * \n * Type-safe descriptor for the platformer 3D behavior system.\n * Provides entity.use() API for 3D platformer movement.\n */\n\nimport { defineBehavior } from '../behavior-descriptor';\nimport type { BehaviorEntityLink, BehaviorSystem } from '../behavior-system';\nimport { Platformer3DSystem, Platformer3DEntity } from './platformer-3d.system';\nimport type { ZylemWorld } from '../../collision/world';\nimport { Platformer3DFSM, Platformer3DState, PlatformerCollisionContext } from './platformer-3d-fsm';\nimport {\n\tcreatePlatformer3DMovementComponent,\n\tcreatePlatformer3DInputComponent,\n\tcreatePlatformer3DStateComponent,\n} from './components';\nimport type { WasmStageRuntime } from '../../runtime/wasm-stage-runtime';\n\n/**\n * Platformer behavior options (typed for entity.use() autocomplete)\n */\nexport interface Platformer3DBehaviorOptions {\n\t/** Base walking speed (default: 12) */\n\twalkSpeed?: number;\n\t/** Sprint/run speed (default: 24) */\n\trunSpeed?: number;\n\t/** Initial jump force (default: 12) */\n\tjumpForce?: number;\n\t/** Maximum number of jumps (default: 1) */\n\tmaxJumps?: number;\n\t/** Gravity force (default: 9.82) */\n\tgravity?: number;\n\t/** Maximum slope angle in degrees the character can walk up (default: 50) */\n\tslopeClimbAngle?: number;\n\t/** Slope angle in degrees past which the character slides (default: 40) */\n\tslopeSlideAngle?: number;\n\t/** Autostep over small ledges, or false to disable (default: { maxHeight: 0.3, minWidth: 0.1 }) */\n\tautostep?: { maxHeight: number; minWidth: number } | false;\n\t/** Snap-to-ground distance for downhill/step-down stability (default: 0.3) */\n\tsnapToGroundDistance?: number;\n\t/** Character controller collision offset (default: 0.05) */\n\tcharacterOffset?: number;\n}\n\nconst defaultOptions: Platformer3DBehaviorOptions = {\n\twalkSpeed: 12,\n\trunSpeed: 24,\n\tjumpForce: 12,\n\tmaxJumps: 1,\n\tgravity: 9.82,\n\tslopeClimbAngle: 50,\n\tslopeSlideAngle: 40,\n\tautostep: { maxHeight: 0.3, minWidth: 0.1 },\n\tsnapToGroundDistance: 0.3,\n\tcharacterOffset: 0.05,\n};\n\nconst PLATFORMER_BEHAVIOR_KEY = Symbol.for('zylem:behavior:platformer-3d');\n\n/**\n * Adapter that wraps Platformer3DSystem as a BehaviorSystem\n */\nclass Platformer3DBehaviorSystem implements BehaviorSystem {\n\tprivate movementBehavior: Platformer3DSystem;\n\tprivate attachedRuntimeSlots = new Set<number>();\n\n\tconstructor(\n\t\tworld: ZylemWorld | null,\n\t\tprivate wasmStage: WasmStageRuntime | null,\n\t\tprivate getBehaviorLinks?: (key: symbol) => Iterable<BehaviorEntityLink>,\n\t) {\n\t\tthis.movementBehavior = new Platformer3DSystem(world);\n\t}\n\n\tupdate(_ecs: unknown, delta: number): void {\n\t\tconst links = this.getBehaviorLinks?.(PLATFORMER_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 platformerRef = link.ref as any;\n\n\t\t\tconst options = platformerRef.options as Platformer3DBehaviorOptions;\n\n\t\t\tif (!gameEntity.platformer) {\n\t\t\t\tgameEntity.platformer = createPlatformer3DMovementComponent(options);\n\t\t\t}\n\t\t\tif (!gameEntity.$platformer) {\n\t\t\t\tgameEntity.$platformer = createPlatformer3DInputComponent();\n\t\t\t}\n\t\t\tif (!gameEntity.platformerState) {\n\t\t\t\tgameEntity.platformerState = createPlatformer3DStateComponent();\n\t\t\t}\n\n\t\t\tif (!platformerRef.fsm && gameEntity.$platformer && gameEntity.platformerState) {\n\t\t\t\tplatformerRef.fsm = new Platformer3DFSM({\n\t\t\t\t\tinput: gameEntity.$platformer,\n\t\t\t\t\tstate: gameEntity.platformerState,\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (platformerRef.fsm && gameEntity.$platformer && gameEntity.platformerState) {\n\t\t\t\tplatformerRef.fsm.update(gameEntity.$platformer, gameEntity.platformerState);\n\t\t\t}\n\n\t\t\tconst handle = (gameEntity.runtimeHandle ?? -1) as number;\n\t\t\tif (this.wasmStage && handle >= 0) {\n\t\t\t\tthis.ensureWasmAttached(handle, options);\n\t\t\t\tconst input = gameEntity.$platformer;\n\t\t\t\tif (input) {\n\t\t\t\t\tthis.wasmStage.setPlatformer3DInputAxes(handle, input.moveX ?? 0, input.moveZ ?? 0);\n\t\t\t\t\tthis.wasmStage.setPlatformer3DInputButtons(handle, !!input.jump, !!input.run);\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (!gameEntity.body) continue;\n\t\t\tthis.movementBehavior.updateEntity(gameEntity, delta);\n\t\t}\n\t}\n\n\tprivate ensureWasmAttached(handle: number, options: Platformer3DBehaviorOptions): void {\n\t\tif (!this.wasmStage || this.attachedRuntimeSlots.has(handle)) return;\n\t\tthis.wasmStage.attachPlatformer3D(handle, {\n\t\t\thalfHeight: 0.9,\n\t\t\tradius: 0.4,\n\t\t\twalkSpeed: options.walkSpeed ?? defaultOptions.walkSpeed!,\n\t\t\trunSpeed: options.runSpeed ?? defaultOptions.runSpeed!,\n\t\t\tjumpForce: options.jumpForce ?? defaultOptions.jumpForce!,\n\t\t\tmaxJumps: options.maxJumps ?? defaultOptions.maxJumps!,\n\t\t\tgravity: options.gravity ?? defaultOptions.gravity!,\n\t\t});\n\t\tthis.attachedRuntimeSlots.add(handle);\n\t}\n\n\tdestroy(_ecs: unknown): void {\n\t\tthis.movementBehavior.destroy();\n\t\tthis.attachedRuntimeSlots.clear();\n\t}\n}\n\n/**\n * Platformer3DBehavior - typed descriptor for 3D platformer movement\n * \n * Provides complete 3D platformer physics including:\n * - Walking and running\n * - Jumping with multi-jump support\n * - Gravity and falling\n * - Ground detection\n * \n * @example\n * ```typescript\n * import { Platformer3DBehavior } from \"@zylem/game-lib/behavior\";\n * \n * const player = await playgroundActor('player');\n * const platformer = player.use(Platformer3DBehavior, {\n * walkSpeed: 12,\n * runSpeed: 24,\n * jumpForce: 12,\n * maxJumps: 2, // Double jump!\n * });\n * \n * // In update loop\n * player.onUpdate(({ inputs }) => {\n * player.$platformer.moveX = inputs.p1.axes.Horizontal.value;\n * player.$platformer.moveZ = inputs.p1.axes.Vertical.value;\n * player.$platformer.jump = inputs.p1.buttons.A.held > 0;\n * player.$platformer.run = inputs.p1.shoulders.LTrigger.held > 0;\n * \n * const state = platformer.getState();\n * const grounded = platformer.isGrounded();\n * });\n * ```\n */\nexport const Platformer3DBehavior = defineBehavior<\n\tPlatformer3DBehaviorOptions,\n\t{\n\t\tgetState: () => Platformer3DState;\n\t\tisGrounded: () => boolean;\n\t\tgetJumpCount: () => number;\n\t\tonPlatformCollision: (ctx: PlatformerCollisionContext) => void;\n\t},\n\tPlatformer3DEntity\n>({\n\tname: 'platformer-3d',\n\tdefaultOptions,\n\tsystemFactory: (ctx) =>\n\t\tnew Platformer3DBehaviorSystem(\n\t\t\tctx.world ?? null,\n\t\t\tctx.wasmStage ?? null,\n\t\t\tctx.getBehaviorLinks,\n\t\t),\n\tcreateHandle: (ref) => ({\n\t\tgetState: () => ref.fsm?.getState() ?? Platformer3DState.Idle,\n\t\tisGrounded: () => ref.fsm?.isGrounded() ?? false,\n\t\tgetJumpCount: () => ref.fsm?.getJumpCount() ?? 0,\n\t\tonPlatformCollision: (ctx) => ref.fsm?.handleCollision(ctx),\n\t}),\n});\n"],"mappings":"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","names":["Platformer3DState","Platformer3DEvent"]}
@@ -1,10 +1,11 @@
1
1
  import {
2
+ fireShooter2DIfRequested,
2
3
  getEntityFacingAngle2D,
3
4
  resolveAim2D
4
- } from "./chunk-IEA7F73P.js";
5
+ } from "./chunk-UL6Q5HDP.js";
5
6
  import {
6
7
  createThrusterInputComponent
7
- } from "./chunk-2HLZBWGD.js";
8
+ } from "./chunk-IZJFUAXB.js";
8
9
 
9
10
  // src/lib/coordinators/multidirectional-space-shooter.coordinator.ts
10
11
  var MultidirectionalSpaceShooterCoordinator = class {
@@ -32,24 +33,11 @@ var MultidirectionalSpaceShooterCoordinator = class {
32
33
  if (aim.hasAimInput) {
33
34
  this.entity.setRotationZ(aim.angle);
34
35
  }
35
- if (!input.shootPressed && !input.shootHeld) {
36
- return;
37
- }
38
- const sourcePosition = this.entity.body?.translation();
39
- if (!sourcePosition) {
40
- return;
41
- }
42
- void this.shooter.fire({
43
- source: this.entity,
44
- stage: this.stage,
45
- target: {
46
- x: sourcePosition.x + aim.direction.x,
47
- y: sourcePosition.y + aim.direction.y
48
- }
49
- });
36
+ fireShooter2DIfRequested(this.entity, this.shooter, this.stage, input, aim);
50
37
  }
51
38
  };
52
39
 
53
40
  export {
54
41
  MultidirectionalSpaceShooterCoordinator
55
42
  };
43
+ //# sourceMappingURL=chunk-FTDZAKZW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/coordinators/multidirectional-space-shooter.coordinator.ts"],"sourcesContent":["import {\n\tcreateThrusterInputComponent,\n\ttype ThrusterInputComponent,\n} from '../behaviors/thruster/components';\nimport type {\n\tShooter2DHandle,\n\tShooter2DSourceEntity,\n\tShooter2DStageLike,\n} from '../behaviors/shooter-2d/shooter-2d.descriptor';\nimport { getEntityFacingAngle2D, resolveAim2D } from './shared/top-down-aim';\nimport {\n\tfireShooter2DIfRequested,\n\ttype Shooter2DAimFireInput,\n} from './shared/shooter-fire';\n\nexport interface MultidirectionalSpaceShooterInput extends Shooter2DAimFireInput {\n\tmoveX: number;\n\tmoveY: number;\n}\n\nexport type MultidirectionalSpaceShooterEntity = Shooter2DSourceEntity & {\n\t$thruster?: ThrusterInputComponent;\n\tsetRotationZ(angle: number): void;\n};\n\n/**\n * Coordinator that fans a single input snapshot out to the thruster behavior\n * (vector thrust), entity rotation (aim), and the Shooter2D behavior (fire\n * requests).\n */\nexport class MultidirectionalSpaceShooterCoordinator {\n\tconstructor(\n\t\tprivate entity: MultidirectionalSpaceShooterEntity,\n\t\tprivate shooter: Shooter2DHandle,\n\t\tprivate stage: Shooter2DStageLike,\n\t) {}\n\n\tpublic update(input: MultidirectionalSpaceShooterInput): void {\n\t\tif (!this.entity.$thruster) {\n\t\t\tthis.entity.$thruster = createThrusterInputComponent();\n\t\t}\n\n\t\tthis.entity.$thruster.thrustX = input.moveX;\n\t\tthis.entity.$thruster.thrustY = input.moveY;\n\t\tthis.entity.$thruster.thrust = 0;\n\t\tthis.entity.$thruster.rotate = 0;\n\n\t\tconst aim = resolveAim2D(\n\t\t\tinput.aimX,\n\t\t\tinput.aimY,\n\t\t\tgetEntityFacingAngle2D(this.entity),\n\t\t);\n\t\tif (aim.hasAimInput) {\n\t\t\tthis.entity.setRotationZ(aim.angle);\n\t\t}\n\n\t\tfireShooter2DIfRequested(this.entity, this.shooter, this.stage, input, aim);\n\t}\n}\n"],"mappings":";;;;;;;;;;AA8BO,IAAM,0CAAN,MAA8C;AAAA,EACpD,YACS,QACA,SACA,OACP;AAHO;AACA;AACA;AAAA,EACN;AAAA,EAHM;AAAA,EACA;AAAA,EACA;AAAA,EAGF,OAAO,OAAgD;AAC7D,QAAI,CAAC,KAAK,OAAO,WAAW;AAC3B,WAAK,OAAO,YAAY,6BAA6B;AAAA,IACtD;AAEA,SAAK,OAAO,UAAU,UAAU,MAAM;AACtC,SAAK,OAAO,UAAU,UAAU,MAAM;AACtC,SAAK,OAAO,UAAU,SAAS;AAC/B,SAAK,OAAO,UAAU,SAAS;AAE/B,UAAM,MAAM;AAAA,MACX,MAAM;AAAA,MACN,MAAM;AAAA,MACN,uBAAuB,KAAK,MAAM;AAAA,IACnC;AACA,QAAI,IAAI,aAAa;AACpB,WAAK,OAAO,aAAa,IAAI,KAAK;AAAA,IACnC;AAEA,6BAAyB,KAAK,QAAQ,KAAK,SAAS,KAAK,OAAO,OAAO,GAAG;AAAA,EAC3E;AACD;","names":[]}
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  createBounds2DRect,
3
3
  wrapPoint2D
4
- } from "./chunk-ZHBD65AZ.js";
4
+ } from "./chunk-KDY5N6J4.js";
5
5
  import {
6
6
  SyncStateMachine,
7
7
  t
8
- } from "./chunk-RER2IDPW.js";
8
+ } from "./chunk-4URF65BR.js";
9
9
  import {
10
10
  defineBehavior
11
- } from "./chunk-YDLO5M7S.js";
11
+ } from "./chunk-EZNCCO4L.js";
12
12
 
13
13
  // src/lib/behaviors/screen-wrap/screen-wrap-fsm.ts
14
14
  var ScreenWrapState = /* @__PURE__ */ ((ScreenWrapState2) => {
@@ -112,12 +112,10 @@ var defaultOptions = {
112
112
  };
113
113
  var SCREEN_WRAP_BEHAVIOR_KEY = /* @__PURE__ */ Symbol.for("zylem:behavior:screen-wrap");
114
114
  var ScreenWrapSystem = class {
115
- constructor(world, wasmStage, getBehaviorLinks) {
116
- this.world = world;
115
+ constructor(wasmStage, getBehaviorLinks) {
117
116
  this.wasmStage = wasmStage;
118
117
  this.getBehaviorLinks = getBehaviorLinks;
119
118
  }
120
- world;
121
119
  wasmStage;
122
120
  getBehaviorLinks;
123
121
  attachedRuntimeSlots = /* @__PURE__ */ new Set();
@@ -198,7 +196,7 @@ var ScreenWrapSystem = class {
198
196
  var ScreenWrapBehavior = defineBehavior({
199
197
  name: "screen-wrap",
200
198
  defaultOptions,
201
- systemFactory: (ctx) => new ScreenWrapSystem(ctx.world, ctx.wasmStage ?? null, ctx.getBehaviorLinks)
199
+ systemFactory: (ctx) => new ScreenWrapSystem(ctx.wasmStage ?? null, ctx.getBehaviorLinks)
202
200
  });
203
201
 
204
202
  export {
@@ -207,3 +205,4 @@ export {
207
205
  ScreenWrapFSM,
208
206
  ScreenWrapBehavior
209
207
  };
208
+ //# sourceMappingURL=chunk-GFARAJV2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/behaviors/screen-wrap/screen-wrap-fsm.ts","../src/lib/behaviors/screen-wrap/screen-wrap.descriptor.ts"],"sourcesContent":["/**\n * ScreenWrapFSM\n * \n * State machine for screen wrap behavior.\n * Reports position relative to bounds edges.\n */\n\nimport { SyncStateMachine, t } from '../../core/utility/sync-state-machine';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// FSM State Model\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport enum ScreenWrapState {\n\tCenter = 'center',\n\tNearEdgeLeft = 'near-edge-left',\n\tNearEdgeRight = 'near-edge-right',\n\tNearEdgeTop = 'near-edge-top',\n\tNearEdgeBottom = 'near-edge-bottom',\n\tWrapped = 'wrapped',\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// FSM Events\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport enum ScreenWrapEvent {\n\tEnterCenter = 'enter-center',\n\tApproachLeft = 'approach-left',\n\tApproachRight = 'approach-right',\n\tApproachTop = 'approach-top',\n\tApproachBottom = 'approach-bottom',\n\tWrap = 'wrap',\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ScreenWrapFSM\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport class ScreenWrapFSM {\n\tmachine: SyncStateMachine<ScreenWrapState, ScreenWrapEvent, never>;\n\n\tconstructor() {\n\t\tthis.machine = new SyncStateMachine<ScreenWrapState, ScreenWrapEvent, never>(\n\t\t\tScreenWrapState.Center,\n\t\t\t[\n\t\t\t\t// From Center\n\t\t\t\tt(ScreenWrapState.Center, ScreenWrapEvent.ApproachLeft, ScreenWrapState.NearEdgeLeft),\n\t\t\t\tt(ScreenWrapState.Center, ScreenWrapEvent.ApproachRight, ScreenWrapState.NearEdgeRight),\n\t\t\t\tt(ScreenWrapState.Center, ScreenWrapEvent.ApproachTop, ScreenWrapState.NearEdgeTop),\n\t\t\t\tt(ScreenWrapState.Center, ScreenWrapEvent.ApproachBottom, ScreenWrapState.NearEdgeBottom),\n\t\t\t\t\n\t\t\t\t// From NearEdge to Wrapped\n\t\t\t\tt(ScreenWrapState.NearEdgeLeft, ScreenWrapEvent.Wrap, ScreenWrapState.Wrapped),\n\t\t\t\tt(ScreenWrapState.NearEdgeRight, ScreenWrapEvent.Wrap, ScreenWrapState.Wrapped),\n\t\t\t\tt(ScreenWrapState.NearEdgeTop, ScreenWrapEvent.Wrap, ScreenWrapState.Wrapped),\n\t\t\t\tt(ScreenWrapState.NearEdgeBottom, ScreenWrapEvent.Wrap, ScreenWrapState.Wrapped),\n\n\t\t\t\t// From NearEdge back to Center\n\t\t\t\tt(ScreenWrapState.NearEdgeLeft, ScreenWrapEvent.EnterCenter, ScreenWrapState.Center),\n\t\t\t\tt(ScreenWrapState.NearEdgeRight, ScreenWrapEvent.EnterCenter, ScreenWrapState.Center),\n\t\t\t\tt(ScreenWrapState.NearEdgeTop, ScreenWrapEvent.EnterCenter, ScreenWrapState.Center),\n\t\t\t\tt(ScreenWrapState.NearEdgeBottom, ScreenWrapEvent.EnterCenter, ScreenWrapState.Center),\n\n\t\t\t\t// From Wrapped back to Center\n\t\t\t\tt(ScreenWrapState.Wrapped, ScreenWrapEvent.EnterCenter, ScreenWrapState.Center),\n\n\t\t\t\t// From Wrapped to NearEdge (landed near opposite edge)\n\t\t\t\tt(ScreenWrapState.Wrapped, ScreenWrapEvent.ApproachLeft, ScreenWrapState.NearEdgeLeft),\n\t\t\t\tt(ScreenWrapState.Wrapped, ScreenWrapEvent.ApproachRight, ScreenWrapState.NearEdgeRight),\n\t\t\t\tt(ScreenWrapState.Wrapped, ScreenWrapEvent.ApproachTop, ScreenWrapState.NearEdgeTop),\n\t\t\t\tt(ScreenWrapState.Wrapped, ScreenWrapEvent.ApproachBottom, ScreenWrapState.NearEdgeBottom),\n\n\t\t\t\t// Self-transitions (no-ops for redundant events)\n\t\t\t\tt(ScreenWrapState.Center, ScreenWrapEvent.EnterCenter, ScreenWrapState.Center),\n\t\t\t\tt(ScreenWrapState.NearEdgeLeft, ScreenWrapEvent.ApproachLeft, ScreenWrapState.NearEdgeLeft),\n\t\t\t\tt(ScreenWrapState.NearEdgeRight, ScreenWrapEvent.ApproachRight, ScreenWrapState.NearEdgeRight),\n\t\t\t\tt(ScreenWrapState.NearEdgeTop, ScreenWrapEvent.ApproachTop, ScreenWrapState.NearEdgeTop),\n\t\t\t\tt(ScreenWrapState.NearEdgeBottom, ScreenWrapEvent.ApproachBottom, ScreenWrapState.NearEdgeBottom),\n\t\t\t]\n\t\t);\n\t}\n\n\tgetState(): ScreenWrapState {\n\t\treturn this.machine.getState();\n\t}\n\n\tdispatch(event: ScreenWrapEvent): void {\n\t\tif (this.machine.can(event)) {\n\t\t\tthis.machine.syncDispatch(event);\n\t\t}\n\t}\n\n\t/**\n\t * Update FSM based on entity position relative to bounds\n\t */\n\tupdate(position: { x: number; y: number }, bounds: {\n\t\tminX: number; maxX: number; minY: number; maxY: number;\n\t\tedgeThreshold: number;\n\t}, wrapped: boolean): void {\n\t\tconst { x, y } = position;\n\t\tconst { minX, maxX, minY, maxY, edgeThreshold } = bounds;\n\n\t\tif (wrapped) {\n\t\t\tthis.dispatch(ScreenWrapEvent.Wrap);\n\t\t\treturn;\n\t\t}\n\n\t\t// Check if near edges\n\t\tconst nearLeft = x < minX + edgeThreshold;\n\t\tconst nearRight = x > maxX - edgeThreshold;\n\t\tconst nearBottom = y < minY + edgeThreshold;\n\t\tconst nearTop = y > maxY - edgeThreshold;\n\n\t\tif (nearLeft) {\n\t\t\tthis.dispatch(ScreenWrapEvent.ApproachLeft);\n\t\t} else if (nearRight) {\n\t\t\tthis.dispatch(ScreenWrapEvent.ApproachRight);\n\t\t} else if (nearTop) {\n\t\t\tthis.dispatch(ScreenWrapEvent.ApproachTop);\n\t\t} else if (nearBottom) {\n\t\t\tthis.dispatch(ScreenWrapEvent.ApproachBottom);\n\t\t} else {\n\t\t\tthis.dispatch(ScreenWrapEvent.EnterCenter);\n\t\t}\n\t}\n}\n","/**\n * ScreenWrapBehavior\n * \n * When an entity exits the defined 2D bounds, it wraps around to the opposite edge.\n * Asteroids-style screen wrapping with FSM for edge detection.\n */\n\nimport { defineBehavior } from '../behavior-descriptor';\nimport type { BehaviorEntityLink, BehaviorSystem } from '../behavior-system';\nimport { ScreenWrapFSM } from './screen-wrap-fsm';\nimport { createBounds2DRect, wrapPoint2D } from '../shared/bounds-2d';\nimport type { WasmStageRuntime } from '../../runtime/wasm-stage-runtime';\n\n/**\n * Screen wrap options (typed for entity.use() autocomplete)\n */\nexport interface ScreenWrapOptions {\n\t/** Width of the wrapping area (default: 20) */\n\twidth: number;\n\t/** Height of the wrapping area (default: 15) */\n\theight: number;\n\t/** Center X position (default: 0) */\n\tcenterX: number;\n\t/** Center Y position (default: 0) */\n\tcenterY: number;\n\t/** Distance from edge to trigger NearEdge state (default: 2) */\n\tedgeThreshold: number;\n}\n\nconst defaultOptions: ScreenWrapOptions = {\n\twidth: 20,\n\theight: 15,\n\tcenterX: 0,\n\tcenterY: 0,\n\tedgeThreshold: 2,\n};\n\nconst SCREEN_WRAP_BEHAVIOR_KEY = Symbol.for('zylem:behavior:screen-wrap');\n\n/**\n * ScreenWrapSystem - Wraps entities around 2D bounds\n *\n * Mirrors the Rust `screen_wrap` Stage behavior: when `wasmStage` is present\n * and the entity has a `runtimeHandle`, the wasm runtime owns the wrap and\n * this system only mirrors the queried state into the local FSM. Otherwise\n * the legacy TS path runs against the Rapier `RigidBody` translation.\n */\nclass ScreenWrapSystem 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?.(SCREEN_WRAP_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 wrapRef = link.ref as any;\n\t\t\tconst options = wrapRef.options as ScreenWrapOptions;\n\n\t\t\tif (!wrapRef.fsm) {\n\t\t\t\twrapRef.fsm = new ScreenWrapFSM();\n\t\t\t}\n\n\t\t\tconst handle = (gameEntity.runtimeHandle ?? -1) as number;\n\t\t\tif (this.wasmStage && handle >= 0) {\n\t\t\t\tthis.ensureWasmAttached(handle, options);\n\t\t\t\tconst pose = this.wasmStage.getPose(handle);\n\t\t\t\tconst query = this.wasmStage.queryScreenWrap(handle);\n\t\t\t\tif (pose && query) {\n\t\t\t\t\tconst bounds = createBounds2DRect(options);\n\t\t\t\t\twrapRef.fsm.update(\n\t\t\t\t\t\t{ x: pose.position[0], y: pose.position[1] },\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tminX: bounds.minX,\n\t\t\t\t\t\t\tmaxX: bounds.maxX,\n\t\t\t\t\t\t\tminY: bounds.minY,\n\t\t\t\t\t\t\tmaxY: bounds.maxY,\n\t\t\t\t\t\t\tedgeThreshold: options.edgeThreshold,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tquery.lastWrappedAxes !== 0,\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!gameEntity.body) continue;\n\t\t\tconst wrapped = this.wrapEntity(gameEntity, options);\n\t\t\tconst pos = gameEntity.body.translation();\n\t\t\tconst bounds = createBounds2DRect(options);\n\t\t\twrapRef.fsm.update(\n\t\t\t\t{ x: pos.x, y: pos.y },\n\t\t\t\t{\n\t\t\t\t\tminX: bounds.minX,\n\t\t\t\t\tmaxX: bounds.maxX,\n\t\t\t\t\tminY: bounds.minY,\n\t\t\t\t\tmaxY: bounds.maxY,\n\t\t\t\t\tedgeThreshold: options.edgeThreshold,\n\t\t\t\t},\n\t\t\t\twrapped,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate ensureWasmAttached(handle: number, options: ScreenWrapOptions): void {\n\t\tif (!this.wasmStage || this.attachedRuntimeSlots.has(handle)) return;\n\t\tthis.wasmStage.attachScreenWrap(handle, {\n\t\t\twidth: options.width,\n\t\t\theight: options.height,\n\t\t\tcenterX: options.centerX,\n\t\t\tcenterY: options.centerY,\n\t\t\tedgeThreshold: options.edgeThreshold,\n\t\t});\n\t\tthis.attachedRuntimeSlots.add(handle);\n\t}\n\n\tprivate wrapEntity(entity: any, options: ScreenWrapOptions): boolean {\n\t\tconst body = entity.body;\n\t\tif (!body) return false;\n\n\t\tconst pos = body.translation();\n\t\tconst bounds = createBounds2DRect(options);\n\t\tconst result = wrapPoint2D({ x: pos.x, y: pos.y }, bounds);\n\n\t\tif (result.wrapped) {\n\t\t\tbody.setTranslation({ x: result.x, y: result.y, z: pos.z }, true);\n\t\t}\n\n\t\treturn result.wrapped;\n\t}\n\n\tdestroy(_ecs: unknown): void {\n\t\tthis.attachedRuntimeSlots.clear();\n\t}\n}\n\n/**\n * ScreenWrapBehavior - Wraps entities around 2D bounds\n * \n * @example\n * ```typescript\n * import { ScreenWrapBehavior } from \"@zylem/game-lib/behavior\";\n * \n * const ship = createSprite({ ... });\n * const wrapRef = ship.use(ScreenWrapBehavior, { width: 20, height: 15 });\n * \n * // Access FSM to observe edge state\n * const fsm = wrapRef.getFSM();\n * console.log(fsm?.getState()); // 'center', 'near-edge-left', 'wrapped', etc.\n * ```\n */\nexport const ScreenWrapBehavior = defineBehavior({\n\tname: 'screen-wrap',\n\tdefaultOptions,\n\tsystemFactory: (ctx) =>\n\t\tnew ScreenWrapSystem(ctx.wasmStage ?? null, ctx.getBehaviorLinks),\n});\n"],"mappings":"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","names":["ScreenWrapState","ScreenWrapEvent","bounds"]}
@@ -2,17 +2,17 @@ import {
2
2
  GroundProbe3D,
3
3
  getGroundAnchorOffsetY,
4
4
  getGroundSnapTargetY
5
- } from "./chunk-UFHYR2LV.js";
5
+ } from "./chunk-JXMF4NUI.js";
6
6
  import {
7
7
  setVelocityIntent
8
- } from "./chunk-RQWWUML4.js";
8
+ } from "./chunk-OTLSUYQQ.js";
9
9
  import {
10
10
  SyncStateMachine,
11
11
  t
12
- } from "./chunk-RER2IDPW.js";
12
+ } from "./chunk-4URF65BR.js";
13
13
  import {
14
14
  defineBehavior
15
- } from "./chunk-YDLO5M7S.js";
15
+ } from "./chunk-EZNCCO4L.js";
16
16
 
17
17
  // src/lib/behaviors/jumper-2d/components.ts
18
18
  function createJumpConfig2D(options = {}) {
@@ -48,7 +48,7 @@ function createJumpState2D() {
48
48
  };
49
49
  }
50
50
 
51
- // src/lib/behaviors/jumper-2d/jumper-2d.behavior.ts
51
+ // src/lib/behaviors/jumper-2d/jumper-2d.system.ts
52
52
  function jumpVelocityFromHeight(gravity, height) {
53
53
  return Math.sqrt(2 * gravity * height);
54
54
  }
@@ -59,7 +59,7 @@ var Jumper2DTickEvent = /* @__PURE__ */ ((Jumper2DTickEvent3) => {
59
59
  Jumper2DTickEvent3["Land"] = "land";
60
60
  return Jumper2DTickEvent3;
61
61
  })(Jumper2DTickEvent || {});
62
- var Jumper2DBehavior = class {
62
+ var Jumper2DSystem = class {
63
63
  tick(config, input, ctx, state) {
64
64
  const dtMs = ctx.dt * 1e3;
65
65
  const now = performance.now();
@@ -218,11 +218,6 @@ var defaultOptions = {
218
218
  debugGroundProbe: false
219
219
  };
220
220
  var JUMPER_2D_BEHAVIOR_KEY = /* @__PURE__ */ Symbol.for("zylem:behavior:jumper-2d");
221
- var JUMPER_2D_OFFSETS = [
222
- { x: 0, z: 0 },
223
- { x: 0.35, z: 0 },
224
- { x: -0.35, z: 0 }
225
- ];
226
221
  function isJumper2DGrounded(params) {
227
222
  if (params.velocityY > 0) {
228
223
  return false;
@@ -237,22 +232,22 @@ function shouldSnapJumper2DToGround(support, options, velocityY) {
237
232
  }
238
233
  var Jumper2DBehaviorSystem = class {
239
234
  constructor(world, scene, wasmStage, getBehaviorLinks) {
240
- this.world = world;
241
235
  this.scene = scene;
242
236
  this.wasmStage = wasmStage;
243
237
  this.getBehaviorLinks = getBehaviorLinks;
244
238
  this.groundProbe = new GroundProbe3D(world);
245
239
  }
246
- world;
247
240
  scene;
248
241
  wasmStage;
249
242
  getBehaviorLinks;
250
- behavior = new Jumper2DBehavior();
243
+ behavior = new Jumper2DSystem();
251
244
  groundProbe;
252
245
  initializedEntities = /* @__PURE__ */ new Set();
253
246
  timeSinceGroundedMs = /* @__PURE__ */ new Map();
254
247
  wasJumpHeld = /* @__PURE__ */ new Map();
255
248
  attachedRuntimeSlots = /* @__PURE__ */ new Set();
249
+ /** Reused per-entity per-frame instead of spreading a fresh input object. */
250
+ effectiveInput = createJumpInput2D();
256
251
  update(_ecs, delta) {
257
252
  const links = this.getBehaviorLinks?.(JUMPER_2D_BEHAVIOR_KEY);
258
253
  if (!links) return;
@@ -292,7 +287,6 @@ var Jumper2DBehaviorSystem = class {
292
287
  const probeOriginYOffset = 0.05 - getGroundAnchorOffsetY(gameEntity);
293
288
  const support = this.groundProbe.probeSupport(gameEntity, {
294
289
  rayLength,
295
- offsets: JUMPER_2D_OFFSETS,
296
290
  mode: "any",
297
291
  debug: options.debugGroundProbe ?? false,
298
292
  scene: this.scene,
@@ -328,11 +322,11 @@ var Jumper2DBehaviorSystem = class {
328
322
  const state = gameEntity.jumper2dState;
329
323
  const previousHeld = this.wasJumpHeld.get(gameEntity.uuid) ?? false;
330
324
  const held = input.jumpHeld === true;
331
- const effectiveInput = {
332
- ...input,
333
- jumpPressed: input.jumpPressed || held && !previousHeld,
334
- jumpReleased: input.jumpReleased || !held && previousHeld
335
- };
325
+ const effectiveInput = this.effectiveInput;
326
+ effectiveInput.jumpPressed = input.jumpPressed || held && !previousHeld;
327
+ effectiveInput.jumpHeld = held;
328
+ effectiveInput.jumpReleased = input.jumpReleased || !held && previousHeld;
329
+ effectiveInput.fastFall = input.fastFall;
336
330
  const store = gameEntity.transformStore;
337
331
  const ctx = {
338
332
  dt: delta,
@@ -376,7 +370,7 @@ var Jumper2DBehaviorSystem = class {
376
370
  this.attachedRuntimeSlots.clear();
377
371
  }
378
372
  };
379
- var Jumper2D = defineBehavior({
373
+ var Jumper2DBehavior = defineBehavior({
380
374
  name: "jumper-2d",
381
375
  defaultOptions,
382
376
  systemFactory: (ctx) => new Jumper2DBehaviorSystem(ctx.world, ctx.scene, ctx.wasmStage ?? null, ctx.getBehaviorLinks),
@@ -397,9 +391,10 @@ export {
397
391
  createJumpInput2D,
398
392
  createJumpState2D,
399
393
  Jumper2DTickEvent,
400
- Jumper2DBehavior,
394
+ Jumper2DSystem,
401
395
  Jumper2DState,
402
396
  Jumper2DEvent,
403
397
  Jumper2DFSM,
404
- Jumper2D
398
+ Jumper2DBehavior
405
399
  };
400
+ //# sourceMappingURL=chunk-GTLA6LOJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/behaviors/jumper-2d/components.ts","../src/lib/behaviors/jumper-2d/jumper-2d.system.ts","../src/lib/behaviors/jumper-2d/jumper-2d-fsm.ts","../src/lib/behaviors/jumper-2d/jumper-2d.descriptor.ts"],"sourcesContent":["export interface JumpConfig2D {\n\tjumpHeight: number;\n\tgravity: number;\n\tmaxFallSpeed?: number;\n\tmaxJumps: number;\n\tresetJumpsOnGround: boolean;\n\tcoyoteTimeMs: number;\n\tjumpBufferMs: number;\n\tminTimeBetweenJumpsMs?: number;\n\tvariableJump?: {\n\t\tenabled: boolean;\n\t\tcutGravityMultiplier: number;\n\t\tmaxHoldMs?: number;\n\t};\n}\n\nexport function createJumpConfig2D(\n\toptions: Partial<JumpConfig2D> = {},\n): JumpConfig2D {\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\tcoyoteTimeMs: options.coyoteTimeMs ?? 100,\n\t\tjumpBufferMs: options.jumpBufferMs ?? 80,\n\t\tminTimeBetweenJumpsMs: options.minTimeBetweenJumpsMs,\n\t\tvariableJump: options.variableJump,\n\t};\n}\n\nexport interface JumpInput2D {\n\tjumpPressed: boolean;\n\tjumpHeld: boolean;\n\tjumpReleased: boolean;\n\tfastFall?: boolean;\n}\n\nexport function createJumpInput2D(): JumpInput2D {\n\treturn {\n\t\tjumpPressed: false,\n\t\tjumpHeld: false,\n\t\tjumpReleased: false,\n\t\tfastFall: false,\n\t};\n}\n\nexport interface JumpContext2D {\n\tdt: number;\n\tvelocityY: number;\n\tisGrounded: boolean;\n\ttimeSinceGroundedMs: number;\n\tsetVerticalVelocity(y: number): void;\n}\n\nexport interface JumpState2D {\n\tjumpsUsed: number;\n\tlastJumpTimeMs: number;\n\tbufferedJumpMs: number;\n\tcoyoteMs: number;\n\tisJumping: boolean;\n\tjumpHoldMs: number;\n\tjumpCutApplied: boolean;\n}\n\nexport function createJumpState2D(): JumpState2D {\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","import type {\n\tJumpConfig2D,\n\tJumpContext2D,\n\tJumpInput2D,\n\tJumpState2D,\n} from './components';\n\nfunction jumpVelocityFromHeight(gravity: number, height: number): number {\n\treturn Math.sqrt(2 * gravity * height);\n}\n\nexport enum Jumper2DTickEvent {\n\tNone = 'none',\n\tJump = 'jump',\n\tFall = 'fall',\n\tLand = 'land',\n}\n\nexport interface Jumper2DTickResult {\n\tevent: Jumper2DTickEvent;\n}\n\nexport class Jumper2DSystem {\n\ttick(\n\t\tconfig: JumpConfig2D,\n\t\tinput: JumpInput2D,\n\t\tctx: JumpContext2D,\n\t\tstate: JumpState2D,\n\t): Jumper2DTickResult {\n\t\tconst dtMs = ctx.dt * 1000;\n\t\tconst now = performance.now();\n\t\tlet event = Jumper2DTickEvent.None;\n\t\tlet jumpedThisFrame = false;\n\n\t\tif (ctx.isGrounded) {\n\t\t\tstate.coyoteMs = config.coyoteTimeMs;\n\t\t\tif (config.resetJumpsOnGround) {\n\t\t\t\tstate.jumpsUsed = 0;\n\t\t\t}\n\t\t\tif (state.isJumping) {\n\t\t\t\tstate.isJumping = false;\n\t\t\t\tstate.jumpCutApplied = false;\n\t\t\t\tevent = Jumper2DTickEvent.Land;\n\t\t\t}\n\t\t} else {\n\t\t\tstate.coyoteMs = Math.max(0, state.coyoteMs - dtMs);\n\t\t}\n\n\t\tif (input.jumpPressed) {\n\t\t\tstate.bufferedJumpMs = config.jumpBufferMs;\n\t\t} else {\n\t\t\tstate.bufferedJumpMs = Math.max(0, state.bufferedJumpMs - dtMs);\n\t\t}\n\n\t\tif (state.isJumping && input.jumpHeld) {\n\t\t\tstate.jumpHoldMs += dtMs;\n\t\t}\n\n\t\tconst wantsJump = state.bufferedJumpMs > 0;\n\t\tconst isFirstJump =\n\t\t\tctx.isGrounded || (state.coyoteMs > 0 && state.jumpsUsed === 0);\n\t\tconst hasAirJumps = state.jumpsUsed > 0 && state.jumpsUsed < config.maxJumps;\n\t\tconst canJump = isFirstJump || hasAirJumps;\n\t\tconst cooldownOk =\n\t\t\tconfig.minTimeBetweenJumpsMs == null ||\n\t\t\tnow - state.lastJumpTimeMs >= config.minTimeBetweenJumpsMs;\n\n\t\tif (wantsJump && canJump && cooldownOk) {\n\t\t\tctx.setVerticalVelocity(jumpVelocityFromHeight(config.gravity, config.jumpHeight));\n\t\t\tjumpedThisFrame = true;\n\t\t\tstate.jumpsUsed++;\n\t\t\tstate.lastJumpTimeMs = now;\n\t\t\tstate.bufferedJumpMs = 0;\n\t\t\tstate.coyoteMs = 0;\n\t\t\tstate.isJumping = true;\n\t\t\tstate.jumpHoldMs = 0;\n\t\t\tstate.jumpCutApplied = false;\n\t\t\tevent = Jumper2DTickEvent.Jump;\n\t\t}\n\n\t\tif (!ctx.isGrounded || jumpedThisFrame) {\n\t\t\tlet gravityMul = 1;\n\t\t\tconst isFalling = ctx.velocityY < 0;\n\n\t\t\tif (isFalling) {\n\t\t\t\tgravityMul = input.fastFall ? 1.75 : 1;\n\t\t\t\tif (event !== Jumper2DTickEvent.Land) {\n\t\t\t\t\tevent = Jumper2DTickEvent.Fall;\n\t\t\t\t}\n\t\t\t} else if (state.isJumping) {\n\t\t\t\tconst variableJump = config.variableJump;\n\t\t\t\tif (variableJump?.enabled && !state.jumpCutApplied) {\n\t\t\t\t\tconst holdExpired =\n\t\t\t\t\t\tvariableJump.maxHoldMs != null &&\n\t\t\t\t\t\tstate.jumpHoldMs >= variableJump.maxHoldMs;\n\t\t\t\t\tif (!input.jumpHeld || holdExpired) {\n\t\t\t\t\t\tgravityMul = variableJump.cutGravityMultiplier;\n\t\t\t\t\t\tstate.jumpCutApplied = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlet newVelocityY = ctx.velocityY - config.gravity * gravityMul * ctx.dt;\n\t\t\tif (config.maxFallSpeed != null) {\n\t\t\t\tnewVelocityY = Math.max(-config.maxFallSpeed, newVelocityY);\n\t\t\t}\n\t\t\tctx.setVerticalVelocity(newVelocityY);\n\t\t} else {\n\t\t\tctx.setVerticalVelocity(0);\n\t\t}\n\n\t\treturn { event };\n\t}\n}\n","import { SyncStateMachine, t } from '../../core/utility/sync-state-machine';\nimport type { JumpState2D } from './components';\n\nexport enum Jumper2DState {\n\tGrounded = 'grounded',\n\tJumping = 'jumping',\n\tFalling = 'falling',\n}\n\nexport enum Jumper2DEvent {\n\tJump = 'jump',\n\tFall = 'fall',\n\tLand = 'land',\n}\n\nexport interface Jumper2DFSMContext {\n\tstate: JumpState2D;\n}\n\nexport class Jumper2DFSM {\n\tmachine: SyncStateMachine<Jumper2DState, Jumper2DEvent, never>;\n\n\tconstructor(private ctx: Jumper2DFSMContext) {\n\t\tthis.machine = new SyncStateMachine<Jumper2DState, Jumper2DEvent, never>(\n\t\t\tJumper2DState.Grounded,\n\t\t\t[\n\t\t\t\tt(Jumper2DState.Grounded, Jumper2DEvent.Jump, Jumper2DState.Jumping),\n\t\t\t\tt(Jumper2DState.Grounded, Jumper2DEvent.Fall, Jumper2DState.Falling),\n\t\t\t\tt(Jumper2DState.Grounded, Jumper2DEvent.Land, Jumper2DState.Grounded),\n\t\t\t\tt(Jumper2DState.Jumping, Jumper2DEvent.Fall, Jumper2DState.Falling),\n\t\t\t\tt(Jumper2DState.Jumping, Jumper2DEvent.Land, Jumper2DState.Grounded),\n\t\t\t\tt(Jumper2DState.Jumping, Jumper2DEvent.Jump, Jumper2DState.Jumping),\n\t\t\t\tt(Jumper2DState.Falling, Jumper2DEvent.Land, Jumper2DState.Grounded),\n\t\t\t\tt(Jumper2DState.Falling, Jumper2DEvent.Jump, Jumper2DState.Jumping),\n\t\t\t\tt(Jumper2DState.Falling, Jumper2DEvent.Fall, Jumper2DState.Falling),\n\t\t\t],\n\t\t);\n\t}\n\n\tgetState(): Jumper2DState {\n\t\treturn this.machine.getState();\n\t}\n\n\tdispatch(event: Jumper2DEvent): void {\n\t\tif (this.machine.can(event)) {\n\t\t\tthis.machine.syncDispatch(event);\n\t\t}\n\t}\n\n\tisJumping(): boolean {\n\t\treturn this.getState() === Jumper2DState.Jumping;\n\t}\n\n\tisGrounded(): boolean {\n\t\treturn this.getState() === Jumper2DState.Grounded;\n\t}\n\n\tgetJumpsUsed(): number {\n\t\treturn this.ctx.state.jumpsUsed;\n\t}\n\n\tapplyTickEvent(tickEvent: string, isGrounded: boolean): void {\n\t\tswitch (tickEvent) {\n\t\t\tcase 'jump':\n\t\t\t\tthis.dispatch(Jumper2DEvent.Jump);\n\t\t\t\tbreak;\n\t\t\tcase 'fall':\n\t\t\t\tthis.dispatch(Jumper2DEvent.Fall);\n\t\t\t\tbreak;\n\t\t\tcase 'land':\n\t\t\t\tthis.dispatch(Jumper2DEvent.Land);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tif (!isGrounded && this.getState() === Jumper2DState.Grounded) {\n\t\t\t\t\tthis.dispatch(Jumper2DEvent.Fall);\n\t\t\t\t}\n\t\t\t\tif (isGrounded && this.getState() !== Jumper2DState.Grounded) {\n\t\t\t\t\tthis.dispatch(Jumper2DEvent.Land);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t}\n}\n","import { defineBehavior } from '../behavior-descriptor';\nimport type { BehaviorEntityLink, BehaviorSystem } from '../behavior-system';\nimport { setVelocityIntent } from '../../capabilities/velocity-intents';\nimport {\n\tGroundProbe3D,\n\tgetGroundAnchorOffsetY,\n\tgetGroundSnapTargetY,\n\ttype GroundProbeSupportHit,\n} from '../shared/ground-probe-3d';\nimport {\n\tcreateJumpConfig2D,\n\tcreateJumpInput2D,\n\tcreateJumpState2D,\n\ttype JumpConfig2D,\n\ttype JumpContext2D,\n\ttype JumpInput2D,\n\ttype JumpState2D,\n} from './components';\nimport {\n\tJumper2DSystem,\n\tJumper2DTickEvent,\n} from './jumper-2d.system';\nimport { Jumper2DFSM, Jumper2DState } from './jumper-2d-fsm';\nimport type { WasmStageRuntime } from '../../runtime/wasm-stage-runtime';\n\nexport interface Jumper2DBehaviorOptions {\n\tjumpHeight?: number;\n\tgravity?: number;\n\tmaxFallSpeed?: number;\n\tmaxJumps?: number;\n\tresetJumpsOnGround?: boolean;\n\tcoyoteTimeMs?: number;\n\tjumpBufferMs?: number;\n\tminTimeBetweenJumpsMs?: number;\n\tvariableJump?: JumpConfig2D['variableJump'];\n\tgroundRayLength?: number;\n\tsnapToGroundDistance?: number;\n\tdebugGroundProbe?: boolean;\n}\n\nconst defaultOptions: Jumper2DBehaviorOptions = {\n\tjumpHeight: 2.5,\n\tgravity: 20,\n\tmaxJumps: 1,\n\tresetJumpsOnGround: true,\n\tcoyoteTimeMs: 100,\n\tjumpBufferMs: 80,\n\tgroundRayLength: 1,\n\tdebugGroundProbe: false,\n};\n\nconst JUMPER_2D_BEHAVIOR_KEY = Symbol.for('zylem:behavior:jumper-2d');\n\nexport function isJumper2DGrounded(params: {\n\tnearGround?: boolean;\n\tvelocityY: number;\n\tsupportToi?: number | null;\n\tsnapToGroundDistance?: number;\n}): boolean {\n\tif (params.velocityY > 0) {\n\t\treturn false;\n\t}\n\n\tif (params.snapToGroundDistance != null) {\n\t\treturn (\n\t\t\tparams.supportToi != null &&\n\t\t\tparams.supportToi <= params.snapToGroundDistance\n\t\t);\n\t}\n\n\treturn params.nearGround === true;\n}\n\nfunction shouldSnapJumper2DToGround(\n\tsupport: GroundProbeSupportHit | null,\n\toptions: Jumper2DBehaviorOptions,\n\tvelocityY: number,\n): support is GroundProbeSupportHit {\n\treturn (\n\t\tsupport != null &&\n\t\toptions.snapToGroundDistance != null &&\n\t\tvelocityY <= 0 &&\n\t\tsupport.toi <= options.snapToGroundDistance\n\t);\n}\n\nexport interface Jumper2DEntity {\n\tuuid: string;\n\tbody: import('@dimforge/rapier3d-compat').RigidBody | null;\n\ttransformStore: import('../../capabilities/transform-store').TransformState;\n\tjumper2d: JumpConfig2D;\n\t$jumper2d: JumpInput2D;\n\tjumper2dState: JumpState2D;\n}\n\nclass Jumper2DBehaviorSystem implements BehaviorSystem {\n\tprivate behavior = new Jumper2DSystem();\n\tprivate groundProbe: GroundProbe3D;\n\tprivate initializedEntities = new Set<string>();\n\tprivate timeSinceGroundedMs = new Map<string, number>();\n\tprivate wasJumpHeld = new Map<string, boolean>();\n\tprivate attachedRuntimeSlots = new Set<number>();\n\t/** Reused per-entity per-frame instead of spreading a fresh input object. */\n\tprivate effectiveInput: JumpInput2D = createJumpInput2D();\n\n\tconstructor(\n\t\tworld: import('../../collision/world').ZylemWorld | null,\n\t\tprivate scene: any,\n\t\tprivate wasmStage: WasmStageRuntime | null,\n\t\tprivate getBehaviorLinks?: (key: symbol) => Iterable<BehaviorEntityLink>,\n\t) {\n\t\tthis.groundProbe = new GroundProbe3D(world);\n\t}\n\n\tupdate(_ecs: unknown, delta: number): void {\n\t\tconst links = this.getBehaviorLinks?.(JUMPER_2D_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 jumperRef = link.ref as any;\n\n\t\t\tconst options = jumperRef.options as Jumper2DBehaviorOptions;\n\t\t\tconst handle = (gameEntity.runtimeHandle ?? -1) as number;\n\t\t\tif (this.wasmStage && handle >= 0) {\n\t\t\t\tthis.ensureWasmAttached(handle, options);\n\t\t\t\tconst input = gameEntity.$jumper2d as JumpInput2D | undefined;\n\t\t\t\tconst jumpPressed = !!(input?.jumpPressed || input?.jumpHeld);\n\t\t\t\tthis.wasmStage.setJumper2DInput(handle, jumpPressed);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (!gameEntity.body) continue;\n\n\t\t\tif (!gameEntity.jumper2d) {\n\t\t\t\tgameEntity.jumper2d = createJumpConfig2D(options);\n\t\t\t}\n\t\t\tif (!gameEntity.$jumper2d) {\n\t\t\t\tgameEntity.$jumper2d = createJumpInput2D();\n\t\t\t}\n\t\t\tif (!gameEntity.jumper2dState) {\n\t\t\t\tgameEntity.jumper2dState = createJumpState2D();\n\t\t\t}\n\t\t\tif (!this.initializedEntities.has(gameEntity.uuid)) {\n\t\t\t\tthis.initializedEntities.add(gameEntity.uuid);\n\t\t\t\tgameEntity.body.setGravityScale(0, true);\n\t\t\t}\n\t\t\tif (!jumperRef.fsm && gameEntity.jumper2dState) {\n\t\t\t\tjumperRef.fsm = new Jumper2DFSM({\n\t\t\t\t\tstate: gameEntity.jumper2dState,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst rayLength = options.groundRayLength ?? 1;\n\t\t\tconst bodyVelocity = gameEntity.body.linvel();\n\t\t\tconst probeOriginYOffset = 0.05 - getGroundAnchorOffsetY(gameEntity);\n\t\t\tconst support = this.groundProbe.probeSupport(gameEntity, {\n\t\t\t\trayLength,\n\t\t\t\tmode: 'any',\n\t\t\t\tdebug: options.debugGroundProbe ?? false,\n\t\t\t\tscene: this.scene,\n\t\t\t\toriginYOffset: probeOriginYOffset,\n\t\t\t});\n\t\t\tconst nearGround = support != null;\n\t\t\tconst isGrounded = isJumper2DGrounded({\n\t\t\t\tnearGround,\n\t\t\t\tvelocityY: bodyVelocity.y,\n\t\t\t\tsupportToi: support?.toi ?? null,\n\t\t\t\tsnapToGroundDistance: options.snapToGroundDistance,\n\t\t\t});\n\t\t\tif (shouldSnapJumper2DToGround(support, options, bodyVelocity.y)) {\n\t\t\t\tconst currentPosition = gameEntity.body.translation();\n\t\t\t\tgameEntity.body.setTranslation(\n\t\t\t\t\t{\n\t\t\t\t\t\tx: currentPosition.x,\n\t\t\t\t\t\ty: getGroundSnapTargetY(gameEntity, support),\n\t\t\t\t\t\tz: currentPosition.z,\n\t\t\t\t\t},\n\t\t\t\t\ttrue,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tlet timeSinceGroundedMs = this.timeSinceGroundedMs.get(gameEntity.uuid) ?? 0;\n\t\t\tif (isGrounded) {\n\t\t\t\ttimeSinceGroundedMs = 0;\n\t\t\t} else {\n\t\t\t\ttimeSinceGroundedMs += delta * 1000;\n\t\t\t}\n\t\t\tthis.timeSinceGroundedMs.set(gameEntity.uuid, timeSinceGroundedMs);\n\n\t\t\tconst config: JumpConfig2D = gameEntity.jumper2d;\n\t\t\tconst input: JumpInput2D = gameEntity.$jumper2d;\n\t\t\tconst state: JumpState2D = gameEntity.jumper2dState;\n\t\t\tconst previousHeld = this.wasJumpHeld.get(gameEntity.uuid) ?? false;\n\t\t\tconst held = input.jumpHeld === true;\n\t\t\tconst effectiveInput = this.effectiveInput;\n\t\t\teffectiveInput.jumpPressed = input.jumpPressed || (held && !previousHeld);\n\t\t\teffectiveInput.jumpHeld = held;\n\t\t\teffectiveInput.jumpReleased = input.jumpReleased || (!held && previousHeld);\n\t\t\teffectiveInput.fastFall = input.fastFall;\n\n\t\t\tconst store = gameEntity.transformStore;\n\t\t\tconst ctx: JumpContext2D = {\n\t\t\t\tdt: delta,\n\t\t\t\tvelocityY: bodyVelocity.y,\n\t\t\t\tisGrounded,\n\t\t\t\ttimeSinceGroundedMs,\n\t\t\t\tsetVerticalVelocity: (y: number) => {\n\t\t\t\t\tsetVelocityIntent(\n\t\t\t\t\t\tstore,\n\t\t\t\t\t\t'jumper-2d',\n\t\t\t\t\t\t{ y },\n\t\t\t\t\t\t{ mode: 'replace', priority: 20 },\n\t\t\t\t\t);\n\t\t\t\t\tctx.velocityY = y;\n\t\t\t\t},\n\t\t\t};\n\n\t\t\tconst result = this.behavior.tick(config, effectiveInput, ctx, state);\n\t\t\tthis.wasJumpHeld.set(gameEntity.uuid, held);\n\n\t\t\tif (jumperRef.fsm) {\n\t\t\t\t(jumperRef.fsm as Jumper2DFSM).applyTickEvent(result.event, isGrounded);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate ensureWasmAttached(handle: number, options: Jumper2DBehaviorOptions): void {\n\t\tif (!this.wasmStage || this.attachedRuntimeSlots.has(handle)) return;\n\t\tconst jumpForce = Math.sqrt(2 * (options.gravity ?? 20) * (options.jumpHeight ?? 2.5));\n\t\tthis.wasmStage.attachJumper2D(handle, {\n\t\t\tjumpForce,\n\t\t\tmaxJumps: options.maxJumps ?? 1,\n\t\t\tjumpBufferTime: (options.jumpBufferMs ?? 80) / 1000,\n\t\t\tcoyoteTime: (options.coyoteTimeMs ?? 100) / 1000,\n\t\t\tgravity: options.gravity ?? 20,\n\t\t});\n\t\tthis.attachedRuntimeSlots.add(handle);\n\t}\n\n\tdestroy(_ecs: unknown): void {\n\t\tthis.groundProbe.destroy();\n\t\tthis.initializedEntities.clear();\n\t\tthis.timeSinceGroundedMs.clear();\n\t\tthis.wasJumpHeld.clear();\n\t\tthis.attachedRuntimeSlots.clear();\n\t}\n}\n\nexport const Jumper2DBehavior = defineBehavior<\n\tJumper2DBehaviorOptions,\n\t{\n\t\tgetState: () => Jumper2DState;\n\t\tisJumping: () => boolean;\n\t\tgetJumpsUsed: () => number;\n\t\tgetJumpsRemaining: () => number;\n\t},\n\tJumper2DEntity\n>({\n\tname: 'jumper-2d',\n\tdefaultOptions,\n\tsystemFactory: (ctx) =>\n\t\tnew Jumper2DBehaviorSystem(ctx.world, ctx.scene, ctx.wasmStage ?? null, ctx.getBehaviorLinks),\n\tcreateHandle: (ref) => ({\n\t\tgetState: () =>\n\t\t\t(ref.fsm as Jumper2DFSM | undefined)?.getState() ??\n\t\t\tJumper2DState.Grounded,\n\t\tisJumping: () =>\n\t\t\t(ref.fsm as Jumper2DFSM | undefined)?.isJumping() ?? false,\n\t\tgetJumpsUsed: () =>\n\t\t\t(ref.fsm as Jumper2DFSM | undefined)?.getJumpsUsed() ?? 0,\n\t\tgetJumpsRemaining: () => {\n\t\t\tconst maxJumps = ref.options.maxJumps ?? 1;\n\t\t\tconst used =\n\t\t\t\t(ref.fsm as Jumper2DFSM | undefined)?.getJumpsUsed() ?? 0;\n\t\t\treturn maxJumps - used;\n\t\t},\n\t}),\n});\n"],"mappings":"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","names":["Jumper2DTickEvent","Jumper2DState","Jumper2DEvent","input"]}
@@ -1,20 +1,18 @@
1
1
  import {
2
2
  createFirstPersonInputComponent
3
- } from "./chunk-4EWASZHB.js";
3
+ } from "./chunk-QVCMJ4Q2.js";
4
4
  import {
5
5
  createJumpInput3D
6
- } from "./chunk-F7E23E26.js";
6
+ } from "./chunk-4WH2ZN7P.js";
7
7
 
8
8
  // src/lib/coordinators/first-person-shooter.coordinator.ts
9
9
  var FirstPersonShooterCoordinator = class {
10
- constructor(entity, fpsController, jumper) {
10
+ constructor(entity, fpsController) {
11
11
  this.entity = entity;
12
12
  this.fpsController = fpsController;
13
- this.jumper = jumper;
14
13
  }
15
14
  entity;
16
15
  fpsController;
17
- jumper;
18
16
  update(input) {
19
17
  if (!this.entity.$fps) {
20
18
  this.entity.$fps = createFirstPersonInputComponent();
@@ -38,7 +36,6 @@ var FirstPersonShooterCoordinator = class {
38
36
  input.moveZ,
39
37
  this.fpsController.getYaw()
40
38
  );
41
- void this.jumper.getState();
42
39
  }
43
40
  getMoveDirWorld(moveX, moveZ, yaw) {
44
41
  const sinYaw = Math.sin(yaw);
@@ -64,3 +61,4 @@ var FirstPersonShooterCoordinator = class {
64
61
  export {
65
62
  FirstPersonShooterCoordinator
66
63
  };
64
+ //# sourceMappingURL=chunk-H4TJ7X4O.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/coordinators/first-person-shooter.coordinator.ts"],"sourcesContent":["import {\n\tcreateFirstPersonInputComponent,\n\ttype FirstPersonInputComponent,\n} from '../behaviors/first-person/components';\nimport {\n\tcreateJumpInput3D,\n\ttype JumpInput3D,\n} from '../behaviors/jumper-3d/components';\n\nexport interface FirstPersonShooterInput {\n\tmoveX: number;\n\tmoveZ: number;\n\tlookX: number;\n\tlookY: number;\n\tsprint: boolean;\n\tjumpPressed: boolean;\n\tjumpHeld: boolean;\n\tjumpReleased: boolean;\n\tfastFall?: boolean;\n}\n\ninterface FirstPersonShooterControllerHandle {\n\tgetYaw(): number;\n}\n\nexport type FirstPersonShooterEntity = {\n\t$fps?: FirstPersonInputComponent;\n\t$jumper?: JumpInput3D;\n};\n\n/**\n * Coordinator that fans a single input snapshot out to the first-person\n * behavior (`$fps`) and the Jumper3D behavior (`$jumper`), converting local\n * move axes into the camera-relative world direction the jumper uses for\n * air control.\n */\nexport class FirstPersonShooterCoordinator {\n\tconstructor(\n\t\tprivate entity: FirstPersonShooterEntity,\n\t\tprivate fpsController: FirstPersonShooterControllerHandle,\n\t) {}\n\n\tpublic update(input: FirstPersonShooterInput): void {\n\t\tif (!this.entity.$fps) {\n\t\t\tthis.entity.$fps = createFirstPersonInputComponent();\n\t\t}\n\t\tif (!this.entity.$jumper) {\n\t\t\tthis.entity.$jumper = createJumpInput3D();\n\t\t}\n\n\t\tconst fpsInput = this.entity.$fps;\n\t\tfpsInput.moveX = input.moveX;\n\t\tfpsInput.moveZ = input.moveZ;\n\t\tfpsInput.lookX = input.lookX;\n\t\tfpsInput.lookY = input.lookY;\n\t\tfpsInput.sprint = input.sprint;\n\n\t\tconst jumperInput = this.entity.$jumper;\n\t\tjumperInput.jumpPressed = input.jumpPressed;\n\t\tjumperInput.jumpHeld = input.jumpHeld;\n\t\tjumperInput.jumpReleased = input.jumpReleased;\n\t\tjumperInput.fastFall = input.fastFall ?? false;\n\t\tjumperInput.moveDirWorld = this.getMoveDirWorld(\n\t\t\tinput.moveX,\n\t\t\tinput.moveZ,\n\t\t\tthis.fpsController.getYaw(),\n\t\t);\n\t}\n\n\tprivate getMoveDirWorld(\n\t\tmoveX: number,\n\t\tmoveZ: number,\n\t\tyaw: number,\n\t): { x: number; y: number; z: number } | undefined {\n\t\tconst sinYaw = Math.sin(yaw);\n\t\tconst cosYaw = Math.cos(yaw);\n\t\tconst rightX = cosYaw;\n\t\tconst rightZ = -sinYaw;\n\t\tconst forwardX = -sinYaw;\n\t\tconst forwardZ = -cosYaw;\n\t\tconst worldX = rightX * moveX + forwardX * -moveZ;\n\t\tconst worldZ = rightZ * moveX + forwardZ * -moveZ;\n\t\tconst length = Math.hypot(worldX, worldZ);\n\t\tif (length <= 1e-6) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\treturn {\n\t\t\tx: worldX / length,\n\t\t\ty: 0,\n\t\t\tz: worldZ / length,\n\t\t};\n\t}\n}\n"],"mappings":";;;;;;;;AAoCO,IAAM,gCAAN,MAAoC;AAAA,EAC1C,YACS,QACA,eACP;AAFO;AACA;AAAA,EACN;AAAA,EAFM;AAAA,EACA;AAAA,EAGF,OAAO,OAAsC;AACnD,QAAI,CAAC,KAAK,OAAO,MAAM;AACtB,WAAK,OAAO,OAAO,gCAAgC;AAAA,IACpD;AACA,QAAI,CAAC,KAAK,OAAO,SAAS;AACzB,WAAK,OAAO,UAAU,kBAAkB;AAAA,IACzC;AAEA,UAAM,WAAW,KAAK,OAAO;AAC7B,aAAS,QAAQ,MAAM;AACvB,aAAS,QAAQ,MAAM;AACvB,aAAS,QAAQ,MAAM;AACvB,aAAS,QAAQ,MAAM;AACvB,aAAS,SAAS,MAAM;AAExB,UAAM,cAAc,KAAK,OAAO;AAChC,gBAAY,cAAc,MAAM;AAChC,gBAAY,WAAW,MAAM;AAC7B,gBAAY,eAAe,MAAM;AACjC,gBAAY,WAAW,MAAM,YAAY;AACzC,gBAAY,eAAe,KAAK;AAAA,MAC/B,MAAM;AAAA,MACN,MAAM;AAAA,MACN,KAAK,cAAc,OAAO;AAAA,IAC3B;AAAA,EACD;AAAA,EAEQ,gBACP,OACA,OACA,KACkD;AAClD,UAAM,SAAS,KAAK,IAAI,GAAG;AAC3B,UAAM,SAAS,KAAK,IAAI,GAAG;AAC3B,UAAM,SAAS;AACf,UAAM,SAAS,CAAC;AAChB,UAAM,WAAW,CAAC;AAClB,UAAM,WAAW,CAAC;AAClB,UAAM,SAAS,SAAS,QAAQ,WAAW,CAAC;AAC5C,UAAM,SAAS,SAAS,QAAQ,WAAW,CAAC;AAC5C,UAAM,SAAS,KAAK,MAAM,QAAQ,MAAM;AACxC,QAAI,UAAU,MAAM;AACnB,aAAO;AAAA,IACR;AAEA,WAAO;AAAA,MACN,GAAG,SAAS;AAAA,MACZ,GAAG;AAAA,MACH,GAAG,SAAS;AAAA,IACb;AAAA,EACD;AACD;","names":[]}
@@ -4,17 +4,17 @@ import {
4
4
  constrainMovementToBounds3D,
5
5
  hasAnyBounds3DHit,
6
6
  insetBounds3D
7
- } from "./chunk-WHKPWIQO.js";
7
+ } from "./chunk-OHCPV2NT.js";
8
8
  import {
9
9
  StageBoundaryDim
10
- } from "./chunk-YYJP62QD.js";
10
+ } from "./chunk-4V7TIRO2.js";
11
11
  import {
12
12
  SyncStateMachine,
13
13
  t
14
- } from "./chunk-RER2IDPW.js";
14
+ } from "./chunk-4URF65BR.js";
15
15
  import {
16
16
  defineBehavior
17
- } from "./chunk-YDLO5M7S.js";
17
+ } from "./chunk-EZNCCO4L.js";
18
18
 
19
19
  // src/lib/behaviors/world-boundary-3d/world-boundary-3d-fsm.ts
20
20
  var WorldBoundary3DState = /* @__PURE__ */ ((WorldBoundary3DState2) => {
@@ -135,12 +135,10 @@ function createWorldBoundary3DHandle(ref) {
135
135
  };
136
136
  }
137
137
  var WorldBoundary3DSystem = class {
138
- constructor(world, wasmStage, getBehaviorLinks) {
139
- this.world = world;
138
+ constructor(wasmStage, getBehaviorLinks) {
140
139
  this.wasmStage = wasmStage;
141
140
  this.getBehaviorLinks = getBehaviorLinks;
142
141
  }
143
- world;
144
142
  wasmStage;
145
143
  getBehaviorLinks;
146
144
  attachedRuntimeSlots = /* @__PURE__ */ new Set();
@@ -197,7 +195,7 @@ var WorldBoundary3DSystem = class {
197
195
  var WorldBoundary3DBehavior = defineBehavior({
198
196
  name: "world-boundary-3d",
199
197
  defaultOptions,
200
- systemFactory: (ctx) => new WorldBoundary3DSystem(ctx.world, ctx.wasmStage ?? null, ctx.getBehaviorLinks),
198
+ systemFactory: (ctx) => new WorldBoundary3DSystem(ctx.wasmStage ?? null, ctx.getBehaviorLinks),
201
199
  createHandle: createWorldBoundary3DHandle
202
200
  });
203
201
 
@@ -209,3 +207,4 @@ export {
209
207
  WorldBoundary3DFSM,
210
208
  WorldBoundary3DBehavior
211
209
  };
210
+ //# sourceMappingURL=chunk-HNESASBB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/behaviors/world-boundary-3d/world-boundary-3d-fsm.ts","../src/lib/behaviors/world-boundary-3d/world-boundary-3d.descriptor.ts"],"sourcesContent":["import { SyncStateMachine, t } from '../../core/utility/sync-state-machine';\nimport {\n\tclampPointToBounds3D,\n\tcomputeBounds3DHits,\n\tconstrainMovementToBounds3D,\n\thasAnyBounds3DHit,\n\tinsetBounds3D,\n\ttype Bounds3DPaddingInput,\n\ttype Bounds3DPoint,\n} from '../shared/bounds-3d';\n\nexport type WorldBoundary3DHit =\n\t| 'top'\n\t| 'bottom'\n\t| 'left'\n\t| 'right'\n\t| 'back'\n\t| 'front';\n\nexport type WorldBoundary3DHits = Record<WorldBoundary3DHit, boolean>;\n\nexport interface WorldBoundary3DPosition extends Bounds3DPoint {}\n\nexport interface WorldBoundary3DBounds {\n\ttop: number;\n\tbottom: number;\n\tleft: number;\n\tright: number;\n\tback: number;\n\tfront: number;\n}\n\nexport enum WorldBoundary3DState {\n\tInside = 'inside',\n\tTouching = 'touching',\n}\n\nexport enum WorldBoundary3DEvent {\n\tEnterInside = 'enter-inside',\n\tTouchBoundary = 'touch-boundary',\n}\n\nexport function computeWorldBoundary3DHits(\n\tposition: WorldBoundary3DPosition,\n\tbounds: WorldBoundary3DBounds,\n\tpadding?: Bounds3DPaddingInput,\n): WorldBoundary3DHits {\n\treturn computeBounds3DHits(position, insetBounds3D({\n\t\tminX: bounds.left,\n\t\tmaxX: bounds.right,\n\t\tminY: bounds.bottom,\n\t\tmaxY: bounds.top,\n\t\tminZ: bounds.back,\n\t\tmaxZ: bounds.front,\n\t}, padding));\n}\n\nexport function hasAnyWorldBoundary3DHit(hits: WorldBoundary3DHits): boolean {\n\treturn hasAnyBounds3DHit(hits);\n}\n\nexport class WorldBoundary3DFSM {\n\tpublic readonly machine: SyncStateMachine<WorldBoundary3DState, WorldBoundary3DEvent, never>;\n\n\tprivate lastHits: WorldBoundary3DHits = {\n\t\ttop: false,\n\t\tbottom: false,\n\t\tleft: false,\n\t\tright: false,\n\t\tback: false,\n\t\tfront: false,\n\t};\n\tprivate lastPosition: WorldBoundary3DPosition | null = null;\n\tprivate lastClampedPosition: WorldBoundary3DPosition | null = null;\n\tprivate lastUpdatedAtMs: number | null = null;\n\n\tconstructor() {\n\t\tthis.machine = new SyncStateMachine<WorldBoundary3DState, WorldBoundary3DEvent, never>(\n\t\t\tWorldBoundary3DState.Inside,\n\t\t\t[\n\t\t\t\tt(WorldBoundary3DState.Inside, WorldBoundary3DEvent.TouchBoundary, WorldBoundary3DState.Touching),\n\t\t\t\tt(WorldBoundary3DState.Touching, WorldBoundary3DEvent.EnterInside, WorldBoundary3DState.Inside),\n\t\t\t\tt(WorldBoundary3DState.Inside, WorldBoundary3DEvent.EnterInside, WorldBoundary3DState.Inside),\n\t\t\t\tt(WorldBoundary3DState.Touching, WorldBoundary3DEvent.TouchBoundary, WorldBoundary3DState.Touching),\n\t\t\t],\n\t\t);\n\t}\n\n\tgetState(): WorldBoundary3DState {\n\t\treturn this.machine.getState();\n\t}\n\n\tgetLastHits(): WorldBoundary3DHits {\n\t\treturn this.lastHits;\n\t}\n\n\tgetMovement(\n\t\tmoveX: number,\n\t\tmoveY: number,\n\t\tmoveZ: number,\n\t): { moveX: number; moveY: number; moveZ: number } {\n\t\treturn constrainMovementToBounds3D(this.lastHits, moveX, moveY, moveZ);\n\t}\n\n\tgetLastPosition(): WorldBoundary3DPosition | null {\n\t\treturn this.lastPosition;\n\t}\n\n\tgetLastClampedPosition(): WorldBoundary3DPosition | null {\n\t\treturn this.lastClampedPosition;\n\t}\n\n\tgetLastUpdatedAtMs(): number | null {\n\t\treturn this.lastUpdatedAtMs;\n\t}\n\n\tupdate(\n\t\tposition: WorldBoundary3DPosition,\n\t\tbounds: WorldBoundary3DBounds,\n\t\tpadding?: Bounds3DPaddingInput,\n\t): WorldBoundary3DHits {\n\t\tconst effectiveBounds = insetBounds3D({\n\t\t\tminX: bounds.left,\n\t\t\tmaxX: bounds.right,\n\t\t\tminY: bounds.bottom,\n\t\t\tmaxY: bounds.top,\n\t\t\tminZ: bounds.back,\n\t\t\tmaxZ: bounds.front,\n\t\t}, padding);\n\n\t\tconst hits = computeBounds3DHits(position, effectiveBounds);\n\t\tthis.lastHits = hits;\n\t\tthis.lastPosition = { x: position.x, y: position.y, z: position.z };\n\t\tthis.lastClampedPosition = clampPointToBounds3D(position, effectiveBounds);\n\t\tthis.lastUpdatedAtMs = Date.now();\n\n\t\tif (hasAnyWorldBoundary3DHit(hits)) {\n\t\t\tthis.dispatch(WorldBoundary3DEvent.TouchBoundary);\n\t\t} else {\n\t\t\tthis.dispatch(WorldBoundary3DEvent.EnterInside);\n\t\t}\n\n\t\treturn hits;\n\t}\n\n\tprivate dispatch(event: WorldBoundary3DEvent): void {\n\t\tif (this.machine.can(event)) {\n\t\t\tthis.machine.syncDispatch(event);\n\t\t}\n\t}\n}\n","import { defineBehavior, type BehaviorRef } from '../behavior-descriptor';\nimport type { BehaviorEntityLink, BehaviorSystem } from '../behavior-system';\nimport type { Bounds3DPaddingInput } from '../shared/bounds-3d';\nimport {\n\tWorldBoundary3DFSM,\n\ttype WorldBoundary3DBounds,\n\ttype WorldBoundary3DHits,\n\ttype WorldBoundary3DPosition,\n} from './world-boundary-3d-fsm';\nimport {\n\tStageBoundaryDim,\n\ttype WasmStageRuntime,\n} from '../../runtime/wasm-stage-runtime';\n\nexport interface WorldBoundary3DOptions {\n\tboundaries: WorldBoundary3DBounds;\n\tpadding?: Bounds3DPaddingInput;\n}\n\nexport interface WorldBoundary3DHandle {\n\tgetLastHits(): WorldBoundary3DHits | null;\n\tgetMovement(\n\t\tmoveX: number,\n\t\tmoveY: number,\n\t\tmoveZ: number,\n\t): { moveX: number; moveY: number; moveZ: number };\n\tgetLastClampedPosition(): WorldBoundary3DPosition | null;\n}\n\nconst defaultOptions: WorldBoundary3DOptions = {\n\tboundaries: { top: 0, bottom: 0, left: 0, right: 0, back: 0, front: 0 },\n\tpadding: 0,\n};\n\nconst WORLD_BOUNDARY_3D_BEHAVIOR_KEY = Symbol.for(\n\t'zylem:behavior:world-boundary-3d',\n);\n\nfunction createWorldBoundary3DHandle(\n\tref: BehaviorRef<WorldBoundary3DOptions>,\n): WorldBoundary3DHandle {\n\treturn {\n\t\tgetLastHits: () => {\n\t\t\tconst fsm = ref.fsm as WorldBoundary3DFSM | undefined;\n\t\t\treturn fsm?.getLastHits() ?? null;\n\t\t},\n\t\tgetMovement: (moveX: number, moveY: number, moveZ: number) => {\n\t\t\tconst fsm = ref.fsm as WorldBoundary3DFSM | undefined;\n\t\t\treturn fsm?.getMovement(moveX, moveY, moveZ) ?? { moveX, moveY, moveZ };\n\t\t},\n\t\tgetLastClampedPosition: () => {\n\t\t\tconst fsm = ref.fsm as WorldBoundary3DFSM | undefined;\n\t\t\treturn fsm?.getLastClampedPosition() ?? null;\n\t\t},\n\t};\n}\n\nclass WorldBoundary3DSystem 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?.(WORLD_BOUNDARY_3D_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 boundaryRef = link.ref as any;\n\t\t\tconst options = boundaryRef.options as WorldBoundary3DOptions;\n\t\t\tif (!boundaryRef.fsm) {\n\t\t\t\tboundaryRef.fsm = new WorldBoundary3DFSM();\n\t\t\t}\n\n\t\t\tconst handle = (gameEntity.runtimeHandle ?? -1) as number;\n\t\t\tif (this.wasmStage && handle >= 0) {\n\t\t\t\tthis.ensureWasmAttached(handle, options);\n\t\t\t\tconst query = this.wasmStage.queryWorldBoundary(handle);\n\t\t\t\tif (query) {\n\t\t\t\t\tboundaryRef.fsm.update(\n\t\t\t\t\t\t{ x: query.clamped[0], y: query.clamped[1], z: query.clamped[2] },\n\t\t\t\t\t\toptions.boundaries,\n\t\t\t\t\t\toptions.padding,\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!gameEntity.body) continue;\n\t\t\tconst pos = gameEntity.body.translation();\n\t\t\tboundaryRef.fsm.update(\n\t\t\t\t{ x: pos.x, y: pos.y, z: pos.z },\n\t\t\t\toptions.boundaries,\n\t\t\t\toptions.padding,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate ensureWasmAttached(handle: number, options: WorldBoundary3DOptions): void {\n\t\tif (!this.wasmStage || this.attachedRuntimeSlots.has(handle)) return;\n\t\tconst padding = typeof options.padding === 'number' ? options.padding : 0;\n\t\tthis.wasmStage.attachWorldBoundary(handle, StageBoundaryDim.Three, {\n\t\t\ttop: options.boundaries.top,\n\t\t\tbottom: options.boundaries.bottom,\n\t\t\tleft: options.boundaries.left,\n\t\t\tright: options.boundaries.right,\n\t\t\tfront: options.boundaries.front,\n\t\t\tback: options.boundaries.back,\n\t\t\tpadding,\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 WorldBoundary3DBehavior = defineBehavior({\n\tname: 'world-boundary-3d',\n\tdefaultOptions,\n\tsystemFactory: (ctx) =>\n\t\tnew WorldBoundary3DSystem(ctx.wasmStage ?? null, ctx.getBehaviorLinks),\n\tcreateHandle: createWorldBoundary3DHandle,\n});\n"],"mappings":"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","names":["WorldBoundary3DState","WorldBoundary3DEvent"]}
@@ -241,3 +241,4 @@ export {
241
241
  plainWorkerResultToTemplateParts,
242
242
  destructibleFragmentsToPlainResponse
243
243
  };
244
+ //# sourceMappingURL=chunk-HZMY3TT4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/destructible-prebake/source-geometry.ts","../src/lib/destructible-prebake/cache-key.ts","../src/lib/destructible-prebake/payload.ts"],"sourcesContent":["/**\n * Normalizes source mesh geometry for Voronoi prebake / fracture so runtime and\n * CLI tooling share one code path (float attributes, non-indexed, root-relative transform).\n */\n\nimport {\n\tBufferAttribute,\n\tBufferGeometry,\n\tMatrix4,\n\ttype Group,\n\ttype Mesh,\n} from 'three';\n\ntype ReadableGeometryAttribute = Pick<\n\tBufferAttribute,\n\t'count' | 'itemSize' | 'getComponent'\n>;\n\nfunction cloneAttributeAsFloat32(\n\tattribute: ReadableGeometryAttribute,\n): BufferAttribute {\n\tconst values = new Float32Array(attribute.count * attribute.itemSize);\n\tlet offset = 0;\n\tfor (let index = 0; index < attribute.count; index += 1) {\n\t\tfor (let component = 0; component < attribute.itemSize; component += 1) {\n\t\t\tvalues[offset] = attribute.getComponent(index, component);\n\t\t\toffset += 1;\n\t\t}\n\t}\n\n\treturn new BufferAttribute(values, attribute.itemSize);\n}\n\n/**\n * Clones geometry with float32 attributes and expands indexed meshes to non-indexed.\n */\nexport function normalizeFractureSourceGeometry(\n\tsourceGeometry: BufferGeometry,\n): BufferGeometry {\n\tconst geometry = sourceGeometry.clone();\n\tfor (const [name, attribute] of Object.entries(geometry.attributes)) {\n\t\tgeometry.setAttribute(name, cloneAttributeAsFloat32(attribute));\n\t}\n\n\tconst index = geometry.getIndex();\n\tif (!index) {\n\t\treturn geometry;\n\t}\n\n\tconst indices = new Uint32Array(index.count);\n\tfor (let cursor = 0; cursor < index.count; cursor += 1) {\n\t\tindices[cursor] = index.getX(cursor);\n\t}\n\n\tgeometry.setIndex(new BufferAttribute(indices, 1));\n\treturn geometry.toNonIndexed();\n}\n\n/**\n * Produces fracture-space geometry: normalized mesh vertices in root-local space.\n */\nexport function buildNormalizedFractureSourceGeometry(\n\tmesh: Mesh,\n\troot: Group,\n): BufferGeometry {\n\tconst geometry = normalizeFractureSourceGeometry(mesh.geometry);\n\troot.updateMatrixWorld(true);\n\tmesh.updateMatrixWorld(true);\n\n\tconst relativeMatrix = new Matrix4()\n\t\t.copy(root.matrixWorld)\n\t\t.invert()\n\t\t.multiply(mesh.matrixWorld);\n\n\tgeometry.applyMatrix4(relativeMatrix);\n\tgeometry.computeBoundingBox();\n\tgeometry.computeBoundingSphere();\n\treturn geometry;\n}\n","/**\n * Stable JSON key for matching prebaked fragment templates to fracture options.\n */\n\nimport type { FractureOptions as PinataFractureOptions } from '@dgreenheck/three-pinata';\nimport { Vector2, Vector3 } from 'three';\n\nfunction vector2ToTuple(value?: Vector2): [number, number] | null {\n\tif (!value) {\n\t\treturn null;\n\t}\n\n\treturn [value.x, value.y];\n}\n\nfunction vector3ToTuple(value?: Vector3): [number, number, number] | null {\n\tif (!value) {\n\t\treturn null;\n\t}\n\n\treturn [value.x, value.y, value.z];\n}\n\n/**\n * Serializes fracture options for prebake cache identity (mesh not included).\n */\nexport function buildFractureGeometryCacheKey(\n\toptions: PinataFractureOptions,\n): string {\n\treturn JSON.stringify({\n\t\tfractureMethod: options.fractureMethod,\n\t\tfragmentCount: options.fragmentCount,\n\t\tfracturePlanes: {\n\t\t\tx: options.fracturePlanes?.x ?? true,\n\t\t\ty: options.fracturePlanes?.y ?? true,\n\t\t\tz: options.fracturePlanes?.z ?? true,\n\t\t},\n\t\ttextureScale: vector2ToTuple(options.textureScale),\n\t\ttextureOffset: vector2ToTuple(options.textureOffset),\n\t\tseed: options.seed ?? null,\n\t\tvoronoiOptions: options.voronoiOptions\n\t\t\t? {\n\t\t\t\tmode: options.voronoiOptions.mode,\n\t\t\t\tseedPoints:\n\t\t\t\t\toptions.voronoiOptions.seedPoints?.map((point) => vector3ToTuple(point)),\n\t\t\t\tprojectionAxis: options.voronoiOptions.projectionAxis ?? null,\n\t\t\t\tprojectionNormal: vector3ToTuple(options.voronoiOptions.projectionNormal),\n\t\t\t\tuseApproximation: options.voronoiOptions.useApproximation ?? false,\n\t\t\t\tapproximationNeighborCount:\n\t\t\t\t\toptions.voronoiOptions.approximationNeighborCount ?? 12,\n\t\t\t}\n\t\t\t: null,\n\t});\n}\n","/**\n * Serializable payloads for destructible Voronoi prebake in a Web Worker.\n */\n\nimport { FractureOptions, type DestructibleMesh } from '@dgreenheck/three-pinata';\nimport {\n\tBufferAttribute,\n\tBufferGeometry,\n\tQuaternion,\n\tVector2,\n\tVector3,\n} from 'three';\n\nexport interface PlainBufferGeometryPayload {\n\tattributes: Record<\n\t\tstring,\n\t\t{\n\t\t\tarray: ArrayBuffer;\n\t\t\titemSize: number;\n\t\t\tnormalized?: boolean;\n\t\t}\n\t>;\n}\n\nexport type PlainFractureOptions = {\n\tfractureMethod: 'voronoi' | 'simple';\n\tfragmentCount: number;\n\tfracturePlanes: { x: boolean; y: boolean; z: boolean };\n\ttextureScale: [number, number];\n\ttextureOffset: [number, number];\n\tseed?: number;\n\tvoronoiOptions?: {\n\t\tmode: '3D' | '2.5D';\n\t\tseedPoints?: [number, number, number][];\n\t\timpactPoint?: [number, number, number];\n\t\timpactRadius?: number;\n\t\tprojectionAxis?: 'x' | 'y' | 'z' | 'auto' | null;\n\t\tprojectionNormal?: [number, number, number];\n\t\tuseApproximation: boolean;\n\t\tapproximationNeighborCount: number;\n\t};\n};\n\nexport type PlainWorkerFragmentPayload = {\n\tattributes: PlainBufferGeometryPayload['attributes'];\n\tposition: [number, number, number];\n\tquaternion: [number, number, number, number];\n\tscale: [number, number, number];\n};\n\nexport type PrebakeWorkerRequest = {\n\tgeometry: PlainBufferGeometryPayload;\n\toptions: PlainFractureOptions;\n};\n\nexport type PrebakeWorkerResponse = {\n\tfragments: PlainWorkerFragmentPayload[];\n};\n\nexport interface DestructiblePrebakeWorkerAPI {\n\tprebake(request: PrebakeWorkerRequest): Promise<PrebakeWorkerResponse>;\n}\n\n/**\n * Copies a buffer geometry into detached ArrayBuffers suitable for postMessage transfer.\n */\nexport function bufferGeometryToPlain(geometry: BufferGeometry): {\n\tpayload: PlainBufferGeometryPayload;\n\ttransferables: ArrayBuffer[];\n} {\n\tconst attributes: PlainBufferGeometryPayload['attributes'] = {};\n\tconst transferables: ArrayBuffer[] = [];\n\n\tfor (const name of Object.keys(geometry.attributes)) {\n\t\tconst attr = geometry.getAttribute(name);\n\t\tif (!attr || !(attr instanceof BufferAttribute)) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst view = attr.array as ArrayBufferView;\n\t\tconst buffer = view.buffer.slice(\n\t\t\tview.byteOffset,\n\t\t\tview.byteOffset + view.byteLength,\n\t\t);\n\t\ttransferables.push(buffer);\n\t\tattributes[name] = {\n\t\t\tarray: buffer,\n\t\t\titemSize: attr.itemSize,\n\t\t\tnormalized: attr.normalized,\n\t\t};\n\t}\n\n\treturn { payload: { attributes }, transferables };\n}\n\n/**\n * Rebuilds a BufferGeometry from a plain payload (e.g. in a worker).\n */\nexport function plainToBufferGeometry(\n\tpayload: PlainBufferGeometryPayload,\n): BufferGeometry {\n\tconst geometry = new BufferGeometry();\n\n\tfor (const [name, spec] of Object.entries(payload.attributes)) {\n\t\tconst array = new Float32Array(spec.array);\n\t\tgeometry.setAttribute(\n\t\t\tname,\n\t\t\tnew BufferAttribute(array, spec.itemSize, spec.normalized),\n\t\t);\n\t}\n\n\treturn geometry;\n}\n\n/**\n * Serializes {@link FractureOptions} for structured clone / worker transfer.\n */\nexport function pinataFractureOptionsToPlain(\n\toptions: FractureOptions,\n): PlainFractureOptions {\n\tconst voronoi = options.voronoiOptions;\n\treturn {\n\t\tfractureMethod: options.fractureMethod,\n\t\tfragmentCount: options.fragmentCount,\n\t\tfracturePlanes: {\n\t\t\tx: options.fracturePlanes.x,\n\t\t\ty: options.fracturePlanes.y,\n\t\t\tz: options.fracturePlanes.z,\n\t\t},\n\t\ttextureScale: [options.textureScale.x, options.textureScale.y],\n\t\ttextureOffset: [options.textureOffset.x, options.textureOffset.y],\n\t\tseed: options.seed,\n\t\tvoronoiOptions: voronoi\n\t\t\t? {\n\t\t\t\tmode: voronoi.mode,\n\t\t\t\tseedPoints: voronoi.seedPoints?.map((p) => [p.x, p.y, p.z]),\n\t\t\t\timpactPoint: voronoi.impactPoint\n\t\t\t\t\t? [voronoi.impactPoint.x, voronoi.impactPoint.y, voronoi.impactPoint.z]\n\t\t\t\t\t: undefined,\n\t\t\t\timpactRadius: voronoi.impactRadius,\n\t\t\t\tprojectionAxis: voronoi.projectionAxis ?? null,\n\t\t\t\tprojectionNormal: voronoi.projectionNormal\n\t\t\t\t\t? [\n\t\t\t\t\t\tvoronoi.projectionNormal.x,\n\t\t\t\t\t\tvoronoi.projectionNormal.y,\n\t\t\t\t\t\tvoronoi.projectionNormal.z,\n\t\t\t\t\t]\n\t\t\t\t\t: undefined,\n\t\t\t\tuseApproximation: voronoi.useApproximation ?? false,\n\t\t\t\tapproximationNeighborCount:\n\t\t\t\t\tvoronoi.approximationNeighborCount ?? 12,\n\t\t\t}\n\t\t\t: undefined,\n\t};\n}\n\n/**\n * Reconstructs {@link FractureOptions} from a plain payload.\n */\nexport function plainFractureOptionsToPinata(\n\tplain: PlainFractureOptions,\n): FractureOptions {\n\treturn new FractureOptions({\n\t\tfractureMethod: plain.fractureMethod,\n\t\tfragmentCount: plain.fragmentCount,\n\t\tfracturePlanes: plain.fracturePlanes,\n\t\ttextureScale: new Vector2(plain.textureScale[0], plain.textureScale[1]),\n\t\ttextureOffset: new Vector2(plain.textureOffset[0], plain.textureOffset[1]),\n\t\tseed: plain.seed,\n\t\tvoronoiOptions: plain.voronoiOptions\n\t\t\t? {\n\t\t\t\tmode: plain.voronoiOptions.mode,\n\t\t\t\tseedPoints: plain.voronoiOptions.seedPoints?.map(\n\t\t\t\t\t([x, y, z]) => new Vector3(x, y, z),\n\t\t\t\t),\n\t\t\t\timpactPoint: plain.voronoiOptions.impactPoint\n\t\t\t\t\t? new Vector3(\n\t\t\t\t\t\tplain.voronoiOptions.impactPoint[0],\n\t\t\t\t\t\tplain.voronoiOptions.impactPoint[1],\n\t\t\t\t\t\tplain.voronoiOptions.impactPoint[2],\n\t\t\t\t\t)\n\t\t\t\t\t: undefined,\n\t\t\t\timpactRadius: plain.voronoiOptions.impactRadius,\n\t\t\t\tprojectionAxis:\n\t\t\t\t\tplain.voronoiOptions.projectionAxis === null\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: plain.voronoiOptions.projectionAxis ?? undefined,\n\t\t\t\tprojectionNormal: plain.voronoiOptions.projectionNormal\n\t\t\t\t\t? new Vector3(\n\t\t\t\t\t\tplain.voronoiOptions.projectionNormal[0],\n\t\t\t\t\t\tplain.voronoiOptions.projectionNormal[1],\n\t\t\t\t\t\tplain.voronoiOptions.projectionNormal[2],\n\t\t\t\t\t)\n\t\t\t\t\t: undefined,\n\t\t\t\tuseApproximation: plain.voronoiOptions.useApproximation,\n\t\t\t\tapproximationNeighborCount:\n\t\t\t\t\tplain.voronoiOptions.approximationNeighborCount,\n\t\t\t}\n\t\t\t: undefined,\n\t});\n}\n\n/**\n * Converts worker response fragments into template parts for the main-thread cache.\n */\nexport function plainWorkerResultToTemplateParts(response: PrebakeWorkerResponse): Array<{\n\tgeometry: BufferGeometry;\n\tposition: Vector3;\n\tquaternion: Quaternion;\n\tscale: Vector3;\n}> {\n\treturn response.fragments.map((fragment) => {\n\t\tconst geometry = plainToBufferGeometry({ attributes: fragment.attributes });\n\t\tgeometry.computeBoundingBox();\n\t\tgeometry.computeBoundingSphere();\n\t\treturn {\n\t\t\tgeometry,\n\t\t\tposition: new Vector3(\n\t\t\t\tfragment.position[0],\n\t\t\t\tfragment.position[1],\n\t\t\t\tfragment.position[2],\n\t\t\t),\n\t\t\tquaternion: new Quaternion(\n\t\t\t\tfragment.quaternion[0],\n\t\t\t\tfragment.quaternion[1],\n\t\t\t\tfragment.quaternion[2],\n\t\t\t\tfragment.quaternion[3],\n\t\t\t),\n\t\t\tscale: new Vector3(\n\t\t\t\tfragment.scale[0],\n\t\t\t\tfragment.scale[1],\n\t\t\t\tfragment.scale[2],\n\t\t\t),\n\t\t};\n\t});\n}\n\n/**\n * Serializes fracture fragments for Comlink.transfer (used in the worker).\n */\nexport function destructibleFragmentsToPlainResponse(\n\tfragments: readonly DestructibleMesh[],\n): {\n\tresponse: PrebakeWorkerResponse;\n\ttransferables: ArrayBuffer[];\n} {\n\tconst out: PlainWorkerFragmentPayload[] = [];\n\tconst transferables: ArrayBuffer[] = [];\n\n\tfor (const fragment of fragments) {\n\t\tconst { payload, transferables: geoTransfer } = bufferGeometryToPlain(\n\t\t\tfragment.geometry,\n\t\t);\n\t\tfor (const buffer of geoTransfer) {\n\t\t\ttransferables.push(buffer);\n\t\t}\n\t\tout.push({\n\t\t\tattributes: payload.attributes,\n\t\t\tposition: [fragment.position.x, fragment.position.y, fragment.position.z],\n\t\t\tquaternion: [\n\t\t\t\tfragment.quaternion.x,\n\t\t\t\tfragment.quaternion.y,\n\t\t\t\tfragment.quaternion.z,\n\t\t\t\tfragment.quaternion.w,\n\t\t\t],\n\t\t\tscale: [fragment.scale.x, fragment.scale.y, fragment.scale.z],\n\t\t});\n\t\tfragment.geometry.dispose();\n\t}\n\n\treturn { response: { fragments: out }, transferables };\n}\n"],"mappings":";AAKA;AAAA,EACC;AAAA,EAEA;AAAA,OAGM;AAOP,SAAS,wBACR,WACkB;AAClB,QAAM,SAAS,IAAI,aAAa,UAAU,QAAQ,UAAU,QAAQ;AACpE,MAAI,SAAS;AACb,WAAS,QAAQ,GAAG,QAAQ,UAAU,OAAO,SAAS,GAAG;AACxD,aAAS,YAAY,GAAG,YAAY,UAAU,UAAU,aAAa,GAAG;AACvE,aAAO,MAAM,IAAI,UAAU,aAAa,OAAO,SAAS;AACxD,gBAAU;AAAA,IACX;AAAA,EACD;AAEA,SAAO,IAAI,gBAAgB,QAAQ,UAAU,QAAQ;AACtD;AAKO,SAAS,gCACf,gBACiB;AACjB,QAAM,WAAW,eAAe,MAAM;AACtC,aAAW,CAAC,MAAM,SAAS,KAAK,OAAO,QAAQ,SAAS,UAAU,GAAG;AACpE,aAAS,aAAa,MAAM,wBAAwB,SAAS,CAAC;AAAA,EAC/D;AAEA,QAAM,QAAQ,SAAS,SAAS;AAChC,MAAI,CAAC,OAAO;AACX,WAAO;AAAA,EACR;AAEA,QAAM,UAAU,IAAI,YAAY,MAAM,KAAK;AAC3C,WAAS,SAAS,GAAG,SAAS,MAAM,OAAO,UAAU,GAAG;AACvD,YAAQ,MAAM,IAAI,MAAM,KAAK,MAAM;AAAA,EACpC;AAEA,WAAS,SAAS,IAAI,gBAAgB,SAAS,CAAC,CAAC;AACjD,SAAO,SAAS,aAAa;AAC9B;AAKO,SAAS,sCACf,MACA,MACiB;AACjB,QAAM,WAAW,gCAAgC,KAAK,QAAQ;AAC9D,OAAK,kBAAkB,IAAI;AAC3B,OAAK,kBAAkB,IAAI;AAE3B,QAAM,iBAAiB,IAAI,QAAQ,EACjC,KAAK,KAAK,WAAW,EACrB,OAAO,EACP,SAAS,KAAK,WAAW;AAE3B,WAAS,aAAa,cAAc;AACpC,WAAS,mBAAmB;AAC5B,WAAS,sBAAsB;AAC/B,SAAO;AACR;;;ACvEA,SAAS,eAAe,OAA0C;AACjE,MAAI,CAAC,OAAO;AACX,WAAO;AAAA,EACR;AAEA,SAAO,CAAC,MAAM,GAAG,MAAM,CAAC;AACzB;AAEA,SAAS,eAAe,OAAkD;AACzE,MAAI,CAAC,OAAO;AACX,WAAO;AAAA,EACR;AAEA,SAAO,CAAC,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAClC;AAKO,SAAS,8BACf,SACS;AACT,SAAO,KAAK,UAAU;AAAA,IACrB,gBAAgB,QAAQ;AAAA,IACxB,eAAe,QAAQ;AAAA,IACvB,gBAAgB;AAAA,MACf,GAAG,QAAQ,gBAAgB,KAAK;AAAA,MAChC,GAAG,QAAQ,gBAAgB,KAAK;AAAA,MAChC,GAAG,QAAQ,gBAAgB,KAAK;AAAA,IACjC;AAAA,IACA,cAAc,eAAe,QAAQ,YAAY;AAAA,IACjD,eAAe,eAAe,QAAQ,aAAa;AAAA,IACnD,MAAM,QAAQ,QAAQ;AAAA,IACtB,gBAAgB,QAAQ,iBACrB;AAAA,MACD,MAAM,QAAQ,eAAe;AAAA,MAC7B,YACC,QAAQ,eAAe,YAAY,IAAI,CAAC,UAAU,eAAe,KAAK,CAAC;AAAA,MACxE,gBAAgB,QAAQ,eAAe,kBAAkB;AAAA,MACzD,kBAAkB,eAAe,QAAQ,eAAe,gBAAgB;AAAA,MACxE,kBAAkB,QAAQ,eAAe,oBAAoB;AAAA,MAC7D,4BACC,QAAQ,eAAe,8BAA8B;AAAA,IACvD,IACE;AAAA,EACJ,CAAC;AACF;;;ACjDA,SAAS,uBAA8C;AACvD;AAAA,EACC,mBAAAA;AAAA,EACA,kBAAAC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AAuDA,SAAS,sBAAsB,UAGpC;AACD,QAAM,aAAuD,CAAC;AAC9D,QAAM,gBAA+B,CAAC;AAEtC,aAAW,QAAQ,OAAO,KAAK,SAAS,UAAU,GAAG;AACpD,UAAM,OAAO,SAAS,aAAa,IAAI;AACvC,QAAI,CAAC,QAAQ,EAAE,gBAAgBD,mBAAkB;AAChD;AAAA,IACD;AACA,UAAM,OAAO,KAAK;AAClB,UAAM,SAAS,KAAK,OAAO;AAAA,MAC1B,KAAK;AAAA,MACL,KAAK,aAAa,KAAK;AAAA,IACxB;AACA,kBAAc,KAAK,MAAM;AACzB,eAAW,IAAI,IAAI;AAAA,MAClB,OAAO;AAAA,MACP,UAAU,KAAK;AAAA,MACf,YAAY,KAAK;AAAA,IAClB;AAAA,EACD;AAEA,SAAO,EAAE,SAAS,EAAE,WAAW,GAAG,cAAc;AACjD;AAKO,SAAS,sBACf,SACiB;AACjB,QAAM,WAAW,IAAIC,gBAAe;AAEpC,aAAW,CAAC,MAAM,IAAI,KAAK,OAAO,QAAQ,QAAQ,UAAU,GAAG;AAC9D,UAAM,QAAQ,IAAI,aAAa,KAAK,KAAK;AACzC,aAAS;AAAA,MACR;AAAA,MACA,IAAID,iBAAgB,OAAO,KAAK,UAAU,KAAK,UAAU;AAAA,IAC1D;AAAA,EACD;AAEA,SAAO;AACR;AAKO,SAAS,6BACf,SACuB;AACvB,QAAM,UAAU,QAAQ;AACxB,SAAO;AAAA,IACN,gBAAgB,QAAQ;AAAA,IACxB,eAAe,QAAQ;AAAA,IACvB,gBAAgB;AAAA,MACf,GAAG,QAAQ,eAAe;AAAA,MAC1B,GAAG,QAAQ,eAAe;AAAA,MAC1B,GAAG,QAAQ,eAAe;AAAA,IAC3B;AAAA,IACA,cAAc,CAAC,QAAQ,aAAa,GAAG,QAAQ,aAAa,CAAC;AAAA,IAC7D,eAAe,CAAC,QAAQ,cAAc,GAAG,QAAQ,cAAc,CAAC;AAAA,IAChE,MAAM,QAAQ;AAAA,IACd,gBAAgB,UACb;AAAA,MACD,MAAM,QAAQ;AAAA,MACd,YAAY,QAAQ,YAAY,IAAI,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;AAAA,MAC1D,aAAa,QAAQ,cAClB,CAAC,QAAQ,YAAY,GAAG,QAAQ,YAAY,GAAG,QAAQ,YAAY,CAAC,IACpE;AAAA,MACH,cAAc,QAAQ;AAAA,MACtB,gBAAgB,QAAQ,kBAAkB;AAAA,MAC1C,kBAAkB,QAAQ,mBACvB;AAAA,QACD,QAAQ,iBAAiB;AAAA,QACzB,QAAQ,iBAAiB;AAAA,QACzB,QAAQ,iBAAiB;AAAA,MAC1B,IACE;AAAA,MACH,kBAAkB,QAAQ,oBAAoB;AAAA,MAC9C,4BACC,QAAQ,8BAA8B;AAAA,IACxC,IACE;AAAA,EACJ;AACD;AAKO,SAAS,6BACf,OACkB;AAClB,SAAO,IAAI,gBAAgB;AAAA,IAC1B,gBAAgB,MAAM;AAAA,IACtB,eAAe,MAAM;AAAA,IACrB,gBAAgB,MAAM;AAAA,IACtB,cAAc,IAAI,QAAQ,MAAM,aAAa,CAAC,GAAG,MAAM,aAAa,CAAC,CAAC;AAAA,IACtE,eAAe,IAAI,QAAQ,MAAM,cAAc,CAAC,GAAG,MAAM,cAAc,CAAC,CAAC;AAAA,IACzE,MAAM,MAAM;AAAA,IACZ,gBAAgB,MAAM,iBACnB;AAAA,MACD,MAAM,MAAM,eAAe;AAAA,MAC3B,YAAY,MAAM,eAAe,YAAY;AAAA,QAC5C,CAAC,CAAC,GAAG,GAAG,CAAC,MAAM,IAAI,QAAQ,GAAG,GAAG,CAAC;AAAA,MACnC;AAAA,MACA,aAAa,MAAM,eAAe,cAC/B,IAAI;AAAA,QACL,MAAM,eAAe,YAAY,CAAC;AAAA,QAClC,MAAM,eAAe,YAAY,CAAC;AAAA,QAClC,MAAM,eAAe,YAAY,CAAC;AAAA,MACnC,IACE;AAAA,MACH,cAAc,MAAM,eAAe;AAAA,MACnC,gBACC,MAAM,eAAe,mBAAmB,OACrC,SACA,MAAM,eAAe,kBAAkB;AAAA,MAC3C,kBAAkB,MAAM,eAAe,mBACpC,IAAI;AAAA,QACL,MAAM,eAAe,iBAAiB,CAAC;AAAA,QACvC,MAAM,eAAe,iBAAiB,CAAC;AAAA,QACvC,MAAM,eAAe,iBAAiB,CAAC;AAAA,MACxC,IACE;AAAA,MACH,kBAAkB,MAAM,eAAe;AAAA,MACvC,4BACC,MAAM,eAAe;AAAA,IACvB,IACE;AAAA,EACJ,CAAC;AACF;AAKO,SAAS,iCAAiC,UAK9C;AACF,SAAO,SAAS,UAAU,IAAI,CAAC,aAAa;AAC3C,UAAM,WAAW,sBAAsB,EAAE,YAAY,SAAS,WAAW,CAAC;AAC1E,aAAS,mBAAmB;AAC5B,aAAS,sBAAsB;AAC/B,WAAO;AAAA,MACN;AAAA,MACA,UAAU,IAAI;AAAA,QACb,SAAS,SAAS,CAAC;AAAA,QACnB,SAAS,SAAS,CAAC;AAAA,QACnB,SAAS,SAAS,CAAC;AAAA,MACpB;AAAA,MACA,YAAY,IAAI;AAAA,QACf,SAAS,WAAW,CAAC;AAAA,QACrB,SAAS,WAAW,CAAC;AAAA,QACrB,SAAS,WAAW,CAAC;AAAA,QACrB,SAAS,WAAW,CAAC;AAAA,MACtB;AAAA,MACA,OAAO,IAAI;AAAA,QACV,SAAS,MAAM,CAAC;AAAA,QAChB,SAAS,MAAM,CAAC;AAAA,QAChB,SAAS,MAAM,CAAC;AAAA,MACjB;AAAA,IACD;AAAA,EACD,CAAC;AACF;AAKO,SAAS,qCACf,WAIC;AACD,QAAM,MAAoC,CAAC;AAC3C,QAAM,gBAA+B,CAAC;AAEtC,aAAW,YAAY,WAAW;AACjC,UAAM,EAAE,SAAS,eAAe,YAAY,IAAI;AAAA,MAC/C,SAAS;AAAA,IACV;AACA,eAAW,UAAU,aAAa;AACjC,oBAAc,KAAK,MAAM;AAAA,IAC1B;AACA,QAAI,KAAK;AAAA,MACR,YAAY,QAAQ;AAAA,MACpB,UAAU,CAAC,SAAS,SAAS,GAAG,SAAS,SAAS,GAAG,SAAS,SAAS,CAAC;AAAA,MACxE,YAAY;AAAA,QACX,SAAS,WAAW;AAAA,QACpB,SAAS,WAAW;AAAA,QACpB,SAAS,WAAW;AAAA,QACpB,SAAS,WAAW;AAAA,MACrB;AAAA,MACA,OAAO,CAAC,SAAS,MAAM,GAAG,SAAS,MAAM,GAAG,SAAS,MAAM,CAAC;AAAA,IAC7D,CAAC;AACD,aAAS,SAAS,QAAQ;AAAA,EAC3B;AAEA,SAAO,EAAE,UAAU,EAAE,WAAW,IAAI,GAAG,cAAc;AACtD;","names":["BufferAttribute","BufferGeometry"]}
@@ -27,3 +27,4 @@ export {
27
27
  createThrusterInputComponent,
28
28
  createThrusterStateComponent
29
29
  };
30
+ //# sourceMappingURL=chunk-IZJFUAXB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/behaviors/thruster/components.ts"],"sourcesContent":["/**\n * Thruster-specific ECS Components\n * \n * These components are specific to the thruster movement system.\n */\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ThrusterMovementComponent (capability / hardware spec)\n// Defines the thrust capabilities of an entity\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ThrusterMovementComponent {\n\t/** Linear thrust force in Newtons (or scaled units) */\n\tlinearThrust: number;\n\t/** Angular thrust torque scalar */\n\tangularThrust: number;\n\t/** Optional linear damping override */\n\tlinearDamping?: number;\n\t/** Optional angular damping override */\n\tangularDamping?: number;\n}\n\nexport function createThrusterMovementComponent(\n\tlinearThrust: number,\n\tangularThrust: number,\n\toptions?: { linearDamping?: number; angularDamping?: number }\n): ThrusterMovementComponent {\n\treturn {\n\t\tlinearThrust,\n\t\tangularThrust,\n\t\tlinearDamping: options?.linearDamping,\n\t\tangularDamping: options?.angularDamping,\n\t};\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ThrusterInputComponent (intent)\n// Written by: Player controller, AI controller, FSM adapter\n// Read by: ThrusterSystem\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ThrusterInputComponent {\n\t/** Forward thrust intent: 0..1 */\n\tthrust: number;\n\t/** Rotation intent: -1..1 */\n\trotate: number;\n\t/** World-space X thrust intent: -1..1 */\n\tthrustX: number;\n\t/** World-space Y thrust intent: -1..1 */\n\tthrustY: number;\n}\n\nexport function createThrusterInputComponent(): ThrusterInputComponent {\n\treturn {\n\t\tthrust: 0,\n\t\trotate: 0,\n\t\tthrustX: 0,\n\t\tthrustY: 0,\n\t};\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ThrusterStateComponent (optional but useful)\n// Useful for: damage, EMP, overheating, UI feedback\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ThrusterStateComponent {\n\t/** Whether the thruster is enabled */\n\tenabled: boolean;\n\t/** Current thrust after FSM/gating */\n\tcurrentThrust: number;\n}\n\nexport function createThrusterStateComponent(): ThrusterStateComponent {\n\treturn {\n\t\tenabled: true,\n\t\tcurrentThrust: 0,\n\t};\n}\n"],"mappings":";AAsBO,SAAS,gCACf,cACA,eACA,SAC4B;AAC5B,SAAO;AAAA,IACN;AAAA,IACA;AAAA,IACA,eAAe,SAAS;AAAA,IACxB,gBAAgB,SAAS;AAAA,EAC1B;AACD;AAmBO,SAAS,+BAAuD;AACtE,SAAO;AAAA,IACN,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,SAAS;AAAA,EACV;AACD;AAcO,SAAS,+BAAuD;AACtE,SAAO;AAAA,IACN,SAAS;AAAA,IACT,eAAe;AAAA,EAChB;AACD;","names":[]}