@zylem/game-lib 0.6.2 → 0.6.4

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 (82) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +9 -16
  3. package/dist/actions.d.ts +30 -21
  4. package/dist/actions.js +793 -146
  5. package/dist/actions.js.map +1 -1
  6. package/dist/behavior/jumper-2d.d.ts +114 -0
  7. package/dist/behavior/jumper-2d.js +711 -0
  8. package/dist/behavior/jumper-2d.js.map +1 -0
  9. package/dist/behavior/platformer-3d.d.ts +296 -0
  10. package/dist/behavior/platformer-3d.js +761 -0
  11. package/dist/behavior/platformer-3d.js.map +1 -0
  12. package/dist/behavior/ricochet-2d.d.ts +275 -0
  13. package/dist/behavior/ricochet-2d.js +425 -0
  14. package/dist/behavior/ricochet-2d.js.map +1 -0
  15. package/dist/behavior/ricochet-3d.d.ts +117 -0
  16. package/dist/behavior/ricochet-3d.js +443 -0
  17. package/dist/behavior/ricochet-3d.js.map +1 -0
  18. package/dist/behavior/screen-visibility.d.ts +79 -0
  19. package/dist/behavior/screen-visibility.js +358 -0
  20. package/dist/behavior/screen-visibility.js.map +1 -0
  21. package/dist/behavior/screen-wrap.d.ts +87 -0
  22. package/dist/behavior/screen-wrap.js +246 -0
  23. package/dist/behavior/screen-wrap.js.map +1 -0
  24. package/dist/behavior/shooter-2d.d.ts +79 -0
  25. package/dist/behavior/shooter-2d.js +180 -0
  26. package/dist/behavior/shooter-2d.js.map +1 -0
  27. package/dist/behavior/thruster.d.ts +11 -0
  28. package/dist/behavior/thruster.js +292 -0
  29. package/dist/behavior/thruster.js.map +1 -0
  30. package/dist/behavior/top-down-movement.d.ts +56 -0
  31. package/dist/behavior/top-down-movement.js +125 -0
  32. package/dist/behavior/top-down-movement.js.map +1 -0
  33. package/dist/behavior/world-boundary-2d.d.ts +142 -0
  34. package/dist/behavior/world-boundary-2d.js +235 -0
  35. package/dist/behavior/world-boundary-2d.js.map +1 -0
  36. package/dist/behavior/world-boundary-3d.d.ts +76 -0
  37. package/dist/behavior/world-boundary-3d.js +274 -0
  38. package/dist/behavior/world-boundary-3d.js.map +1 -0
  39. package/dist/behavior-descriptor-BXnVR8Ki.d.ts +159 -0
  40. package/dist/{blueprints-Cq3Ko6_G.d.ts → blueprints-DmbK2dki.d.ts} +2 -2
  41. package/dist/camera-4XO5gbQH.d.ts +905 -0
  42. package/dist/camera.d.ts +3 -2
  43. package/dist/camera.js +1653 -377
  44. package/dist/camera.js.map +1 -1
  45. package/dist/composition-BASvMKrW.d.ts +218 -0
  46. package/dist/{core-bO8TzV7u.d.ts → core-CARRaS55.d.ts} +110 -69
  47. package/dist/core.d.ts +11 -6
  48. package/dist/core.js +10766 -5626
  49. package/dist/core.js.map +1 -1
  50. package/dist/{entities-DvByhMGU.d.ts → entities-ChFirVL9.d.ts} +133 -29
  51. package/dist/entities.d.ts +5 -3
  52. package/dist/entities.js +4679 -3202
  53. package/dist/entities.js.map +1 -1
  54. package/dist/entity-vj-HTjzU.d.ts +1169 -0
  55. package/dist/global-change-2JvMaz44.d.ts +25 -0
  56. package/dist/main.d.ts +1118 -16
  57. package/dist/main.js +17538 -8499
  58. package/dist/main.js.map +1 -1
  59. package/dist/physics-pose-DCc4oE44.d.ts +25 -0
  60. package/dist/physics-protocol-BDD3P5W2.d.ts +200 -0
  61. package/dist/physics-worker.d.ts +21 -0
  62. package/dist/physics-worker.js +306 -0
  63. package/dist/physics-worker.js.map +1 -0
  64. package/dist/physics.d.ts +205 -0
  65. package/dist/physics.js +577 -0
  66. package/dist/physics.js.map +1 -0
  67. package/dist/stage-types-C19IhuzA.d.ts +731 -0
  68. package/dist/stage.d.ts +11 -7
  69. package/dist/stage.js +8024 -3852
  70. package/dist/stage.js.map +1 -1
  71. package/dist/sync-state-machine-CZyspBpj.d.ts +16 -0
  72. package/dist/thruster-23lzoPZd.d.ts +180 -0
  73. package/dist/world-DfgxoNMt.d.ts +105 -0
  74. package/package.json +53 -13
  75. package/dist/behaviors.d.ts +0 -854
  76. package/dist/behaviors.js +0 -1209
  77. package/dist/behaviors.js.map +0 -1
  78. package/dist/camera-CeJPAgGg.d.ts +0 -116
  79. package/dist/moveable-B_vyA6cw.d.ts +0 -67
  80. package/dist/stage-types-Bd-KtcYT.d.ts +0 -375
  81. package/dist/transformable-CUhvyuYO.d.ts +0 -67
  82. package/dist/world-C8tQ7Plj.d.ts +0 -774
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/behaviors/platformer-3d/components.ts","../../src/lib/core/utility/sync-state-machine.ts","../../src/lib/behaviors/platformer-3d/platformer-3d-fsm.ts","../../src/lib/behaviors/shared/ground-probe-3d.ts","../../src/lib/physics/serialize-descriptors.ts","../../src/lib/behaviors/platformer-3d/platformer-3d.behavior.ts","../../src/lib/behaviors/behavior-descriptor.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/** Ray length for ground detection */\n\tgroundRayLength: 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/** Enable ground-ray debug visualization */\n\tdebugGroundProbe: boolean;\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\tgroundRayLength: options.groundRayLength ?? 1.0,\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\tdebugGroundProbe: options.debugGroundProbe ?? false,\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}\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};\n}\n","import {\n\tStateMachine as BaseStateMachine,\n\ttype ILogger,\n\ttype ITransition,\n\ttype SyncCallback,\n} from 'typescript-fsm';\n\nexport { t } from 'typescript-fsm';\nexport type { ILogger, ITransition, SyncCallback };\n\n/**\n * Local wrapper around typescript-fsm's SyncStateMachine.\n *\n * typescript-fsm@1.6.0 incorrectly reports callback-less transitions as\n * unhandled even after moving to the next state, which causes noisy\n * `No transition...` console errors for valid FSM dispatches.\n */\nexport class SyncStateMachine<\n\tSTATE extends string | number | symbol,\n\tEVENT extends string | number | symbol,\n\tCALLBACK extends Record<EVENT, SyncCallback> = Record<EVENT, SyncCallback>,\n> extends BaseStateMachine<STATE, EVENT, CALLBACK> {\n\tconstructor(\n\t\tinit: STATE,\n\t\ttransitions: ITransition<STATE, EVENT, CALLBACK[EVENT]>[] = [],\n\t\tlogger: ILogger = console,\n\t) {\n\t\tsuper(init, transitions, logger);\n\t}\n\n\tdispatch<E extends EVENT>(_event: E, ..._args: unknown[]): Promise<void> {\n\t\tthrow new Error('SyncStateMachine does not support async dispatch.');\n\t}\n\n\tsyncDispatch<E extends EVENT>(event: E, ...args: unknown[]): boolean {\n\t\tconst found = this.transitions.some((transition) => {\n\t\t\tif (transition.fromState !== this._current || transition.event !== event) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst current = this._current;\n\t\t\tthis._current = transition.toState;\n\n\t\t\tif (!transition.cb) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\ttransition.cb(...args);\n\t\t\t\treturn true;\n\t\t\t} catch (error) {\n\t\t\t\tthis._current = current;\n\t\t\t\tthis.logger.error('Exception in callback', error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t});\n\n\t\tif (!found) {\n\t\t\tconst errorMessage = this.formatErr(this._current, event);\n\t\t\tthis.logger.error(errorMessage);\n\t\t}\n\n\t\treturn found;\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","import { Ray } from '@dimforge/rapier3d-compat';\nimport { BufferGeometry, Line, LineBasicMaterial, Vector3 } from 'three';\nimport { serializeColliderDesc } from '../../physics/serialize-descriptors';\n\nexport interface GroundProbeOffset {\n\tx: number;\n\tz: number;\n}\n\nexport type GroundProbeMode = 'center' | 'any';\n\nexport interface GroundProbeEntity {\n\tuuid: string;\n\tbody: any;\n}\n\nexport interface GroundProbeOptions {\n\trayLength: number;\n\toffsets?: readonly GroundProbeOffset[];\n\tmode?: GroundProbeMode;\n\tdebug?: boolean;\n\tscene?: any;\n\toriginYOffset?: number;\n}\n\nexport interface GroundProbeSupportHit {\n\ttoi: number;\n\tpoint: {\n\t\tx: number;\n\t\ty: number;\n\t\tz: number;\n\t};\n\torigin: {\n\t\tx: number;\n\t\ty: number;\n\t\tz: number;\n\t};\n\trayIndex: number;\n\tcolliderUuid?: string;\n}\n\nexport const GROUND_SNAP_EPSILON = 0.001;\n\nconst DEFAULT_OFFSETS: readonly GroundProbeOffset[] = [\n\t{ x: 0, z: 0 },\n\t{ x: 0.4, z: 0.4 },\n\t{ x: -0.4, z: 0.4 },\n\t{ x: 0.4, z: -0.4 },\n\t{ x: -0.4, z: -0.4 },\n] as const;\n\nexport class GroundProbe3D {\n\tprivate rays = new Map<string, Ray[]>();\n\tprivate debugLines = new Map<string, Line[]>();\n\n\tconstructor(private world: any) {}\n\n\tprobeSupport(\n\t\tentity: GroundProbeEntity,\n\t\toptions: GroundProbeOptions,\n\t): GroundProbeSupportHit | null {\n\t\tif (!this.world?.world || !entity.body) return null;\n\n\t\tconst mode = options.mode ?? 'any';\n\t\tconst offsets =\n\t\t\tmode === 'center'\n\t\t\t\t? (options.offsets ?? DEFAULT_OFFSETS).slice(0, 1)\n\t\t\t\t: (options.offsets ?? DEFAULT_OFFSETS);\n\t\tconst translation = entity.body.translation();\n\t\tconst rays = this.getOrCreateRays(entity.uuid, offsets.length);\n\t\tconst originYOffset = options.originYOffset ?? 0;\n\t\tlet support: GroundProbeSupportHit | null = null;\n\n\t\tfor (let index = 0; index < offsets.length; index++) {\n\t\t\tconst offset = offsets[index];\n\t\t\tconst ray = rays[index];\n\n\t\t\tray.origin = {\n\t\t\t\tx: translation.x + offset.x,\n\t\t\t\ty: translation.y + originYOffset,\n\t\t\t\tz: translation.z + offset.z,\n\t\t\t};\n\t\t\tray.dir = { x: 0, y: -1, z: 0 };\n\n\t\t\tconst hit = this.world.world.castRay(\n\t\t\t\tray,\n\t\t\t\toptions.rayLength,\n\t\t\t\ttrue,\n\t\t\t\tundefined,\n\t\t\t\tundefined,\n\t\t\t\tundefined,\n\t\t\t\tentity.body,\n\t\t\t);\n\t\t\tif (!hit) continue;\n\n\t\t\tconst nextSupport: GroundProbeSupportHit = {\n\t\t\t\ttoi: hit.toi,\n\t\t\t\tpoint: {\n\t\t\t\t\tx: ray.origin.x + ray.dir.x * hit.toi,\n\t\t\t\t\ty: ray.origin.y + ray.dir.y * hit.toi,\n\t\t\t\t\tz: ray.origin.z + ray.dir.z * hit.toi,\n\t\t\t\t},\n\t\t\t\torigin: {\n\t\t\t\t\tx: ray.origin.x,\n\t\t\t\t\ty: ray.origin.y,\n\t\t\t\t\tz: ray.origin.z,\n\t\t\t\t},\n\t\t\t\trayIndex: index,\n\t\t\t\tcolliderUuid: hit.collider?._parent?.userData?.uuid,\n\t\t\t};\n\n\t\t\tif (mode === 'center') {\n\t\t\t\tsupport = nextSupport;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (!support || nextSupport.toi < support.toi) {\n\t\t\t\tsupport = nextSupport;\n\t\t\t}\n\t\t}\n\n\t\tif (options.debug && options.scene) {\n\t\t\tthis.updateDebugLines(\n\t\t\t\tentity.uuid,\n\t\t\t\trays,\n\t\t\t\tBoolean(support),\n\t\t\t\toptions.rayLength,\n\t\t\t\toptions.scene,\n\t\t\t);\n\t\t} else {\n\t\t\tthis.disposeDebugLines(entity.uuid);\n\t\t}\n\n\t\treturn support;\n\t}\n\n\tdetect(entity: GroundProbeEntity, options: GroundProbeOptions): boolean {\n\t\treturn this.probeSupport(entity, options) != null;\n\t}\n\n\tdestroyEntity(uuid: string): void {\n\t\tthis.rays.delete(uuid);\n\t\tthis.disposeDebugLines(uuid);\n\t}\n\n\tdestroy(): void {\n\t\tthis.rays.clear();\n\t\tfor (const uuid of this.debugLines.keys()) {\n\t\t\tthis.disposeDebugLines(uuid);\n\t\t}\n\t\tthis.debugLines.clear();\n\t}\n\n\tprivate getOrCreateRays(uuid: string, count: number): Ray[] {\n\t\tlet rays = this.rays.get(uuid);\n\t\tif (!rays || rays.length !== count) {\n\t\t\trays = Array.from(\n\t\t\t\t{ length: count },\n\t\t\t\t() => new Ray({ x: 0, y: 0, z: 0 }, { x: 0, y: -1, z: 0 }),\n\t\t\t);\n\t\t\tthis.rays.set(uuid, rays);\n\t\t}\n\t\treturn rays;\n\t}\n\n\tprivate updateDebugLines(\n\t\tuuid: string,\n\t\trays: Ray[],\n\t\thasGround: boolean,\n\t\tlength: number,\n\t\tscene: any,\n\t): void {\n\t\tlet lines = this.debugLines.get(uuid);\n\t\tif (!lines) {\n\t\t\tlines = rays.map(() => {\n\t\t\t\tconst geometry = new BufferGeometry().setFromPoints([\n\t\t\t\t\tnew Vector3(),\n\t\t\t\t\tnew Vector3(),\n\t\t\t\t]);\n\t\t\t\tconst material = new LineBasicMaterial({ color: 0xff0000 });\n\t\t\t\tconst line = new Line(geometry, material);\n\t\t\t\tscene.add(line);\n\t\t\t\treturn line;\n\t\t\t});\n\t\t\tthis.debugLines.set(uuid, lines);\n\t\t}\n\n\t\trays.forEach((ray, index) => {\n\t\t\tconst line = lines![index];\n\t\t\tconst start = new Vector3(ray.origin.x, ray.origin.y, ray.origin.z);\n\t\t\tconst end = new Vector3(\n\t\t\t\tray.origin.x + ray.dir.x * length,\n\t\t\t\tray.origin.y + ray.dir.y * length,\n\t\t\t\tray.origin.z + ray.dir.z * length,\n\t\t\t);\n\t\t\tline.visible = true;\n\t\t\tline.geometry.setFromPoints([start, end]);\n\t\t\t(line.material as LineBasicMaterial).color.setHex(\n\t\t\t\thasGround ? 0x00ff00 : 0xff0000,\n\t\t\t);\n\t\t});\n\t}\n\n\tprivate disposeDebugLines(uuid: string): void {\n\t\tconst lines = this.debugLines.get(uuid);\n\t\tif (!lines) return;\n\n\t\tfor (const line of lines) {\n\t\t\tline.removeFromParent();\n\t\t\tline.geometry.dispose();\n\t\t\t(line.material as LineBasicMaterial).dispose();\n\t\t}\n\n\t\tthis.debugLines.delete(uuid);\n\t}\n}\n\nexport function getGroundAnchorOffsetY(entity: any): number {\n\tconst runtimeColliderDesc = entity?.colliderDesc;\n\tif (runtimeColliderDesc) {\n\t\tconst serialized = serializeColliderDesc(runtimeColliderDesc);\n\t\tconst centerY = serialized.translation?.[1] ?? 0;\n\t\tif (serialized.shape === 'capsule' && serialized.dimensions.length >= 2) {\n\t\t\tconst halfCylinder = serialized.dimensions[0] ?? 0;\n\t\t\tconst radius = serialized.dimensions[1] ?? 0;\n\t\t\treturn halfCylinder + radius - centerY;\n\t\t}\n\t\tif (serialized.shape === 'cuboid' && serialized.dimensions.length >= 2) {\n\t\t\tconst halfHeight = serialized.dimensions[1] ?? 0;\n\t\t\treturn halfHeight - centerY;\n\t\t}\n\t}\n\n\tconst collisionSize =\n\t\tentity?.options?.collision?.size ??\n\t\tentity?.options?.collisionSize ??\n\t\tentity?.options?.size;\n\tconst height = collisionSize?.y ?? 0;\n\tif (height <= 0) {\n\t\treturn 0;\n\t}\n\n\tconst collisionPosition =\n\t\tentity?.options?.collision?.position ??\n\t\tentity?.options?.collisionPosition;\n\tconst centerY = collisionPosition?.y ?? height / 2;\n\treturn (height / 2) - centerY;\n}\n\nexport function getGroundSnapTargetY(\n\tentity: any,\n\tsupport: GroundProbeSupportHit,\n\tepsilon: number = GROUND_SNAP_EPSILON,\n): number {\n\treturn support.point.y + getGroundAnchorOffsetY(entity) + epsilon;\n}\n","import type { RigidBodyDesc, ColliderDesc } from '@dimforge/rapier3d-compat';\nimport { RigidBodyType } from '@dimforge/rapier3d-compat';\nimport type {\n\tSerializableBodyDesc,\n\tSerializableBodyType,\n\tSerializableColliderDesc,\n\tColliderShapeKind,\n\tSerializableCharacterController,\n} from './physics-protocol';\n\n/**\n * Convert a Rapier RigidBodyDesc to a plain serializable object.\n *\n * Rapier descriptors are WASM-backed class instances that cannot cross\n * the postMessage boundary. This extracts the relevant data into a\n * JSON-safe format that the worker can reconstruct.\n */\nexport function serializeBodyDesc(desc: RigidBodyDesc): SerializableBodyDesc {\n\tconst status = (desc as any).status as number;\n\tlet type: SerializableBodyType = 'dynamic';\n\tif (status === RigidBodyType.Fixed) type = 'fixed';\n\telse if (status === RigidBodyType.KinematicPositionBased) type = 'kinematicPositionBased';\n\telse if (status === RigidBodyType.KinematicVelocityBased) type = 'kinematicVelocityBased';\n\n\tconst t = (desc as any).translation ?? { x: 0, y: 0, z: 0 };\n\n\treturn {\n\t\ttype,\n\t\ttranslation: [t.x ?? 0, t.y ?? 0, t.z ?? 0],\n\t\tgravityScale: (desc as any).gravityScale ?? 1,\n\t\tcanSleep: (desc as any).canSleep ?? false,\n\t\tccdEnabled: (desc as any).ccdEnabled ?? true,\n\t};\n}\n\n/**\n * Convert a Rapier ColliderDesc to a plain serializable object.\n *\n * Because Rapier's ColliderDesc uses opaque shape enums and internal\n * buffers, we infer the shape from the descriptor's internal type tag\n * and extract the numeric dimensions.\n */\nexport function serializeColliderDesc(desc: ColliderDesc): SerializableColliderDesc {\n\tconst internal = desc as any;\n\tconst customShapeData = internal.__zylemShapeData as\n\t\t| { shape: 'trimesh'; vertices: number[]; indices: number[] }\n\t\t| undefined;\n\tif (customShapeData?.shape === 'trimesh') {\n\t\tconst result: SerializableColliderDesc = {\n\t\t\tshape: 'trimesh',\n\t\t\tdimensions: [],\n\t\t\tvertices: [...customShapeData.vertices],\n\t\t\tindices: [...customShapeData.indices],\n\t\t};\n\n\t\tconst translation = internal.translation;\n\t\tif (\n\t\t\ttranslation\n\t\t\t&& (translation.x !== 0 || translation.y !== 0 || translation.z !== 0)\n\t\t) {\n\t\t\tresult.translation = [translation.x, translation.y, translation.z];\n\t\t}\n\n\t\tif (internal.isSensor) {\n\t\t\tresult.sensor = true;\n\t\t}\n\n\t\tif (internal.collisionGroups !== undefined && internal.collisionGroups !== 0xFFFFFFFF) {\n\t\t\tresult.collisionGroups = internal.collisionGroups;\n\t\t}\n\n\t\tif (internal.activeCollisionTypes !== undefined) {\n\t\t\tresult.activeCollisionTypes = internal.activeCollisionTypes;\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tconst shapeType = internal.shape?.type ?? internal.shapeType ?? 0;\n\n\tconst { shape, dimensions, heightfieldMeta } = extractShapeData(shapeType, internal);\n\n\tconst result: SerializableColliderDesc = {\n\t\tshape,\n\t\tdimensions,\n\t};\n\n\tconst t = internal.translation;\n\tif (t && (t.x !== 0 || t.y !== 0 || t.z !== 0)) {\n\t\tresult.translation = [t.x, t.y, t.z];\n\t}\n\n\tif (internal.isSensor) {\n\t\tresult.sensor = true;\n\t}\n\n\tif (internal.collisionGroups !== undefined && internal.collisionGroups !== 0xFFFFFFFF) {\n\t\tresult.collisionGroups = internal.collisionGroups;\n\t}\n\n\tif (internal.activeCollisionTypes !== undefined) {\n\t\tresult.activeCollisionTypes = internal.activeCollisionTypes;\n\t}\n\n\tif (heightfieldMeta) {\n\t\tresult.heightfieldMeta = heightfieldMeta;\n\t}\n\n\treturn result;\n}\n\n/**\n * Create a serializable character controller descriptor.\n */\nexport function serializeCharacterController(): SerializableCharacterController {\n\treturn {\n\t\toffset: 0.01,\n\t\tmaxSlopeClimbAngle: 45 * Math.PI / 180,\n\t\tminSlopeSlideAngle: 30 * Math.PI / 180,\n\t\tsnapToGroundDistance: 0.01,\n\t\tslideEnabled: true,\n\t\tapplyImpulsesToDynamic: true,\n\t\tcharacterMass: 1,\n\t};\n}\n\n// ─── Shape Extraction ──────────────────────────────────────────────────────\n\n/**\n * Rapier shape type enum values (from rapier internals):\n * 0 = Ball, 1 = Cuboid, 2 = Capsule, 6 = Cone, 7 = Cylinder,\n * 11 = HeightField\n */\nfunction extractShapeData(\n\tshapeType: number,\n\tinternal: any,\n): { shape: ColliderShapeKind; dimensions: number[]; heightfieldMeta?: { nrows: number; ncols: number } } {\n\tswitch (shapeType) {\n\t\tcase 0: // Ball\n\t\t\treturn {\n\t\t\t\tshape: 'ball',\n\t\t\t\tdimensions: [internal.shape?.radius ?? internal.halfExtents?.x ?? 1],\n\t\t\t};\n\t\tcase 1: // Cuboid\n\t\t\treturn {\n\t\t\t\tshape: 'cuboid',\n\t\t\t\tdimensions: [\n\t\t\t\t\tinternal.shape?.halfExtents?.x ?? internal.halfExtents?.x ?? 0.5,\n\t\t\t\t\tinternal.shape?.halfExtents?.y ?? internal.halfExtents?.y ?? 0.5,\n\t\t\t\t\tinternal.shape?.halfExtents?.z ?? internal.halfExtents?.z ?? 0.5,\n\t\t\t\t],\n\t\t\t};\n\t\tcase 2: // Capsule\n\t\t\treturn {\n\t\t\t\tshape: 'capsule',\n\t\t\t\tdimensions: [\n\t\t\t\t\tinternal.shape?.halfHeight ?? 0.5,\n\t\t\t\t\tinternal.shape?.radius ?? 0.5,\n\t\t\t\t],\n\t\t\t};\n\t\tcase 6: // Cone\n\t\t\treturn {\n\t\t\t\tshape: 'cone',\n\t\t\t\tdimensions: [\n\t\t\t\t\tinternal.shape?.halfHeight ?? 1,\n\t\t\t\t\tinternal.shape?.radius ?? 1,\n\t\t\t\t],\n\t\t\t};\n\t\tcase 7: // Cylinder\n\t\t\treturn {\n\t\t\t\tshape: 'cylinder',\n\t\t\t\tdimensions: [\n\t\t\t\t\tinternal.shape?.halfHeight ?? 1,\n\t\t\t\t\tinternal.shape?.radius ?? 1,\n\t\t\t\t],\n\t\t\t};\n\t\tcase 11: { // HeightField\n\t\t\tconst nrows = internal.shape?.nrows ?? 10;\n\t\t\tconst ncols = internal.shape?.ncols ?? 10;\n\t\t\tconst heights = internal.shape?.heights;\n\t\t\treturn {\n\t\t\t\tshape: 'heightfield',\n\t\t\t\tdimensions: heights ? Array.from(heights) : [],\n\t\t\t\theightfieldMeta: { nrows, ncols },\n\t\t\t};\n\t\t}\n\t\tdefault:\n\t\t\treturn { shape: 'cuboid', dimensions: [0.5, 0.5, 0.5] };\n\t}\n}\n","/**\n * Platformer 3D Behavior System\n * \n * Handles 3D platformer physics including:\n * - Ground detection via raycasting\n * - Horizontal movement (walk/run)\n * - Jumping with multi-jump support\n * - Gravity application\n * - State synchronization with FSM\n */\n\nimport { GroundProbe3D, getGroundAnchorOffsetY } from '../shared/ground-probe-3d';\nimport type {\n\tPlatformer3DMovementComponent,\n\tPlatformer3DInputComponent,\n\tPlatformer3DStateComponent,\n} from './components';\n\n/**\n * Entity with platformer components\n */\nexport interface Platformer3DEntity {\n\tuuid: string;\n\tbody: any; // RigidBody\n\tplatformer: Platformer3DMovementComponent;\n\t$platformer: Platformer3DInputComponent;\n\tplatformerState: Platformer3DStateComponent;\n\ttransformStore?: any;\n}\n\n/**\n * Platformer 3D Movement Behavior\n * \n * Core physics system for 3D platformer movement\n */\nexport class Platformer3DBehavior {\n\tprivate world: any;\n\tprivate scene: any;\n\tprivate groundProbe: GroundProbe3D;\n\n\tconstructor(world: any, scene: any) {\n\t\tthis.world = world;\n\t\tthis.scene = scene;\n\t\tthis.groundProbe = new GroundProbe3D(world);\n\t}\n\n\t/**\n\t * Apply horizontal movement based on input\n\t */\n\tprivate applyMovement(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// Determine current speed\n\t\tconst speed = input.run ? movement.runSpeed : movement.walkSpeed;\n\t\tstate.currentSpeed = speed;\n\n\t\t// Calculate horizontal movement\n\t\tconst moveX = input.moveX * speed;\n\t\tconst moveZ = input.moveZ * speed;\n\n\t\t// Get current Y velocity from physics body (preserve vertical momentum)\n\t\tconst currentVel = entity.body.linvel();\n\n\t\t// Set complete velocity - horizontal from input, vertical preserved\n\t\tentity.transformStore.velocity.x = moveX;\n\t\tentity.transformStore.velocity.y = currentVel.y;\n\t\tentity.transformStore.velocity.z = moveZ;\n\t\tentity.transformStore.dirty.velocity = true;\n\t}\n\n\t/**\n\t * Handle jump logic with multi-jump support\n\t */\n\t/**\n\t * Handle jump logic with multi-jump, coyote time, and buffering\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\t\t\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 apply jump cut if we've been jumping for a bit (prevents cutting multi-jump immediately)\n\t\tconst minTimeBeforeCut = 0.1; // 100ms minimum before cut can apply\n\t\tconst canApplyCut = state.timeSinceJump >= minTimeBeforeCut;\n\t\tif (!input.jump && state.jumping && !state.jumpCutApplied && canApplyCut) {\n\t\t\tconst velocity = entity.body.linvel();\n\t\t\tentity.transformStore.velocity.y = velocity.y * movement.jumpCutMultiplier;\n\t\t\tentity.transformStore.dirty.velocity = true;\n\t\t\tstate.jumpCutApplied = true;\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\t\t\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\n\t\t\t// Apply jump force via transform store\n\t\t\tentity.transformStore.velocity.y = movement.jumpForce;\n\t\t\tentity.transformStore.dirty.velocity = true;\n\t\t}\n\t}\n\n\t/**\n\t * Apply gravity when not grounded\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.grounded) return;\n\t\t\n\t\t// Skip gravity on the same frame as a jump (prevents overwriting jump velocity)\n\t\t// timeSinceJump is reset to 0 when we jump, so if it's very small, we just jumped\n\t\tif (state.jumping && state.timeSinceJump < 0.01) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Get current velocity from physics body and add gravity\n\t\tconst currentVel = entity.body.linvel();\n\t\tconst newYVelocity = currentVel.y - movement.gravity * delta;\n\n\t\t// Update the Y velocity in transform store (applyMovement already set x/z and old y)\n\t\tentity.transformStore.velocity.y = newYVelocity;\n\t\tentity.transformStore.dirty.velocity = true;\n\t}\n\n\t/**\n\t * Update entity state based on physics\n\t */\n\tprivate updateState(entity: Platformer3DEntity, delta: number): void {\n\t\tconst state = entity.platformerState;\n\n\t\t// 1. Reset grounded state before detection\n\t\tconst wasGrounded = state.grounded;\n\t\t\n\t\t// Read ACTUAL velocity from physics body\n\t\tconst velocity = entity.body.linvel();\n\t\t\n\t\tlet isGrounded = false;\n\t\t\n\t\t// Separate ground detection for airborne vs walking\n\t\tconst isAirborne = state.jumping || state.falling;\n\t\t\n\t\tif (isAirborne) {\n\t\t\t// Airborne: Must be FALLING (not jumping) with very low velocity to land\n\t\t\t// This prevents false grounding at jump apex or during descent\n\t\t\tconst probeOriginYOffset = 0.05 - getGroundAnchorOffsetY(entity);\n\t\t\tconst nearGround = this.groundProbe.detect(entity, {\n\t\t\t\trayLength: entity.platformer.groundRayLength,\n\t\t\t\tmode: 'any',\n\t\t\t\tdebug: entity.platformer.debugGroundProbe,\n\t\t\t\tscene: this.scene,\n\t\t\t\toriginYOffset: probeOriginYOffset,\n\t\t\t});\n\t\t\tconst canLand = state.falling && !state.jumping; // Only land when falling, not jumping\n\t\t\tconst hasLanded = Math.abs(velocity.y) < 0.5; // Very strict threshold for landing\n\t\t\tisGrounded = nearGround && canLand && hasLanded;\n\t\t} else {\n\t\t\t// On ground (walking/idle): Normal raycast detection with lenient threshold\n\t\t\tconst probeOriginYOffset = 0.05 - getGroundAnchorOffsetY(entity);\n\t\t\tconst nearGround = this.groundProbe.detect(entity, {\n\t\t\t\trayLength: entity.platformer.groundRayLength,\n\t\t\t\tmode: 'any',\n\t\t\t\tdebug: entity.platformer.debugGroundProbe,\n\t\t\t\tscene: this.scene,\n\t\t\t\toriginYOffset: probeOriginYOffset,\n\t\t\t});\n\t\t\tconst notFallingFast = velocity.y > -2.0; // Lenient - don't ground while falling fast\n\t\t\tisGrounded = nearGround && notFallingFast;\n\t\t}\n\n\t\tstate.grounded = isGrounded;\n\n\t\t// 2. Update Coyote Timer\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\t// 3. Landing Logic\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}\n\n\t\t// 4. Falling Logic (negative Y velocity and not grounded)\n\t\tif (velocity.y < -0.1 && !state.grounded) {\n\t\t\tif (state.jumping && velocity.y < 0) {\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.\n\t */\n\tupdateEntity(entity: any, delta: number): void {\n\t\tif (!entity.platformer || !entity.$platformer || !entity.platformerState) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst platformerEntity = entity as Platformer3DEntity;\n\t\tthis.updateState(platformerEntity, delta);\n\t\tthis.applyMovement(platformerEntity, delta);\n\t\tthis.handleJump(platformerEntity, delta);\n\t\tthis.applyGravity(platformerEntity, delta);\n\t}\n\n\t/**\n\t * Update all platformer entities.\n\t */\n\tupdate(delta: number): void {\n\t\tif (!this.world?.collisionMap) return;\n\t\tfor (const [, entity] of this.world.collisionMap) {\n\t\t\tthis.updateEntity(entity, delta);\n\t\t}\n\t}\n\n\t/**\n\t * Cleanup\n\t */\n\tdestroy(): void {\n\t\tthis.groundProbe.destroy();\n\t}\n}\n","/**\n * BehaviorDescriptor\n *\n * Type-safe behavior descriptors that provide options inference.\n * Used with entity.use() to declaratively attach behaviors to entities.\n *\n * Each behavior can define its own handle type via `createHandle`,\n * providing behavior-specific methods with full type safety.\n */\n\nimport type { BehaviorSystemFactory } from './behavior-system';\n\n/**\n * Base handle returned by entity.use() for lazy access to behavior runtime.\n * FSM is null until entity is spawned and components are initialized.\n */\nexport interface BaseBehaviorHandle<\n O extends Record<string, any> = Record<string, any>,\n> {\n /** Get the FSM instance (null until entity is spawned) */\n getFSM(): any | null;\n /** Get the current options */\n getOptions(): O;\n /** Access the underlying behavior ref */\n readonly ref: BehaviorRef<O>;\n}\n\n/**\n * Reference to a behavior stored on an entity\n */\nexport interface BehaviorRef<\n O extends Record<string, any> = Record<string, any>,\n> {\n /** The behavior descriptor */\n descriptor: BehaviorDescriptor<O, any>;\n /** Merged options (defaults + overrides) */\n options: O;\n /** Optional FSM instance - set lazily when entity is spawned */\n fsm?: any;\n}\n\n/**\n * A typed behavior descriptor that associates a symbol key with:\n * - Default options (providing type inference)\n * - A system factory to create the behavior system\n * - An optional handle factory for behavior-specific methods\n */\nexport interface BehaviorDescriptor<\n O extends Record<string, any> = Record<string, any>,\n H extends Record<string, any> = Record<string, never>,\n I = unknown,\n> {\n /** Unique symbol identifying this behavior */\n readonly key: symbol;\n /** Default options (used for type inference) */\n readonly defaultOptions: O;\n /** Factory to create the behavior system */\n readonly systemFactory: BehaviorSystemFactory;\n /**\n * Optional factory to create behavior-specific handle methods.\n * These methods are merged into the handle returned by entity.use().\n */\n readonly createHandle?: (ref: BehaviorRef<O>) => H;\n}\n\n/**\n * The full handle type returned by entity.use().\n * Combines base handle with behavior-specific methods.\n */\nexport type BehaviorHandle<\n O extends Record<string, any> = Record<string, any>,\n H extends Record<string, any> = Record<string, never>,\n> = BaseBehaviorHandle<O> & H;\n\n/**\n * Configuration for defining a new behavior\n */\nexport interface DefineBehaviorConfig<\n O extends Record<string, any>,\n H extends Record<string, any> = Record<string, never>,\n I = unknown,\n> {\n /** Human-readable name for debugging */\n name: string;\n /** Default options - these define the type */\n defaultOptions: O;\n /** Factory function to create the system */\n systemFactory: BehaviorSystemFactory;\n /**\n * Optional factory to create behavior-specific handle methods.\n * The returned object is merged into the handle returned by entity.use().\n *\n * @example\n * ```typescript\n * createHandle: (ref) => ({\n * getLastHits: () => ref.fsm?.getLastHits() ?? null,\n * getMovement: (moveX, moveY) => ref.fsm?.getMovement(moveX, moveY) ?? { moveX, moveY },\n * }),\n * ```\n */\n createHandle?: (ref: BehaviorRef<O>) => H;\n}\n\n/**\n * Define a typed behavior descriptor.\n *\n * @example\n * ```typescript\n * export const WorldBoundary2DBehavior = defineBehavior({\n * name: 'world-boundary-2d',\n * defaultOptions: { boundaries: { top: 0, bottom: 0, left: 0, right: 0 } },\n * systemFactory: (ctx) => new WorldBoundary2DSystem(ctx.world),\n * createHandle: (ref) => ({\n * getLastHits: () => ref.fsm?.getLastHits() ?? null,\n * getMovement: (moveX: number, moveY: number) =>\n * ref.fsm?.getMovement(moveX, moveY) ?? { moveX, moveY },\n * }),\n * });\n *\n * // Usage - handle has getLastHits and getMovement with full types\n * const boundary = ship.use(WorldBoundary2DBehavior, { ... });\n * const hits = boundary.getLastHits(); // Fully typed!\n * ```\n */\nexport function defineBehavior<\n O extends Record<string, any>,\n H extends Record<string, any> = Record<string, never>,\n I = unknown,\n>(\n config: DefineBehaviorConfig<O, H, I>,\n): BehaviorDescriptor<O, H, I> {\n return {\n key: Symbol.for(`zylem:behavior:${config.name}`),\n defaultOptions: config.defaultOptions,\n systemFactory: config.systemFactory,\n createHandle: config.createHandle,\n };\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 type { IWorld } from 'bitecs';\nimport { defineBehavior } from '../behavior-descriptor';\nimport type { BehaviorEntityLink, BehaviorSystem } from '../behavior-system';\nimport { Platformer3DBehavior as Platformer3DMovementBehavior, Platformer3DEntity } from './platformer-3d.behavior';\nimport { Platformer3DFSM, Platformer3DState, PlatformerCollisionContext } from './platformer-3d-fsm';\nimport {\n\tcreatePlatformer3DMovementComponent,\n\tcreatePlatformer3DInputComponent,\n\tcreatePlatformer3DStateComponent,\n} from './components';\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/** Ray length for ground detection (default: 1.0) */\n\tgroundRayLength?: number;\n\t/** Enable debug visualization for ground probe rays (default: false) */\n\tdebugGroundProbe?: boolean;\n}\n\nconst defaultOptions: Platformer3DBehaviorOptions = {\n\twalkSpeed: 12,\n\trunSpeed: 24,\n\tjumpForce: 12,\n\tmaxJumps: 1,\n\tgravity: 9.82,\n\tgroundRayLength: 1.0,\n\tdebugGroundProbe: false,\n};\n\nconst PLATFORMER_BEHAVIOR_KEY = Symbol.for('zylem:behavior:platformer-3d');\n\n/**\n * Adapter that wraps Platformer3DBehavior as a BehaviorSystem\n */\nclass Platformer3DBehaviorSystem implements BehaviorSystem {\n\tprivate movementBehavior: Platformer3DMovementBehavior;\n\n\tconstructor(\n\t\tprivate world: any,\n\t\tprivate scene: any,\n\t\tprivate getBehaviorLinks?: (key: symbol) => Iterable<BehaviorEntityLink>,\n\t) {\n\t\tthis.movementBehavior = new Platformer3DMovementBehavior(world, scene);\n\t}\n\n\tupdate(_ecs: IWorld, 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\t\t\tif (!gameEntity.body) continue;\n\n\t\t\tconst options = platformerRef.options as Platformer3DBehaviorOptions;\n\n\t\t\t// Ensure entity has platformer components (initialized once)\n\t\t\tif (!gameEntity.platformer) {\n\t\t\t\tgameEntity.platformer = createPlatformer3DMovementComponent(options);\n\t\t\t}\n\n\t\t\tif (!gameEntity.$platformer) {\n\t\t\t\tgameEntity.$platformer = createPlatformer3DInputComponent();\n\t\t\t}\n\n\t\t\tif (!gameEntity.platformerState) {\n\t\t\t\tgameEntity.platformerState = createPlatformer3DStateComponent();\n\t\t\t}\n\n\t\t\t// Create FSM lazily and attach to the BehaviorRef\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\n\t\t\t// Update FSM to sync state with physics\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\tthis.movementBehavior.updateEntity(gameEntity, delta);\n\t\t}\n\t}\n\n\tdestroy(_ecs: IWorld): void {\n\t\tthis.movementBehavior.destroy();\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\";\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,\n\t\t\tctx.scene,\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","result","t","centerY","Platformer3DBehavior"]}
@@ -0,0 +1,275 @@
1
+ import { B as BaseEntityInterface } from '../entity-types-DAu8sGJH.js';
2
+ import { S as SyncStateMachine } from '../sync-state-machine-CZyspBpj.js';
3
+ import { c as BehaviorDescriptor } from '../behavior-descriptor-BXnVR8Ki.js';
4
+ import 'three';
5
+ import '@dimforge/rapier3d-compat';
6
+ import 'typescript-fsm';
7
+ import 'bitecs';
8
+
9
+ /**
10
+ * Ricochet2DFSM
11
+ *
12
+ * FSM + extended state to track ricochet events and results.
13
+ * The FSM state tracks whether a ricochet is currently occurring.
14
+ */
15
+
16
+ interface Ricochet2DResult {
17
+ /** The reflected velocity vector */
18
+ velocity: {
19
+ x: number;
20
+ y: number;
21
+ z?: number;
22
+ };
23
+ /** The resulting speed after reflection */
24
+ speed: number;
25
+ /** The collision normal used for reflection */
26
+ normal: {
27
+ x: number;
28
+ y: number;
29
+ z?: number;
30
+ };
31
+ }
32
+ interface Ricochet2DCollisionContext {
33
+ entity?: BaseEntityInterface;
34
+ otherEntity?: BaseEntityInterface;
35
+ /** Current velocity of the entity (optional if entity is provided) */
36
+ selfVelocity?: {
37
+ x: number;
38
+ y: number;
39
+ z?: number;
40
+ };
41
+ /** Contact information from the collision */
42
+ contact: {
43
+ /** The collision normal */
44
+ normal?: {
45
+ x: number;
46
+ y: number;
47
+ z?: number;
48
+ };
49
+ /**
50
+ * Optional position where the collision occurred.
51
+ * If provided, used for precise offset calculation.
52
+ */
53
+ position?: {
54
+ x: number;
55
+ y: number;
56
+ z?: number;
57
+ };
58
+ };
59
+ /**
60
+ * Optional position of the entity that owns this behavior.
61
+ * Used with contact.position for offset calculations.
62
+ */
63
+ selfPosition?: {
64
+ x: number;
65
+ y: number;
66
+ z?: number;
67
+ };
68
+ /**
69
+ * Optional position of the other entity in the collision.
70
+ * Used for paddle-style deflection: offset = (contactY - otherY) / halfHeight.
71
+ */
72
+ otherPosition?: {
73
+ x: number;
74
+ y: number;
75
+ z?: number;
76
+ };
77
+ /**
78
+ * Optional size of the other entity (e.g., paddle size).
79
+ * If provided, used to normalize the offset based on the collision face.
80
+ */
81
+ otherSize?: {
82
+ x: number;
83
+ y: number;
84
+ z?: number;
85
+ };
86
+ }
87
+ declare enum Ricochet2DState {
88
+ Idle = "idle",
89
+ Ricocheting = "ricocheting"
90
+ }
91
+ declare enum Ricochet2DEvent {
92
+ StartRicochet = "start-ricochet",
93
+ EndRicochet = "end-ricochet"
94
+ }
95
+ /**
96
+ * Callback type for ricochet event listeners.
97
+ */
98
+ type RicochetCallback = (result: Ricochet2DResult) => void;
99
+ /**
100
+ * FSM wrapper with extended state (lastResult).
101
+ * Systems or consumers call `computeRicochet(...)` when a collision occurs.
102
+ */
103
+ declare class Ricochet2DFSM {
104
+ readonly machine: SyncStateMachine<Ricochet2DState, Ricochet2DEvent, never>;
105
+ private lastResult;
106
+ private lastUpdatedAtMs;
107
+ private currentTimeMs;
108
+ private listeners;
109
+ constructor();
110
+ /**
111
+ * Add a listener for ricochet events.
112
+ * @returns Unsubscribe function
113
+ */
114
+ addListener(callback: RicochetCallback): () => void;
115
+ /**
116
+ * Emit result to all listeners.
117
+ */
118
+ private emitToListeners;
119
+ getState(): Ricochet2DState;
120
+ /**
121
+ * Returns the last computed ricochet result, or null if none.
122
+ */
123
+ getLastResult(): Ricochet2DResult | null;
124
+ /**
125
+ * Best-effort timestamp (ms) of the last computation.
126
+ */
127
+ getLastUpdatedAtMs(): number | null;
128
+ /**
129
+ * Set current game time (called by system each frame).
130
+ * Used for cooldown calculations.
131
+ */
132
+ setCurrentTimeMs(timeMs: number): void;
133
+ /**
134
+ * Check if ricochet is on cooldown (to prevent rapid duplicate applications).
135
+ * @param cooldownMs Cooldown duration in milliseconds (default: 50ms)
136
+ */
137
+ isOnCooldown(cooldownMs?: number): boolean;
138
+ /**
139
+ * Reset cooldown state (e.g., on entity respawn).
140
+ */
141
+ resetCooldown(): void;
142
+ /**
143
+ * Compute a ricochet result from collision context.
144
+ * Returns the result for the consumer to apply, or null if invalid input.
145
+ */
146
+ computeRicochet(ctx: Ricochet2DCollisionContext, options?: {
147
+ minSpeed?: number;
148
+ maxSpeed?: number;
149
+ speedMultiplier?: number;
150
+ reflectionMode?: 'simple' | 'angled';
151
+ maxAngleDeg?: number;
152
+ }): Ricochet2DResult | null;
153
+ /**
154
+ * Extract velocity, position, and size data from entities or context.
155
+ */
156
+ private extractDataFromEntities;
157
+ /**
158
+ * Compute collision normal from entity positions using AABB heuristic.
159
+ */
160
+ private computeNormalFromPositions;
161
+ /**
162
+ * Compute basic reflection using the formula: v' = v - 2(v·n)n
163
+ */
164
+ private computeBasicReflection;
165
+ /**
166
+ * Compute angled deflection for paddle-style reflections.
167
+ */
168
+ private computeAngledDeflection;
169
+ /**
170
+ * Compute hit offset for angled deflection (-1 to 1).
171
+ */
172
+ private computeHitOffset;
173
+ /**
174
+ * Apply speed constraints to the reflected velocity.
175
+ */
176
+ private applySpeedClamp;
177
+ /**
178
+ * Clear the ricochet state (call after consumer has applied the result).
179
+ */
180
+ clearRicochet(): void;
181
+ private dispatch;
182
+ }
183
+
184
+ interface Ricochet2DOptions {
185
+ /**
186
+ * Minimum speed after reflection.
187
+ * Default: 2
188
+ */
189
+ minSpeed: number;
190
+ /**
191
+ * Maximum speed after reflection.
192
+ * Default: 20
193
+ */
194
+ maxSpeed: number;
195
+ /**
196
+ * Speed multiplier applied during angled reflection.
197
+ * Default: 1.05
198
+ */
199
+ speedMultiplier: number;
200
+ /**
201
+ * Reflection mode:
202
+ * - 'simple': Basic axis inversion
203
+ * - 'angled': Paddle-style deflection based on contact point
204
+ * Default: 'angled'
205
+ */
206
+ reflectionMode: 'simple' | 'angled';
207
+ /**
208
+ * Maximum deflection angle in degrees for angled mode.
209
+ * Default: 60
210
+ */
211
+ maxAngleDeg: number;
212
+ }
213
+ /**
214
+ * Handle methods provided by Ricochet2DBehavior
215
+ */
216
+ interface Ricochet2DHandle {
217
+ /**
218
+ * Compute a ricochet/reflection result from collision context.
219
+ * Returns the result for the consumer to apply, or null if invalid input.
220
+ *
221
+ * @param ctx - Collision context with selfVelocity and contact normal
222
+ * @returns Ricochet result with velocity, speed, and normal, or null
223
+ */
224
+ getRicochet(ctx: Ricochet2DCollisionContext): Ricochet2DResult | null;
225
+ /**
226
+ * Compute ricochet and apply velocity directly via transformStore.
227
+ * Emits to onRicochet listeners if successful.
228
+ *
229
+ * @param ctx - Collision context with entity and contact normal
230
+ * @returns true if ricochet was computed and applied, false otherwise
231
+ */
232
+ applyRicochet(ctx: Ricochet2DCollisionContext): boolean;
233
+ /**
234
+ * Get the last computed ricochet result, or null if none.
235
+ */
236
+ getLastResult(): Ricochet2DResult | null;
237
+ /**
238
+ * Register a listener for ricochet events.
239
+ * Called whenever a ricochet is computed (from getRicochet or applyRicochet).
240
+ *
241
+ * @param callback - Function to call with ricochet result
242
+ * @returns Unsubscribe function
243
+ */
244
+ onRicochet(callback: RicochetCallback): () => void;
245
+ }
246
+ /**
247
+ * Ricochet2DBehavior
248
+ *
249
+ * @example
250
+ * ```ts
251
+ * import { Ricochet2DBehavior } from "@zylem/game-lib";
252
+ *
253
+ * const ball = createSphere({ ... });
254
+ * const ricochet = ball.use(Ricochet2DBehavior, {
255
+ * minSpeed: 3,
256
+ * maxSpeed: 15,
257
+ * reflectionMode: 'angled',
258
+ * });
259
+ *
260
+ * ball.onCollision(({ entity, other }) => {
261
+ * const velocity = entity.body.linvel();
262
+ * const result = ricochet.getRicochet({
263
+ * selfVelocity: velocity,
264
+ * contact: { normal: { x: 1, y: 0 } }, // from collision data
265
+ * });
266
+ *
267
+ * if (result) {
268
+ * entity.body.setLinvel(result.velocity, true);
269
+ * }
270
+ * });
271
+ * ```
272
+ */
273
+ declare const Ricochet2DBehavior: BehaviorDescriptor<Ricochet2DOptions, Ricochet2DHandle, unknown>;
274
+
275
+ export { Ricochet2DBehavior, type Ricochet2DCollisionContext, Ricochet2DEvent, Ricochet2DFSM, type Ricochet2DHandle, type Ricochet2DOptions, type Ricochet2DResult, Ricochet2DState, type RicochetCallback };