@zylem/game-lib 0.6.3 → 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.
- package/dist/actions.d.ts +5 -5
- package/dist/actions.js +196 -32
- package/dist/actions.js.map +1 -1
- package/dist/behavior/jumper-2d.d.ts +114 -0
- package/dist/behavior/jumper-2d.js +711 -0
- package/dist/behavior/jumper-2d.js.map +1 -0
- package/dist/behavior/platformer-3d.d.ts +14 -14
- package/dist/behavior/platformer-3d.js +347 -104
- package/dist/behavior/platformer-3d.js.map +1 -1
- package/dist/behavior/ricochet-2d.d.ts +4 -3
- package/dist/behavior/ricochet-2d.js +53 -22
- package/dist/behavior/ricochet-2d.js.map +1 -1
- package/dist/behavior/ricochet-3d.d.ts +117 -0
- package/dist/behavior/ricochet-3d.js +443 -0
- package/dist/behavior/ricochet-3d.js.map +1 -0
- package/dist/behavior/screen-visibility.d.ts +79 -0
- package/dist/behavior/screen-visibility.js +358 -0
- package/dist/behavior/screen-visibility.js.map +1 -0
- package/dist/behavior/screen-wrap.d.ts +4 -3
- package/dist/behavior/screen-wrap.js +100 -49
- package/dist/behavior/screen-wrap.js.map +1 -1
- package/dist/behavior/shooter-2d.d.ts +79 -0
- package/dist/behavior/shooter-2d.js +180 -0
- package/dist/behavior/shooter-2d.js.map +1 -0
- package/dist/behavior/thruster.d.ts +5 -4
- package/dist/behavior/thruster.js +133 -75
- package/dist/behavior/thruster.js.map +1 -1
- package/dist/behavior/top-down-movement.d.ts +56 -0
- package/dist/behavior/top-down-movement.js +125 -0
- package/dist/behavior/top-down-movement.js.map +1 -0
- package/dist/behavior/world-boundary-2d.d.ts +4 -3
- package/dist/behavior/world-boundary-2d.js +90 -36
- package/dist/behavior/world-boundary-2d.js.map +1 -1
- package/dist/behavior/world-boundary-3d.d.ts +76 -0
- package/dist/behavior/world-boundary-3d.js +274 -0
- package/dist/behavior/world-boundary-3d.js.map +1 -0
- package/dist/{behavior-descriptor-BWNWmIjv.d.ts → behavior-descriptor-BXnVR8Ki.d.ts} +22 -5
- package/dist/{blueprints-BWGz8fII.d.ts → blueprints-DmbK2dki.d.ts} +2 -2
- package/dist/camera-4XO5gbQH.d.ts +905 -0
- package/dist/camera.d.ts +1 -1
- package/dist/camera.js +876 -289
- package/dist/camera.js.map +1 -1
- package/dist/{composition-DrzFrbqI.d.ts → composition-BASvMKrW.d.ts} +1 -1
- package/dist/{core-DAkskq6Y.d.ts → core-CARRaS55.d.ts} +57 -14
- package/dist/core.d.ts +9 -8
- package/dist/core.js +4519 -1255
- package/dist/core.js.map +1 -1
- package/dist/{entities-DC9ce_vx.d.ts → entities-ChFirVL9.d.ts} +22 -28
- package/dist/entities.d.ts +4 -4
- package/dist/entities.js +1231 -314
- package/dist/entities.js.map +1 -1
- package/dist/{entity-BpbZqg19.d.ts → entity-vj-HTjzU.d.ts} +80 -11
- package/dist/{global-change-Dc8uCKi2.d.ts → global-change-2JvMaz44.d.ts} +1 -1
- package/dist/main.d.ts +718 -19
- package/dist/main.js +12129 -5959
- package/dist/main.js.map +1 -1
- package/dist/physics-pose-DCc4oE44.d.ts +25 -0
- package/dist/physics-protocol-BDD3P5W2.d.ts +200 -0
- package/dist/physics-worker.d.ts +21 -0
- package/dist/physics-worker.js +306 -0
- package/dist/physics-worker.js.map +1 -0
- package/dist/physics.d.ts +205 -0
- package/dist/physics.js +577 -0
- package/dist/physics.js.map +1 -0
- package/dist/{stage-types-BFsm3qsZ.d.ts → stage-types-C19IhuzA.d.ts} +253 -89
- package/dist/stage.d.ts +9 -8
- package/dist/stage.js +3782 -1041
- package/dist/stage.js.map +1 -1
- package/dist/sync-state-machine-CZyspBpj.d.ts +16 -0
- package/dist/{thruster-DhRaJnoL.d.ts → thruster-23lzoPZd.d.ts} +16 -8
- package/dist/world-DfgxoNMt.d.ts +105 -0
- package/package.json +25 -1
- package/dist/camera-B5e4c78l.d.ts +0 -468
- package/dist/world-Be5m1XC1.d.ts +0 -31
package/dist/actions.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/actions/action.ts","../src/lib/actions/interval-actions.ts","../src/lib/actions/persistent-actions.ts","../src/lib/actions/composition.ts","../src/lib/actions/capabilities/transform-store.ts","../src/lib/actions/capabilities/apply-transform.ts","../src/lib/actions/capabilities/moveable.ts","../src/lib/actions/capabilities/rotatable.ts","../src/lib/actions/global-change.ts"],"sourcesContent":["import type { GameEntity, GameEntityOptions } from '../entities/entity';\n\n/**\n * Action interface -- the base contract for all actions.\n *\n * Actions are entity-scoped, self-contained objects that modify entity state\n * over time. They are ticked automatically by the entity update loop.\n */\nexport interface Action {\n\t/** Internal duration in seconds (0 = instant, Infinity = persistent) */\n\treadonly duration: number;\n\t/** Whether this action has completed */\n\treadonly done: boolean;\n\t/** Whether this action auto-removes when done (set by entity.action()) */\n\tpersistent: boolean;\n\t/** Advance the action by delta seconds */\n\ttick(entity: GameEntity<any>, delta: number): void;\n\t/** Reset the action to its initial state */\n\treset(): void;\n}\n\n/**\n * Base class for interval actions that run over a fixed duration.\n * Accepts duration in **milliseconds**; converts to seconds internally\n * since the game loop delta is in seconds.\n */\nexport abstract class BaseAction implements Action {\n\tpublic readonly duration: number;\n\tpublic done: boolean = false;\n\tpublic persistent: boolean = false;\n\tprotected elapsed: number = 0;\n\n\t/** @param durationMs Duration in milliseconds */\n\tconstructor(durationMs: number) {\n\t\tthis.duration = durationMs / 1000;\n\t}\n\n\ttick(entity: GameEntity<any>, delta: number): void {\n\t\tif (this.done) return;\n\t\tthis.elapsed += delta;\n\t\tconst progress = this.duration > 0 ? Math.min(this.elapsed / this.duration, 1) : 1;\n\t\tthis.onTick(entity, delta, progress);\n\t\tif (this.elapsed >= this.duration) {\n\t\t\tthis.done = true;\n\t\t}\n\t}\n\n\treset(): void {\n\t\tthis.elapsed = 0;\n\t\tthis.done = false;\n\t}\n\n\t/**\n\t * Subclasses implement this to apply their effect each frame.\n\t * @param entity The entity this action is running on\n\t * @param delta Frame delta in seconds\n\t * @param progress 0..1 normalized progress through the duration\n\t */\n\tprotected abstract onTick(\n\t\tentity: GameEntity<any>,\n\t\tdelta: number,\n\t\tprogress: number,\n\t): void;\n}\n","import type { GameEntity } from '../entities/entity';\nimport { BaseAction, type Action } from './action';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// MoveBy -- accumulate position delta over duration via velocity\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface MoveByOptions {\n\t/** X displacement (default: 0) */\n\tx?: number;\n\t/** Y displacement (default: 0) */\n\ty?: number;\n\t/** Z displacement (default: 0) */\n\tz?: number;\n\t/** Duration in milliseconds */\n\tduration: number;\n}\n\n/**\n * Move an entity by a displacement over a duration.\n * Distributes the movement as velocity each frame so physics stays in sync.\n *\n * @example\n * ```ts\n * entity.runAction(moveBy({ x: 10, duration: 500 }));\n * ```\n */\nexport function moveBy(opts: MoveByOptions): Action {\n\treturn new MoveByAction(opts);\n}\n\nclass MoveByAction extends BaseAction {\n\tprivate dx: number;\n\tprivate dy: number;\n\tprivate dz: number;\n\n\tconstructor(opts: MoveByOptions) {\n\t\tsuper(opts.duration);\n\t\tthis.dx = opts.x ?? 0;\n\t\tthis.dy = opts.y ?? 0;\n\t\tthis.dz = opts.z ?? 0;\n\t}\n\n\tprotected onTick(entity: GameEntity<any>, delta: number, _progress: number): void {\n\t\tif (this.duration <= 0) return;\n\t\t// Distribute displacement as velocity: displacement/duration per second\n\t\tconst vx = this.dx / this.duration;\n\t\tconst vy = this.dy / this.duration;\n\t\tconst vz = this.dz / this.duration;\n\t\tconst store = entity.transformStore;\n\t\tstore.velocity.x += vx;\n\t\tstore.velocity.y += vy;\n\t\tstore.velocity.z += vz;\n\t\tstore.dirty.velocity = true;\n\t}\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// MoveTo -- move to an absolute position over duration\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface MoveToOptions {\n\t/** Target X position */\n\tx: number;\n\t/** Target Y position */\n\ty: number;\n\t/** Target Z position */\n\tz: number;\n\t/** Duration in milliseconds */\n\tduration: number;\n}\n\n/**\n * Move an entity to an absolute position over a duration.\n * Calculates displacement from the entity's position on the first tick.\n *\n * @example\n * ```ts\n * entity.runAction(moveTo({ x: 0, y: 10, z: 0, duration: 2000 }));\n * ```\n */\nexport function moveTo(opts: MoveToOptions): Action {\n\treturn new MoveToAction(opts);\n}\n\nclass MoveToAction extends BaseAction {\n\tprivate targetX: number;\n\tprivate targetY: number;\n\tprivate targetZ: number;\n\tprivate dx = 0;\n\tprivate dy = 0;\n\tprivate dz = 0;\n\tprivate initialized = false;\n\n\tconstructor(opts: MoveToOptions) {\n\t\tsuper(opts.duration);\n\t\tthis.targetX = opts.x;\n\t\tthis.targetY = opts.y;\n\t\tthis.targetZ = opts.z;\n\t}\n\n\tprotected onTick(entity: GameEntity<any>, delta: number, _progress: number): void {\n\t\tif (this.duration <= 0) return;\n\t\tif (!this.initialized) {\n\t\t\tconst pos = entity.getPosition?.();\n\t\t\tconst cx = pos?.x ?? 0;\n\t\t\tconst cy = pos?.y ?? 0;\n\t\t\tconst cz = pos?.z ?? 0;\n\t\t\tthis.dx = this.targetX - cx;\n\t\t\tthis.dy = this.targetY - cy;\n\t\t\tthis.dz = this.targetZ - cz;\n\t\t\tthis.initialized = true;\n\t\t}\n\t\tconst vx = this.dx / this.duration;\n\t\tconst vy = this.dy / this.duration;\n\t\tconst vz = this.dz / this.duration;\n\t\tconst store = entity.transformStore;\n\t\tstore.velocity.x += vx;\n\t\tstore.velocity.y += vy;\n\t\tstore.velocity.z += vz;\n\t\tstore.dirty.velocity = true;\n\t}\n\n\treset(): void {\n\t\tsuper.reset();\n\t\tthis.initialized = false;\n\t}\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// RotateBy -- rotate by euler angles (degrees) over duration\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface RotateByOptions {\n\t/** X rotation in degrees (default: 0) */\n\tx?: number;\n\t/** Y rotation in degrees (default: 0) */\n\ty?: number;\n\t/** Z rotation in degrees (default: 0) */\n\tz?: number;\n\t/** Duration in milliseconds */\n\tduration: number;\n}\n\n/**\n * Rotate an entity by euler angles (degrees) over a duration.\n *\n * @example\n * ```ts\n * entity.runAction(rotateBy({ y: 360, duration: 2000 }));\n * ```\n */\nexport function rotateBy(opts: RotateByOptions): Action {\n\treturn new RotateByAction(opts);\n}\n\nclass RotateByAction extends BaseAction {\n\tprivate rx: number;\n\tprivate ry: number;\n\tprivate rz: number;\n\n\tconstructor(opts: RotateByOptions) {\n\t\tsuper(opts.duration);\n\t\tconst toRad = Math.PI / 180;\n\t\tthis.rx = (opts.x ?? 0) * toRad;\n\t\tthis.ry = (opts.y ?? 0) * toRad;\n\t\tthis.rz = (opts.z ?? 0) * toRad;\n\t}\n\n\tprotected onTick(entity: GameEntity<any>, delta: number, _progress: number): void {\n\t\tif (this.duration <= 0) return;\n\t\t// Distribute angular velocity in radians/sec\n\t\tconst wx = this.rx / this.duration;\n\t\tconst wy = this.ry / this.duration;\n\t\tconst wz = this.rz / this.duration;\n\t\tconst store = entity.transformStore;\n\t\tstore.angularVelocity.x += wx;\n\t\tstore.angularVelocity.y += wy;\n\t\tstore.angularVelocity.z += wz;\n\t\tstore.dirty.angularVelocity = true;\n\t}\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Delay -- no-op for a duration (used in sequences)\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Wait for a duration. Useful inside `sequence()`.\n *\n * @param ms Duration in milliseconds\n * @example\n * ```ts\n * entity.runAction(sequence(moveBy({ x: 5, duration: 1000 }), delay(500), moveBy({ y: 3, duration: 1000 })));\n * ```\n */\nexport function delay(ms: number): Action {\n\treturn new DelayAction(ms);\n}\n\nclass DelayAction extends BaseAction {\n\tconstructor(ms: number) {\n\t\tsuper(ms);\n\t}\n\n\tprotected onTick(): void {\n\t\t// no-op -- just waits\n\t}\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// CallFunc -- instant action that calls a function\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Call a function immediately. Completes in one frame.\n * Useful inside `sequence()` for side effects.\n *\n * @example\n * ```ts\n * entity.runAction(sequence(moveBy({ x: 5, duration: 1000 }), callFunc(() => console.log('done'))));\n * ```\n */\nexport function callFunc(fn: () => void): Action {\n\treturn new CallFuncAction(fn);\n}\n\nclass CallFuncAction extends BaseAction {\n\tprivate fn: () => void;\n\tprivate called = false;\n\n\tconstructor(fn: () => void) {\n\t\tsuper(0);\n\t\tthis.fn = fn;\n\t}\n\n\tprotected onTick(): void {\n\t\tif (!this.called) {\n\t\t\tthis.fn();\n\t\t\tthis.called = true;\n\t\t}\n\t}\n\n\treset(): void {\n\t\tsuper.reset();\n\t\tthis.called = false;\n\t}\n}\n","import type { GameEntity } from '../entities/entity';\nimport type { Action } from './action';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Throttle -- auto-resetting timer\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ThrottleOptions {\n\t/** Interval in milliseconds between ready cycles */\n\tduration: number;\n}\n\n/**\n * A repeating timer that becomes ready every N milliseconds.\n * Register with `entity.action(throttle(...))`.\n *\n * @example\n * ```ts\n * const t = entity.action(throttle({ duration: 500 }));\n * entity.onUpdate(() => {\n * if (t.ready) { t.consume(); // do something every 500ms }\n * });\n * ```\n */\nexport function throttle(opts: ThrottleOptions): ThrottleAction {\n\treturn new ThrottleAction(opts.duration);\n}\n\nexport class ThrottleAction implements Action {\n\tpublic readonly duration: number;\n\tpublic done: boolean = false;\n\tpublic persistent: boolean = true;\n\tpublic ready: boolean = false;\n\tprivate elapsed: number = 0;\n\n\t/** @param durationMs Duration in milliseconds */\n\tconstructor(durationMs: number) {\n\t\tthis.duration = durationMs / 1000;\n\t}\n\n\ttick(_entity: GameEntity<any>, delta: number): void {\n\t\tthis.elapsed += delta;\n\t\tif (this.elapsed >= this.duration) {\n\t\t\tthis.ready = true;\n\t\t}\n\t}\n\n\t/** Consume the ready state and reset the timer */\n\tconsume(): void {\n\t\tthis.ready = false;\n\t\tthis.elapsed = 0;\n\t}\n\n\treset(): void {\n\t\tthis.elapsed = 0;\n\t\tthis.ready = false;\n\t}\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// OnPress -- edge-detection for button press\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Detects the rising edge of a boolean signal (press).\n * Call `.check(isPressed)` each frame in your onUpdate; `.triggered` is\n * true for one frame on press.\n *\n * @example\n * ```ts\n * const press = entity.action(onPress());\n * entity.onUpdate(({ inputs }) => {\n * press.check(inputs.p1.buttons.A.pressed);\n * if (press.triggered) { // do something once on press }\n * });\n * ```\n */\nexport function onPress(): OnPressAction {\n\treturn new OnPressAction();\n}\n\nexport class OnPressAction implements Action {\n\tpublic readonly duration = Infinity;\n\tpublic done: boolean = false;\n\tpublic persistent: boolean = true;\n\tpublic triggered: boolean = false;\n\tprivate wasPressed: boolean = false;\n\n\ttick(): void {\n\t\t// Reset triggered each frame; .check() sets it\n\t\tthis.triggered = false;\n\t}\n\n\t/**\n\t * Feed the current pressed state. Sets `.triggered = true` on the\n\t * frame where `isPressed` transitions from false to true.\n\t */\n\tcheck(isPressed: boolean): void {\n\t\tif (isPressed && !this.wasPressed) {\n\t\t\tthis.triggered = true;\n\t\t}\n\t\tthis.wasPressed = isPressed;\n\t}\n\n\treset(): void {\n\t\tthis.triggered = false;\n\t\tthis.wasPressed = false;\n\t}\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// OnRelease -- edge-detection for button release\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Detects the falling edge of a boolean signal (release).\n * Call `.check(isPressed)` each frame; `.triggered` is true for one frame\n * on release.\n *\n * @example\n * ```ts\n * const release = entity.action(onRelease());\n * entity.onUpdate(({ inputs }) => {\n * release.check(inputs.p1.buttons.A.pressed);\n * if (release.triggered) { // do something once on release }\n * });\n * ```\n */\nexport function onRelease(): OnReleaseAction {\n\treturn new OnReleaseAction();\n}\n\nexport class OnReleaseAction implements Action {\n\tpublic readonly duration = Infinity;\n\tpublic done: boolean = false;\n\tpublic persistent: boolean = true;\n\tpublic triggered: boolean = false;\n\tprivate wasPressed: boolean = false;\n\n\ttick(): void {\n\t\tthis.triggered = false;\n\t}\n\n\t/**\n\t * Feed the current pressed state. Sets `.triggered = true` on the\n\t * frame where `isPressed` transitions from true to false.\n\t */\n\tcheck(isPressed: boolean): void {\n\t\tif (!isPressed && this.wasPressed) {\n\t\t\tthis.triggered = true;\n\t\t}\n\t\tthis.wasPressed = isPressed;\n\t}\n\n\treset(): void {\n\t\tthis.triggered = false;\n\t\tthis.wasPressed = false;\n\t}\n}\n","import type { GameEntity } from '../entities/entity';\nimport type { Action } from './action';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Sequence -- runs actions one after another\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Run actions in order, one after another.\n *\n * @example\n * ```ts\n * entity.runAction(sequence(\n * moveBy({ x: 5, duration: 1000 }),\n * delay(500),\n * moveBy({ y: 3, duration: 1000 }),\n * ));\n * ```\n */\nexport function sequence(...actions: Action[]): Action {\n\treturn new SequenceAction(actions);\n}\n\nclass SequenceAction implements Action {\n\tpublic duration: number;\n\tpublic done: boolean = false;\n\tpublic persistent: boolean = false;\n\tprivate actions: Action[];\n\tprivate currentIndex: number = 0;\n\n\tconstructor(actions: Action[]) {\n\t\tthis.actions = actions;\n\t\tthis.duration = actions.reduce((sum, a) => sum + a.duration, 0);\n\t}\n\n\ttick(entity: GameEntity<any>, delta: number): void {\n\t\tif (this.done || this.actions.length === 0) {\n\t\t\tthis.done = true;\n\t\t\treturn;\n\t\t}\n\n\t\tlet remaining = delta;\n\t\twhile (remaining > 0 && this.currentIndex < this.actions.length) {\n\t\t\tconst current = this.actions[this.currentIndex];\n\t\t\tcurrent.tick(entity, remaining);\n\t\t\tif (current.done) {\n\t\t\t\t// Calculate overflow time for the next action\n\t\t\t\tconst actionDuration = current.duration;\n\t\t\t\tconst actionElapsed = (current as any).elapsed ?? actionDuration;\n\t\t\t\tconst overflow = Math.max(0, actionElapsed - actionDuration);\n\t\t\t\tremaining = overflow;\n\t\t\t\tthis.currentIndex++;\n\t\t\t} else {\n\t\t\t\tremaining = 0;\n\t\t\t}\n\t\t}\n\n\t\tif (this.currentIndex >= this.actions.length) {\n\t\t\tthis.done = true;\n\t\t}\n\t}\n\n\treset(): void {\n\t\tthis.currentIndex = 0;\n\t\tthis.done = false;\n\t\tfor (const action of this.actions) {\n\t\t\taction.reset();\n\t\t}\n\t}\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Parallel -- runs all actions simultaneously\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Run all actions simultaneously. Done when all children are done.\n *\n * @example\n * ```ts\n * entity.runAction(parallel(\n * moveBy({ x: 5, duration: 1000 }),\n * rotateBy({ y: 360, duration: 1000 }),\n * ));\n * ```\n */\nexport function parallel(...actions: Action[]): Action {\n\treturn new ParallelAction(actions);\n}\n\nclass ParallelAction implements Action {\n\tpublic duration: number;\n\tpublic done: boolean = false;\n\tpublic persistent: boolean = false;\n\tprivate actions: Action[];\n\n\tconstructor(actions: Action[]) {\n\t\tthis.actions = actions;\n\t\tthis.duration = Math.max(0, ...actions.map(a => a.duration));\n\t}\n\n\ttick(entity: GameEntity<any>, delta: number): void {\n\t\tif (this.done) return;\n\n\t\tlet allDone = true;\n\t\tfor (const action of this.actions) {\n\t\t\tif (!action.done) {\n\t\t\t\taction.tick(entity, delta);\n\t\t\t}\n\t\t\tif (!action.done) {\n\t\t\t\tallDone = false;\n\t\t\t}\n\t\t}\n\n\t\tif (allDone) {\n\t\t\tthis.done = true;\n\t\t}\n\t}\n\n\treset(): void {\n\t\tthis.done = false;\n\t\tfor (const action of this.actions) {\n\t\t\taction.reset();\n\t\t}\n\t}\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Repeat -- repeats an action N times\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Repeat an action a fixed number of times.\n *\n * @example\n * ```ts\n * entity.runAction(repeat(moveBy({ x: 2, duration: 500 }), 5));\n * ```\n */\nexport function repeat(action: Action, times: number): Action {\n\treturn new RepeatAction(action, times);\n}\n\nclass RepeatAction implements Action {\n\tpublic duration: number;\n\tpublic done: boolean = false;\n\tpublic persistent: boolean = false;\n\tprivate action: Action;\n\tprivate totalTimes: number;\n\tprivate currentCount: number = 0;\n\n\tconstructor(action: Action, times: number) {\n\t\tthis.action = action;\n\t\tthis.totalTimes = times;\n\t\tthis.duration = action.duration * times;\n\t}\n\n\ttick(entity: GameEntity<any>, delta: number): void {\n\t\tif (this.done) return;\n\n\t\tthis.action.tick(entity, delta);\n\t\tif (this.action.done) {\n\t\t\tthis.currentCount++;\n\t\t\tif (this.currentCount >= this.totalTimes) {\n\t\t\t\tthis.done = true;\n\t\t\t} else {\n\t\t\t\tthis.action.reset();\n\t\t\t}\n\t\t}\n\t}\n\n\treset(): void {\n\t\tthis.currentCount = 0;\n\t\tthis.done = false;\n\t\tthis.action.reset();\n\t}\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// RepeatForever -- repeats an action indefinitely\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Repeat an action forever. Never completes.\n *\n * @example\n * ```ts\n * enemy.runAction(repeatForever(\n * sequence(moveBy({ x: 3, duration: 2000 }), moveBy({ x: -3, duration: 2000 })),\n * ));\n * ```\n */\nexport function repeatForever(action: Action): Action {\n\treturn new RepeatForeverAction(action);\n}\n\nclass RepeatForeverAction implements Action {\n\tpublic readonly duration = Infinity;\n\tpublic done: boolean = false;\n\tpublic persistent: boolean = false;\n\tprivate action: Action;\n\n\tconstructor(action: Action) {\n\t\tthis.action = action;\n\t}\n\n\ttick(entity: GameEntity<any>, delta: number): void {\n\t\tthis.action.tick(entity, delta);\n\t\tif (this.action.done) {\n\t\t\tthis.action.reset();\n\t\t}\n\t}\n\n\treset(): void {\n\t\tthis.done = false;\n\t\tthis.action.reset();\n\t}\n}\n","import { proxy } from 'valtio';\n\n/**\n * Transform state managed by Valtio for batched physics updates.\n * \n * This store accumulates transformation intents during the frame,\n * then applies them all at once after onUpdate() callbacks complete.\n * \n * Design decisions:\n * - Position: Accumulated deltas (additive)\n * - Rotation: Last value wins (replacement)\n * - Velocity: Accumulated deltas (additive)\n * - Angular velocity: Accumulated deltas (additive)\n */\nexport interface TransformState {\n\t/** Position deltas to be applied (accumulated) */\n\tposition: { x: number; y: number; z: number };\n\t\n\t/** Rotation quaternion (last value wins) */\n\trotation: { x: number; y: number; z: number; w: number };\n\t\n\t/** Linear velocity (accumulated) */\n\tvelocity: { x: number; y: number; z: number };\n\t\n\t/** Angular velocity (accumulated) */\n\tangularVelocity: { x: number; y: number; z: number };\n\t\n\t/** Dirty flags to track what needs to be applied */\n\tdirty: {\n\t\tposition: boolean;\n\t\trotation: boolean;\n\t\tvelocity: boolean;\n\t\tangularVelocity: boolean;\n\t};\n}\n\n/**\n * Create a new transform store with optional initial values.\n * The store is a Valtio proxy, making it reactive for debugging/visualization.\n * \n * @param initial Optional initial state values\n * @returns Reactive transform state\n */\nexport function createTransformStore(initial?: Partial<TransformState>): TransformState {\n\tconst defaultState: TransformState = {\n\t\tposition: { x: 0, y: 0, z: 0 },\n\t\trotation: { x: 0, y: 0, z: 0, w: 1 },\n\t\tvelocity: { x: 0, y: 0, z: 0 },\n\t\tangularVelocity: { x: 0, y: 0, z: 0 },\n\t\tdirty: {\n\t\t\tposition: false,\n\t\t\trotation: false,\n\t\t\tvelocity: false,\n\t\t\tangularVelocity: false,\n\t\t},\n\t};\n\n\treturn proxy({\n\t\t...defaultState,\n\t\t...initial,\n\t\t// Ensure dirty flags are properly initialized even if partial initial state\n\t\tdirty: {\n\t\t\t...defaultState.dirty,\n\t\t\t...initial?.dirty,\n\t\t},\n\t});\n}\n\n/**\n * Reset a transform store to its initial clean state.\n * Called after applying changes to prepare for the next frame.\n * \n * @param store The transform store to reset\n */\nexport function resetTransformStore(store: TransformState): void {\n\t// Reset position deltas\n\tstore.position.x = 0;\n\tstore.position.y = 0;\n\tstore.position.z = 0;\n\n\t// Reset rotation to identity quaternion\n\tstore.rotation.x = 0;\n\tstore.rotation.y = 0;\n\tstore.rotation.z = 0;\n\tstore.rotation.w = 1;\n\n\t// Reset velocity\n\tstore.velocity.x = 0;\n\tstore.velocity.y = 0;\n\tstore.velocity.z = 0;\n\n\t// Reset angular velocity\n\tstore.angularVelocity.x = 0;\n\tstore.angularVelocity.y = 0;\n\tstore.angularVelocity.z = 0;\n\n\t// Clear all dirty flags\n\tstore.dirty.position = false;\n\tstore.dirty.rotation = false;\n\tstore.dirty.velocity = false;\n\tstore.dirty.angularVelocity = false;\n}\n","import { RigidBody } from '@dimforge/rapier3d-compat';\nimport { TransformState } from './transform-store';\n\n/**\n * Entity that can have transformations applied from a store\n */\nexport interface TransformableEntity {\n\tbody: RigidBody | null;\n\ttransformStore?: TransformState;\n}\n\n/**\n * Apply accumulated transformations from the store to the physics body.\n * \n * This is called automatically after onUpdate() callbacks complete,\n * flushing all pending transformations to the physics engine in a single batch.\n * \n * Flow:\n * 1. Check dirty flags to see what changed\n * 2. Apply changes to RigidBody\n * 3. Reset store for next frame\n * \n * @param entity Entity with physics body and transform store\n * @param store Transform store containing pending changes\n */\nexport function applyTransformChanges(\n\tentity: TransformableEntity,\n\tstore: TransformState\n): void {\n\tif (!entity.body) return;\n\n\t// Apply velocity if dirty\n\tif (store.dirty.velocity) {\n\t\tentity.body.setLinvel(store.velocity, true);\n\t}\n\n\t// Apply rotation if dirty\n\tif (store.dirty.rotation) {\n\t\tentity.body.setRotation(store.rotation, true);\n\t}\n\n\t// Apply angular velocity if dirty\n\tif (store.dirty.angularVelocity) {\n\t\tentity.body.setAngvel(store.angularVelocity, true);\n\t}\n\n\t// Apply position deltas if dirty\n\tif (store.dirty.position) {\n\t\tconst current = entity.body.translation();\n\t\tentity.body.setTranslation(\n\t\t\t{\n\t\t\t\tx: current.x + store.position.x,\n\t\t\t\ty: current.y + store.position.y,\n\t\t\t\tz: current.z + store.position.z,\n\t\t\t},\n\t\t\ttrue\n\t\t);\n\t}\n}\n","import { Vector3 } from 'three';\nimport { RigidBody, Vector } from '@dimforge/rapier3d-compat';\nimport type { TransformState } from './transform-store';\nimport { createTransformStore } from './transform-store';\n\nexport interface EntityWithBody {\n\tbody: RigidBody | null;\n\ttransformStore?: TransformState;\n}\n\n/**\n * Move an entity along the X axis, preserving other velocities.\n */\nexport function moveX(entity: EntityWithBody, delta: number): void {\n\tif (!entity.transformStore) return;\n\tentity.transformStore.velocity.x = delta;\n\tentity.transformStore.dirty.velocity = true;\n}\n\n/**\n * Move an entity along the Y axis, preserving other velocities.\n */\nexport function moveY(entity: EntityWithBody, delta: number): void {\n\tif (!entity.transformStore) return;\n\tentity.transformStore.velocity.y = delta;\n\tentity.transformStore.dirty.velocity = true;\n}\n\n/**\n * Move an entity along the Z axis, preserving other velocities.\n */\nexport function moveZ(entity: EntityWithBody, delta: number): void {\n\tif (!entity.transformStore) return;\n\tentity.transformStore.velocity.z = delta;\n\tentity.transformStore.dirty.velocity = true;\n}\n\n/**\n * Move an entity along the X and Y axis, preserving Z velocity.\n */\nexport function moveXY(entity: EntityWithBody, deltaX: number, deltaY: number): void {\n\tif (!entity.transformStore) return;\n\tentity.transformStore.velocity.x = deltaX;\n\tentity.transformStore.velocity.y = deltaY;\n\tentity.transformStore.dirty.velocity = true;\n}\n\n/**\n * Move an entity along the X and Z axis, preserving Y velocity.\n */\nexport function moveXZ(entity: EntityWithBody, deltaX: number, deltaZ: number): void {\n\tif (!entity.transformStore) return;\n\tentity.transformStore.velocity.x = deltaX;\n\tentity.transformStore.velocity.z = deltaZ;\n\tentity.transformStore.dirty.velocity = true;\n}\n\n/**\n * Move entity based on a vector, adding to existing velocities.\n */\nexport function move(entity: EntityWithBody, vector: Vector3): void {\n\tif (!entity.transformStore) return;\n\tentity.transformStore.velocity.x += vector.x;\n\tentity.transformStore.velocity.y += vector.y;\n\tentity.transformStore.velocity.z += vector.z;\n\tentity.transformStore.dirty.velocity = true;\n}\n\n/**\n * Reset entity velocity\n */\nexport function resetVelocity(entity: EntityWithBody): void {\n\tif (!entity.body) return;\n\tentity.body.setLinvel(new Vector3(0, 0, 0), true);\n\tentity.body.setLinearDamping(5);\n}\n\n/**\n * Move entity forward in 2D space, preserving Z velocity\n */\nexport function moveForwardXY(entity: EntityWithBody, delta: number, rotation2DAngle: number): void {\n\tconst deltaX = Math.sin(-rotation2DAngle) * delta;\n\tconst deltaY = Math.cos(-rotation2DAngle) * delta;\n\tmoveXY(entity, deltaX, deltaY);\n}\n\n/**\n * Get entity position\n */\nexport function getPosition(entity: EntityWithBody): Vector | null {\n\tif (!entity.body) return null;\n\treturn entity.body.translation();\n}\n\n/**\n * Get entity velocity\n */\nexport function getVelocity(entity: EntityWithBody): Vector | null {\n\tif (!entity.body) return null;\n\treturn entity.body.linvel();\n}\n\n/**\n * Set entity position\n */\nexport function setPosition(entity: EntityWithBody, x: number, y: number, z: number): void {\n\tif (!entity.body) return;\n\tentity.body.setTranslation({ x, y, z }, true);\n}\n\n/**\n * Set entity X position\n */\nexport function setPositionX(entity: EntityWithBody, x: number): void {\n\tif (!entity.body) return;\n\tconst { y, z } = entity.body.translation();\n\tentity.body.setTranslation({ x, y, z }, true);\n}\n\n/**\n * Set entity Y position\n */\nexport function setPositionY(entity: EntityWithBody, y: number): void {\n\tif (!entity.body) return;\n\tconst { x, z } = entity.body.translation();\n\tentity.body.setTranslation({ x, y, z }, true);\n}\n\n/**\n * Set entity Z position\n */\nexport function setPositionZ(entity: EntityWithBody, z: number): void {\n\tif (!entity.body) return;\n\tconst { x, y } = entity.body.translation();\n\tentity.body.setTranslation({ x, y, z }, true);\n}\n\n/**\n * Wrap entity around 2D bounds\n */\nexport function wrapAroundXY(entity: EntityWithBody, boundsX: number, boundsY: number): void {\n\tconst position = getPosition(entity);\n\tif (!position) return;\n\n\tconst { x, y } = position;\n\tconst newX = x > boundsX ? -boundsX : (x < -boundsX ? boundsX : x);\n\tconst newY = y > boundsY ? -boundsY : (y < -boundsY ? boundsY : y);\n\n\tif (newX !== x || newY !== y) {\n\t\tsetPosition(entity, newX, newY, 0);\n\t}\n}\n\n/**\n * Wrap entity around 3D bounds\n */\nexport function wrapAround3D(entity: EntityWithBody, boundsX: number, boundsY: number, boundsZ: number): void {\n\tconst position = getPosition(entity);\n\tif (!position) return;\n\n\tconst { x, y, z } = position;\n\tconst newX = x > boundsX ? -boundsX : (x < -boundsX ? boundsX : x);\n\tconst newY = y > boundsY ? -boundsY : (y < -boundsY ? boundsY : y);\n\tconst newZ = z > boundsZ ? -boundsZ : (z < -boundsZ ? boundsZ : z);\n\n\tif (newX !== x || newY !== y || newZ !== z) {\n\t\tsetPosition(entity, newX, newY, newZ);\n\t}\n}\n\n/**\n * Enhanced moveable entity with bound methods\n */\nexport interface MoveableEntity extends EntityWithBody {\n\tmoveX(delta: number): void;\n\tmoveY(delta: number): void;\n\tmoveZ(delta: number): void;\n\tmoveXY(deltaX: number, deltaY: number): void;\n\tmoveXZ(deltaX: number, deltaZ: number): void;\n\tmove(vector: Vector3): void;\n\tresetVelocity(): void;\n\tmoveForwardXY(delta: number, rotation2DAngle: number): void;\n\tgetPosition(): Vector | null;\n\tgetVelocity(): Vector | null;\n\tsetPosition(x: number, y: number, z: number): void;\n\tsetPositionX(x: number): void;\n\tsetPositionY(y: number): void;\n\tsetPositionZ(z: number): void;\n\twrapAroundXY(boundsX: number, boundsY: number): void;\n\twrapAround3D(boundsX: number, boundsY: number, boundsZ: number): void;\n}\n\n/**\n * Enhance an entity with additive movement methods.\n * Automatically creates a transform store if one doesn't exist.\n */\nexport function makeMoveable<T extends EntityWithBody>(entity: T): T & MoveableEntity {\n\tconst moveable = entity as T & MoveableEntity;\n\n\t// Create transform store if it doesn't exist\n\tif (!moveable.transformStore) {\n\t\tmoveable.transformStore = createTransformStore();\n\t}\n\n\tmoveable.moveX = (delta: number) => moveX(entity, delta);\n\tmoveable.moveY = (delta: number) => moveY(entity, delta);\n\tmoveable.moveZ = (delta: number) => moveZ(entity, delta);\n\tmoveable.moveXY = (deltaX: number, deltaY: number) => moveXY(entity, deltaX, deltaY);\n\tmoveable.moveXZ = (deltaX: number, deltaZ: number) => moveXZ(entity, deltaX, deltaZ);\n\tmoveable.move = (vector: Vector3) => move(entity, vector);\n\tmoveable.resetVelocity = () => resetVelocity(entity);\n\tmoveable.moveForwardXY = (delta: number, rotation2DAngle: number) => moveForwardXY(entity, delta, rotation2DAngle);\n\tmoveable.getPosition = () => getPosition(entity);\n\tmoveable.getVelocity = () => getVelocity(entity);\n\tmoveable.setPosition = (x: number, y: number, z: number) => setPosition(entity, x, y, z);\n\tmoveable.setPositionX = (x: number) => setPositionX(entity, x);\n\tmoveable.setPositionY = (y: number) => setPositionY(entity, y);\n\tmoveable.setPositionZ = (z: number) => setPositionZ(entity, z);\n\tmoveable.wrapAroundXY = (boundsX: number, boundsY: number) => wrapAroundXY(entity, boundsX, boundsY);\n\tmoveable.wrapAround3D = (boundsX: number, boundsY: number, boundsZ: number) => wrapAround3D(entity, boundsX, boundsY, boundsZ);\n\n\treturn moveable;\n}","import { Euler, Vector3, MathUtils, Quaternion } from 'three';\nimport { RigidBody } from '@dimforge/rapier3d-compat';\nimport type { TransformState } from './transform-store';\nimport { createTransformStore } from './transform-store';\n\nexport interface RotatableEntity {\n\tbody: RigidBody | null;\n\tgroup: any;\n\ttransformStore?: TransformState;\n}\n\n/**\n * Rotate an entity in the direction of a movement vector\n */\nexport function rotateInDirection(entity: RotatableEntity, moveVector: Vector3): void {\n\tif (!entity.body) return;\n\tconst rotate = Math.atan2(-moveVector.x, moveVector.z);\n\trotateYEuler(entity, rotate);\n}\n\n/**\n * Rotate an entity around the Y axis using Euler angles\n */\nexport function rotateYEuler(entity: RotatableEntity, amount: number): void {\n\trotateEuler(entity, new Vector3(0, -amount, 0));\n}\n\n/**\n * Rotate an entity using Euler angles\n */\nexport function rotateEuler(entity: RotatableEntity, rotation: Vector3): void {\n\tif (!entity.group) return;\n\tconst euler = new Euler(rotation.x, rotation.y, rotation.z);\n\tentity.group.setRotationFromEuler(euler);\n}\n\n/**\n * Rotate an entity around the Y axis.\n */\nexport function rotateY(entity: RotatableEntity, delta: number): void {\n\tif (!entity.transformStore) return;\n\t\n\t// Create delta rotation quaternion\n\tconst halfAngle = delta / 2;\n\tconst deltaW = Math.cos(halfAngle);\n\tconst deltaY = Math.sin(halfAngle);\n\t\n\t// Get current rotation from store\n\tconst q = entity.transformStore.rotation;\n\t\n\t// Multiply quaternions: q_new = q_current * q_delta\n\t// For Y-axis rotation: q_delta = (0, deltaY, 0, deltaW)\n\tconst newW = q.w * deltaW - q.y * deltaY;\n\tconst newX = q.x * deltaW + q.z * deltaY;\n\tconst newY = q.y * deltaW + q.w * deltaY;\n\tconst newZ = q.z * deltaW - q.x * deltaY;\n\t\n\tentity.transformStore.rotation.w = newW;\n\tentity.transformStore.rotation.x = newX;\n\tentity.transformStore.rotation.y = newY;\n\tentity.transformStore.rotation.z = newZ;\n\tentity.transformStore.dirty.rotation = true;\n}\n\n/**\n * Rotate an entity around the X axis.\n */\nexport function rotateX(entity: RotatableEntity, delta: number): void {\n\tif (!entity.transformStore) return;\n\t\n\t// Create delta rotation quaternion\n\tconst halfAngle = delta / 2;\n\tconst deltaW = Math.cos(halfAngle);\n\tconst deltaX = Math.sin(halfAngle);\n\t\n\t// Get current rotation from store\n\tconst q = entity.transformStore.rotation;\n\t\n\t// Multiply quaternions: q_new = q_current * q_delta\n\t// For X-axis rotation: q_delta = (deltaX, 0, 0, deltaW)\n\tconst newW = q.w * deltaW - q.x * deltaX;\n\tconst newX = q.x * deltaW + q.w * deltaX;\n\tconst newY = q.y * deltaW + q.z * deltaX;\n\tconst newZ = q.z * deltaW - q.y * deltaX;\n\t\n\tentity.transformStore.rotation.w = newW;\n\tentity.transformStore.rotation.x = newX;\n\tentity.transformStore.rotation.y = newY;\n\tentity.transformStore.rotation.z = newZ;\n\tentity.transformStore.dirty.rotation = true;\n}\n\n/**\n * Rotate an entity around the Z axis.\n */\nexport function rotateZ(entity: RotatableEntity, delta: number): void {\n\tif (!entity.transformStore) return;\n\t\n\t// Create delta rotation quaternion\n\tconst halfAngle = delta / 2;\n\tconst deltaW = Math.cos(halfAngle);\n\tconst deltaZ = Math.sin(halfAngle);\n\t\n\t// Get current rotation from store\n\tconst q = entity.transformStore.rotation;\n\t\n\t// Multiply quaternions: q_new = q_current * q_delta\n\t// For Z-axis rotation: q_delta = (0, 0, deltaZ, deltaW)\n\tconst newW = q.w * deltaW - q.z * deltaZ;\n\tconst newX = q.x * deltaW - q.y * deltaZ;\n\tconst newY = q.y * deltaW + q.x * deltaZ;\n\tconst newZ = q.z * deltaW + q.w * deltaZ;\n\t\n\tentity.transformStore.rotation.w = newW;\n\tentity.transformStore.rotation.x = newX;\n\tentity.transformStore.rotation.y = newY;\n\tentity.transformStore.rotation.z = newZ;\n\tentity.transformStore.dirty.rotation = true;\n}\n\n/**\n * Set rotation around Y axis.\n */\nexport function setRotationY(entity: RotatableEntity, y: number): void {\n\tif (!entity.transformStore) return;\n\tconst halfAngle = y / 2;\n\tconst w = Math.cos(halfAngle);\n\tconst yComponent = Math.sin(halfAngle);\n\tentity.transformStore.rotation.w = w;\n\tentity.transformStore.rotation.x = 0;\n\tentity.transformStore.rotation.y = yComponent;\n\tentity.transformStore.rotation.z = 0;\n\tentity.transformStore.dirty.rotation = true;\n}\n\n/**\n * Set rotation around Y axis\n */\nexport function setRotationDegreesY(entity: RotatableEntity, y: number): void {\n\tif (!entity.body) return;\n\tsetRotationY(entity, MathUtils.degToRad(y));\n}\n\n/**\n * Set rotation around X axis.\n */\nexport function setRotationX(entity: RotatableEntity, x: number): void {\n\tif (!entity.transformStore) return;\n\tconst halfAngle = x / 2;\n\tconst w = Math.cos(halfAngle);\n\tconst xComponent = Math.sin(halfAngle);\n\tentity.transformStore.rotation.w = w;\n\tentity.transformStore.rotation.x = xComponent;\n\tentity.transformStore.rotation.y = 0;\n\tentity.transformStore.rotation.z = 0;\n\tentity.transformStore.dirty.rotation = true;\n}\n\n/**\n * Set rotation around X axis\n */\nexport function setRotationDegreesX(entity: RotatableEntity, x: number): void {\n\tif (!entity.body) return;\n\tsetRotationX(entity, MathUtils.degToRad(x));\n}\n\n/**\n * Set rotation around Z axis.\n */\nexport function setRotationZ(entity: RotatableEntity, z: number): void {\n\tif (!entity.transformStore) return;\n\tconst halfAngle = z / 2;\n\tconst w = Math.cos(halfAngle);\n\tconst zComponent = Math.sin(halfAngle);\n\tentity.transformStore.rotation.w = w;\n\tentity.transformStore.rotation.x = 0;\n\tentity.transformStore.rotation.y = 0;\n\tentity.transformStore.rotation.z = zComponent;\n\tentity.transformStore.dirty.rotation = true;\n}\n\n/**\n * Set rotation around Z axis\n */\nexport function setRotationDegreesZ(entity: RotatableEntity, z: number): void {\n\tif (!entity.body) return;\n\tsetRotationZ(entity, MathUtils.degToRad(z));\n}\n\n/**\n * Set rotation for all axes.\n */\nexport function setRotation(entity: RotatableEntity, x: number, y: number, z: number): void {\n\tif (!entity.transformStore) return;\n\tconst quat = new Quaternion().setFromEuler(new Euler(x, y, z));\n\tentity.transformStore.rotation.w = quat.w;\n\tentity.transformStore.rotation.x = quat.x;\n\tentity.transformStore.rotation.y = quat.y;\n\tentity.transformStore.rotation.z = quat.z;\n\tentity.transformStore.dirty.rotation = true;\n}\n\n/**\n * Set rotation for all axes\n */\nexport function setRotationDegrees(entity: RotatableEntity, x: number, y: number, z: number): void {\n\tif (!entity.body) return;\n\tsetRotation(entity, MathUtils.degToRad(x), MathUtils.degToRad(y), MathUtils.degToRad(z));\n}\n\n/**\n * Get current rotation\n */\nexport function getRotation(entity: RotatableEntity): any {\n\tif (!entity.body) return null;\n\treturn entity.body.rotation();\n}\n\n/**\n * Rotatable entity API with bound methods\n */\nexport interface RotatableEntityAPI extends RotatableEntity {\n\trotateInDirection(moveVector: Vector3): void;\n\trotateYEuler(amount: number): void;\n\trotateEuler(rotation: Vector3): void;\n\trotateY(delta: number): void;\n\trotateX(delta: number): void;\n\trotateZ(delta: number): void;\n\tsetRotationY(y: number): void;\n\tsetRotationX(x: number): void;\n\tsetRotationZ(z: number): void;\n\tsetRotationDegrees(x: number, y: number, z: number): void;\n\tsetRotationDegreesY(y: number): void;\n\tsetRotationDegreesX(x: number): void;\n\tsetRotationDegreesZ(z: number): void;\n\tsetRotation(x: number, y: number, z: number): void;\n\tgetRotation(): any;\n}\n\n/**\n * Enhance an entity instance with rotatable methods.\n * Automatically creates a transform store if one doesn't exist.\n */\nexport function makeRotatable<T extends RotatableEntity>(entity: T): T & RotatableEntityAPI {\n\tconst rotatableEntity = entity as T & RotatableEntityAPI;\n\n\t// Create transform store if it doesn't exist\n\tif (!rotatableEntity.transformStore) {\n\t\trotatableEntity.transformStore = createTransformStore();\n\t}\n\n\trotatableEntity.rotateInDirection = (moveVector: Vector3) => rotateInDirection(entity, moveVector);\n\trotatableEntity.rotateYEuler = (amount: number) => rotateYEuler(entity, amount);\n\trotatableEntity.rotateEuler = (rotation: Vector3) => rotateEuler(entity, rotation);\n\trotatableEntity.rotateX = (delta: number) => rotateX(entity, delta);\n\trotatableEntity.rotateY = (delta: number) => rotateY(entity, delta);\n\trotatableEntity.rotateZ = (delta: number) => rotateZ(entity, delta);\n\trotatableEntity.setRotationY = (y: number) => setRotationY(entity, y);\n\trotatableEntity.setRotationX = (x: number) => setRotationX(entity, x);\n\trotatableEntity.setRotationZ = (z: number) => setRotationZ(entity, z);\n\trotatableEntity.setRotationDegreesY = (y: number) => setRotationDegreesY(entity, y);\n\trotatableEntity.setRotationDegreesX = (x: number) => setRotationDegreesX(entity, x);\n\trotatableEntity.setRotationDegreesZ = (z: number) => setRotationDegreesZ(entity, z);\n\trotatableEntity.setRotationDegrees = (x: number, y: number, z: number) => setRotationDegrees(entity, x, y, z);\n\trotatableEntity.setRotation = (x: number, y: number, z: number) => setRotation(entity, x, y, z);\n\trotatableEntity.getRotation = () => getRotation(entity);\n\n\treturn rotatableEntity;\n}","import { UpdateContext } from \"../core/base-node-life-cycle\";\n\n/**\n * Listen for a single global key change inside an onUpdate pipeline.\n * Usage: onUpdate(globalChange('p1Score', (value) => { ... }))\n */\nexport function globalChange<T = any>(\n\tkey: string,\n\tcallback: (value: T, ctx: UpdateContext<any>) => void\n) {\n\tlet previousValue: T | undefined = undefined;\n\treturn (ctx: UpdateContext<any>) => {\n\t\tconst currentValue = ctx.globals?.[key] as T;\n\t\tif (previousValue !== currentValue) {\n\t\t\t// Ignore the very first undefined->value transition only if both are undefined\n\t\t\tif (!(previousValue === undefined && currentValue === undefined)) {\n\t\t\t\tcallback(currentValue, ctx);\n\t\t\t}\n\t\t\tpreviousValue = currentValue;\n\t\t}\n\t};\n}\n\n/**\n * Listen for multiple global key changes inside an onUpdate pipeline.\n * Calls back when any of the provided keys changes.\n * Usage: onUpdate(globalChanges(['p1Score','p2Score'], ([p1,p2]) => { ... }))\n */\nexport function globalChanges<T = any>(\n\tkeys: string[],\n\tcallback: (values: T[], ctx: UpdateContext<any>) => void\n) {\n\tlet previousValues: (T | undefined)[] = new Array(keys.length).fill(undefined);\n\treturn (ctx: UpdateContext<any>) => {\n\t\tconst currentValues = keys.map((k) => ctx.globals?.[k] as T);\n\t\tconst hasAnyChange = currentValues.some((val, idx) => previousValues[idx] !== val);\n\t\tif (hasAnyChange) {\n\t\t\t// Ignore initial all-undefined state\n\t\t\tconst allPrevUndef = previousValues.every((v) => v === undefined);\n\t\t\tconst allCurrUndef = currentValues.every((v) => v === undefined);\n\t\t\tif (!(allPrevUndef && allCurrUndef)) {\n\t\t\t\tcallback(currentValues as T[], ctx);\n\t\t\t}\n\t\t\tpreviousValues = currentValues;\n\t\t}\n\t};\n}\n\n\n/**\n * Listen for a single stage variable change inside an onUpdate pipeline.\n * Usage: onUpdate(variableChange('score', (value, ctx) => { ... }))\n */\nexport function variableChange<T = any>(\n\tkey: string,\n\tcallback: (value: T, ctx: UpdateContext<any>) => void\n) {\n\tlet previousValue: T | undefined = undefined;\n\treturn (ctx: UpdateContext<any>) => {\n\t\t// @ts-ignore - stage is optional on UpdateContext\n\t\tconst currentValue = (ctx.stage?.getVariable?.(key) ?? undefined) as T;\n\t\tif (previousValue !== currentValue) {\n\t\t\tif (!(previousValue === undefined && currentValue === undefined)) {\n\t\t\t\tcallback(currentValue, ctx);\n\t\t\t}\n\t\t\tpreviousValue = currentValue;\n\t\t}\n\t};\n}\n\n/**\n * Listen for multiple stage variable changes; fires when any changes.\n * Usage: onUpdate(variableChanges(['a','b'], ([a,b], ctx) => { ... }))\n */\nexport function variableChanges<T = any>(\n\tkeys: string[],\n\tcallback: (values: T[], ctx: UpdateContext<any>) => void\n) {\n\tlet previousValues: (T | undefined)[] = new Array(keys.length).fill(undefined);\n\treturn (ctx: UpdateContext<any>) => {\n\t\t// @ts-ignore - stage is optional on UpdateContext\n\t\tconst reader = (k: string) => ctx.stage?.getVariable?.(k) as T;\n\t\tconst currentValues = keys.map(reader);\n\t\tconst hasAnyChange = currentValues.some((val, idx) => previousValues[idx] !== val);\n\t\tif (hasAnyChange) {\n\t\t\tconst allPrevUndef = previousValues.every((v) => v === undefined);\n\t\t\tconst allCurrUndef = currentValues.every((v) => v === undefined);\n\t\t\tif (!(allPrevUndef && allCurrUndef)) {\n\t\t\t\tcallback(currentValues as T[], ctx);\n\t\t\t}\n\t\t\tpreviousValues = currentValues;\n\t\t}\n\t};\n}\n\n\n"],"mappings":"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","names":["Vector3","Vector3"]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/actions/action.ts","../src/lib/actions/capabilities/velocity-intents.ts","../src/lib/actions/interval-actions.ts","../src/lib/actions/persistent-actions.ts","../src/lib/actions/composition.ts","../src/lib/actions/capabilities/transform-store.ts","../src/lib/actions/capabilities/apply-transform.ts","../src/lib/actions/capabilities/moveable.ts","../src/lib/actions/capabilities/rotatable.ts","../src/lib/actions/global-change.ts"],"sourcesContent":["import type { GameEntity, GameEntityOptions } from '../entities/entity';\n\n/**\n * Action interface -- the base contract for all actions.\n *\n * Actions are entity-scoped, self-contained objects that modify entity state\n * over time. They are ticked automatically by the entity update loop.\n */\nexport interface Action {\n\t/** Internal duration in seconds (0 = instant, Infinity = persistent) */\n\treadonly duration: number;\n\t/** Whether this action has completed */\n\treadonly done: boolean;\n\t/** Whether this action auto-removes when done (set by entity.action()) */\n\tpersistent: boolean;\n\t/** Advance the action by delta seconds */\n\ttick(entity: GameEntity<any>, delta: number): void;\n\t/** Reset the action to its initial state */\n\treset(): void;\n}\n\n/**\n * Base class for interval actions that run over a fixed duration.\n * Accepts duration in **milliseconds**; converts to seconds internally\n * since the game loop delta is in seconds.\n */\nexport abstract class BaseAction implements Action {\n\tpublic readonly duration: number;\n\tpublic done: boolean = false;\n\tpublic persistent: boolean = false;\n\tprotected elapsed: number = 0;\n\n\t/** @param durationMs Duration in milliseconds */\n\tconstructor(durationMs: number) {\n\t\tthis.duration = durationMs / 1000;\n\t}\n\n\ttick(entity: GameEntity<any>, delta: number): void {\n\t\tif (this.done) return;\n\t\tthis.elapsed += delta;\n\t\tconst progress = this.duration > 0 ? Math.min(this.elapsed / this.duration, 1) : 1;\n\t\tthis.onTick(entity, delta, progress);\n\t\tif (this.elapsed >= this.duration) {\n\t\t\tthis.done = true;\n\t\t}\n\t}\n\n\treset(): void {\n\t\tthis.elapsed = 0;\n\t\tthis.done = false;\n\t}\n\n\t/**\n\t * Subclasses implement this to apply their effect each frame.\n\t * @param entity The entity this action is running on\n\t * @param delta Frame delta in seconds\n\t * @param progress 0..1 normalized progress through the duration\n\t */\n\tprotected abstract onTick(\n\t\tentity: GameEntity<any>,\n\t\tdelta: number,\n\t\tprogress: number,\n\t): void;\n}\n","import { TransformState, VelocityIntentMode } from './transform-store';\n\nexport interface VelocityIntentOptions {\n\tmode?: VelocityIntentMode;\n\tpriority?: number;\n}\n\nexport interface VelocityIntentVector {\n\tx?: number;\n\ty?: number;\n\tz?: number;\n}\n\nexport function setVelocityIntent(\n\tstore: TransformState,\n\tsourceId: string,\n\tvector: VelocityIntentVector,\n\toptions: VelocityIntentOptions = {},\n): void {\n\tconst prev = store.velocityChannels[sourceId];\n\tconst mode = options.mode ?? prev?.mode ?? 'replace';\n\tconst priority = options.priority ?? prev?.priority ?? 0;\n\tconst isAdditiveMerge = mode === 'add' && prev?.mode === 'add';\n\n\tconst x =\n\t\tvector.x == null\n\t\t\t? prev?.x\n\t\t\t: isAdditiveMerge\n\t\t\t\t? (prev?.x ?? 0) + vector.x\n\t\t\t\t: vector.x;\n\tconst y =\n\t\tvector.y == null\n\t\t\t? prev?.y\n\t\t\t: isAdditiveMerge\n\t\t\t\t? (prev?.y ?? 0) + vector.y\n\t\t\t\t: vector.y;\n\tconst z =\n\t\tvector.z == null\n\t\t\t? prev?.z\n\t\t\t: isAdditiveMerge\n\t\t\t\t? (prev?.z ?? 0) + vector.z\n\t\t\t\t: vector.z;\n\n\tstore.velocityChannels[sourceId] = {\n\t\tx,\n\t\ty,\n\t\tz,\n\t\tmode,\n\t\tpriority,\n\t};\n\tstore.dirty.velocityChannels = true;\n}\n\nexport function clearVelocityIntent(store: TransformState, sourceId: string): void {\n\tif (!(sourceId in store.velocityChannels)) return;\n\tdelete store.velocityChannels[sourceId];\n\tstore.dirty.velocityChannels = true;\n}\n","import type { GameEntity } from '../entities/entity';\nimport { BaseAction, type Action } from './action';\nimport { setVelocityIntent } from './capabilities/velocity-intents';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// MoveBy -- accumulate position delta over duration via velocity\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface MoveByOptions {\n\t/** X displacement (default: 0) */\n\tx?: number;\n\t/** Y displacement (default: 0) */\n\ty?: number;\n\t/** Z displacement (default: 0) */\n\tz?: number;\n\t/** Duration in milliseconds */\n\tduration: number;\n}\n\n/**\n * Move an entity by a displacement over a duration.\n * Distributes the movement as velocity each frame so physics stays in sync.\n *\n * @example\n * ```ts\n * entity.runAction(moveBy({ x: 10, duration: 500 }));\n * ```\n */\nexport function moveBy(opts: MoveByOptions): Action {\n\treturn new MoveByAction(opts);\n}\n\nclass MoveByAction extends BaseAction {\n\tprivate dx: number;\n\tprivate dy: number;\n\tprivate dz: number;\n\n\tconstructor(opts: MoveByOptions) {\n\t\tsuper(opts.duration);\n\t\tthis.dx = opts.x ?? 0;\n\t\tthis.dy = opts.y ?? 0;\n\t\tthis.dz = opts.z ?? 0;\n\t}\n\n\tprotected onTick(entity: GameEntity<any>, delta: number, _progress: number): void {\n\t\tif (this.duration <= 0) return;\n\t\t// Distribute displacement as velocity: displacement/duration per second\n\t\tconst vx = this.dx / this.duration;\n\t\tconst vy = this.dy / this.duration;\n\t\tconst vz = this.dz / this.duration;\n\t\tconst store = entity.transformStore;\n\t\tif (!store) return;\n\t\tsetVelocityIntent(store, 'actions', { x: vx, y: vy, z: vz }, { mode: 'add' });\n\t}\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// MoveTo -- move to an absolute position over duration\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface MoveToOptions {\n\t/** Target X position */\n\tx: number;\n\t/** Target Y position */\n\ty: number;\n\t/** Target Z position */\n\tz: number;\n\t/** Duration in milliseconds */\n\tduration: number;\n}\n\n/**\n * Move an entity to an absolute position over a duration.\n * Calculates displacement from the entity's position on the first tick.\n *\n * @example\n * ```ts\n * entity.runAction(moveTo({ x: 0, y: 10, z: 0, duration: 2000 }));\n * ```\n */\nexport function moveTo(opts: MoveToOptions): Action {\n\treturn new MoveToAction(opts);\n}\n\nclass MoveToAction extends BaseAction {\n\tprivate targetX: number;\n\tprivate targetY: number;\n\tprivate targetZ: number;\n\tprivate dx = 0;\n\tprivate dy = 0;\n\tprivate dz = 0;\n\tprivate initialized = false;\n\n\tconstructor(opts: MoveToOptions) {\n\t\tsuper(opts.duration);\n\t\tthis.targetX = opts.x;\n\t\tthis.targetY = opts.y;\n\t\tthis.targetZ = opts.z;\n\t}\n\n\tprotected onTick(entity: GameEntity<any>, delta: number, _progress: number): void {\n\t\tif (this.duration <= 0) return;\n\t\tif (!this.initialized) {\n\t\t\tconst pos = entity.getPosition?.();\n\t\t\tconst cx = pos?.x ?? 0;\n\t\t\tconst cy = pos?.y ?? 0;\n\t\t\tconst cz = pos?.z ?? 0;\n\t\t\tthis.dx = this.targetX - cx;\n\t\t\tthis.dy = this.targetY - cy;\n\t\t\tthis.dz = this.targetZ - cz;\n\t\t\tthis.initialized = true;\n\t\t}\n\t\tconst vx = this.dx / this.duration;\n\t\tconst vy = this.dy / this.duration;\n\t\tconst vz = this.dz / this.duration;\n\t\tconst store = entity.transformStore;\n\t\tif (!store) return;\n\t\tsetVelocityIntent(store, 'actions', { x: vx, y: vy, z: vz }, { mode: 'add' });\n\t}\n\n\treset(): void {\n\t\tsuper.reset();\n\t\tthis.initialized = false;\n\t}\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// RotateBy -- rotate by euler angles (degrees) over duration\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface RotateByOptions {\n\t/** X rotation in degrees (default: 0) */\n\tx?: number;\n\t/** Y rotation in degrees (default: 0) */\n\ty?: number;\n\t/** Z rotation in degrees (default: 0) */\n\tz?: number;\n\t/** Duration in milliseconds */\n\tduration: number;\n}\n\n/**\n * Rotate an entity by euler angles (degrees) over a duration.\n *\n * @example\n * ```ts\n * entity.runAction(rotateBy({ y: 360, duration: 2000 }));\n * ```\n */\nexport function rotateBy(opts: RotateByOptions): Action {\n\treturn new RotateByAction(opts);\n}\n\nclass RotateByAction extends BaseAction {\n\tprivate rx: number;\n\tprivate ry: number;\n\tprivate rz: number;\n\n\tconstructor(opts: RotateByOptions) {\n\t\tsuper(opts.duration);\n\t\tconst toRad = Math.PI / 180;\n\t\tthis.rx = (opts.x ?? 0) * toRad;\n\t\tthis.ry = (opts.y ?? 0) * toRad;\n\t\tthis.rz = (opts.z ?? 0) * toRad;\n\t}\n\n\tprotected onTick(entity: GameEntity<any>, delta: number, _progress: number): void {\n\t\tif (this.duration <= 0) return;\n\t\t// Distribute angular velocity in radians/sec\n\t\tconst wx = this.rx / this.duration;\n\t\tconst wy = this.ry / this.duration;\n\t\tconst wz = this.rz / this.duration;\n\t\tconst store = entity.transformStore;\n\t\tstore.angularVelocity.x += wx;\n\t\tstore.angularVelocity.y += wy;\n\t\tstore.angularVelocity.z += wz;\n\t\tstore.dirty.angularVelocity = true;\n\t}\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Delay -- no-op for a duration (used in sequences)\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Wait for a duration. Useful inside `sequence()`.\n *\n * @param ms Duration in milliseconds\n * @example\n * ```ts\n * entity.runAction(sequence(moveBy({ x: 5, duration: 1000 }), delay(500), moveBy({ y: 3, duration: 1000 })));\n * ```\n */\nexport function delay(ms: number): Action {\n\treturn new DelayAction(ms);\n}\n\nclass DelayAction extends BaseAction {\n\tconstructor(ms: number) {\n\t\tsuper(ms);\n\t}\n\n\tprotected onTick(): void {\n\t\t// no-op -- just waits\n\t}\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// CallFunc -- instant action that calls a function\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Call a function immediately. Completes in one frame.\n * Useful inside `sequence()` for side effects.\n *\n * @example\n * ```ts\n * entity.runAction(sequence(moveBy({ x: 5, duration: 1000 }), callFunc(() => console.log('done'))));\n * ```\n */\nexport function callFunc(fn: () => void): Action {\n\treturn new CallFuncAction(fn);\n}\n\nclass CallFuncAction extends BaseAction {\n\tprivate fn: () => void;\n\tprivate called = false;\n\n\tconstructor(fn: () => void) {\n\t\tsuper(0);\n\t\tthis.fn = fn;\n\t}\n\n\tprotected onTick(): void {\n\t\tif (!this.called) {\n\t\t\tthis.fn();\n\t\t\tthis.called = true;\n\t\t}\n\t}\n\n\treset(): void {\n\t\tsuper.reset();\n\t\tthis.called = false;\n\t}\n}\n","import type { GameEntity } from '../entities/entity';\nimport type { Action } from './action';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Throttle -- auto-resetting timer\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ThrottleOptions {\n\t/** Interval in milliseconds between ready cycles */\n\tduration: number;\n}\n\n/**\n * A repeating timer that becomes ready every N milliseconds.\n * Register with `entity.action(throttle(...))`.\n *\n * @example\n * ```ts\n * const t = entity.action(throttle({ duration: 500 }));\n * entity.onUpdate(() => {\n * if (t.ready) { t.consume(); // do something every 500ms }\n * });\n * ```\n */\nexport function throttle(opts: ThrottleOptions): ThrottleAction {\n\treturn new ThrottleAction(opts.duration);\n}\n\nexport class ThrottleAction implements Action {\n\tpublic readonly duration: number;\n\tpublic done: boolean = false;\n\tpublic persistent: boolean = true;\n\tpublic ready: boolean = false;\n\tprivate elapsed: number = 0;\n\n\t/** @param durationMs Duration in milliseconds */\n\tconstructor(durationMs: number) {\n\t\tthis.duration = durationMs / 1000;\n\t}\n\n\ttick(_entity: GameEntity<any>, delta: number): void {\n\t\tthis.elapsed += delta;\n\t\tif (this.elapsed >= this.duration) {\n\t\t\tthis.ready = true;\n\t\t}\n\t}\n\n\t/** Consume the ready state and reset the timer */\n\tconsume(): void {\n\t\tthis.ready = false;\n\t\tthis.elapsed = 0;\n\t}\n\n\treset(): void {\n\t\tthis.elapsed = 0;\n\t\tthis.ready = false;\n\t}\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// OnPress -- edge-detection for button press\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Detects the rising edge of a boolean signal (press).\n * Call `.check(isPressed)` each frame in your onUpdate; `.triggered` is\n * true for one frame on press.\n *\n * @example\n * ```ts\n * const press = entity.action(onPress());\n * entity.onUpdate(({ inputs }) => {\n * press.check(inputs.p1.buttons.A.pressed);\n * if (press.triggered) { // do something once on press }\n * });\n * ```\n */\nexport function onPress(): OnPressAction {\n\treturn new OnPressAction();\n}\n\nexport class OnPressAction implements Action {\n\tpublic readonly duration = Infinity;\n\tpublic done: boolean = false;\n\tpublic persistent: boolean = true;\n\tpublic triggered: boolean = false;\n\tprivate wasPressed: boolean = false;\n\n\ttick(): void {\n\t\t// Reset triggered each frame; .check() sets it\n\t\tthis.triggered = false;\n\t}\n\n\t/**\n\t * Feed the current pressed state. Sets `.triggered = true` on the\n\t * frame where `isPressed` transitions from false to true.\n\t */\n\tcheck(isPressed: boolean): void {\n\t\tif (isPressed && !this.wasPressed) {\n\t\t\tthis.triggered = true;\n\t\t}\n\t\tthis.wasPressed = isPressed;\n\t}\n\n\treset(): void {\n\t\tthis.triggered = false;\n\t\tthis.wasPressed = false;\n\t}\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// OnRelease -- edge-detection for button release\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Detects the falling edge of a boolean signal (release).\n * Call `.check(isPressed)` each frame; `.triggered` is true for one frame\n * on release.\n *\n * @example\n * ```ts\n * const release = entity.action(onRelease());\n * entity.onUpdate(({ inputs }) => {\n * release.check(inputs.p1.buttons.A.pressed);\n * if (release.triggered) { // do something once on release }\n * });\n * ```\n */\nexport function onRelease(): OnReleaseAction {\n\treturn new OnReleaseAction();\n}\n\nexport class OnReleaseAction implements Action {\n\tpublic readonly duration = Infinity;\n\tpublic done: boolean = false;\n\tpublic persistent: boolean = true;\n\tpublic triggered: boolean = false;\n\tprivate wasPressed: boolean = false;\n\n\ttick(): void {\n\t\tthis.triggered = false;\n\t}\n\n\t/**\n\t * Feed the current pressed state. Sets `.triggered = true` on the\n\t * frame where `isPressed` transitions from true to false.\n\t */\n\tcheck(isPressed: boolean): void {\n\t\tif (!isPressed && this.wasPressed) {\n\t\t\tthis.triggered = true;\n\t\t}\n\t\tthis.wasPressed = isPressed;\n\t}\n\n\treset(): void {\n\t\tthis.triggered = false;\n\t\tthis.wasPressed = false;\n\t}\n}\n","import type { GameEntity } from '../entities/entity';\nimport type { Action } from './action';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Sequence -- runs actions one after another\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Run actions in order, one after another.\n *\n * @example\n * ```ts\n * entity.runAction(sequence(\n * moveBy({ x: 5, duration: 1000 }),\n * delay(500),\n * moveBy({ y: 3, duration: 1000 }),\n * ));\n * ```\n */\nexport function sequence(...actions: Action[]): Action {\n\treturn new SequenceAction(actions);\n}\n\nclass SequenceAction implements Action {\n\tpublic duration: number;\n\tpublic done: boolean = false;\n\tpublic persistent: boolean = false;\n\tprivate actions: Action[];\n\tprivate currentIndex: number = 0;\n\n\tconstructor(actions: Action[]) {\n\t\tthis.actions = actions;\n\t\tthis.duration = actions.reduce((sum, a) => sum + a.duration, 0);\n\t}\n\n\ttick(entity: GameEntity<any>, delta: number): void {\n\t\tif (this.done || this.actions.length === 0) {\n\t\t\tthis.done = true;\n\t\t\treturn;\n\t\t}\n\n\t\tlet remaining = delta;\n\t\twhile (remaining > 0 && this.currentIndex < this.actions.length) {\n\t\t\tconst current = this.actions[this.currentIndex];\n\t\t\tcurrent.tick(entity, remaining);\n\t\t\tif (current.done) {\n\t\t\t\t// Calculate overflow time for the next action\n\t\t\t\tconst actionDuration = current.duration;\n\t\t\t\tconst actionElapsed = (current as any).elapsed ?? actionDuration;\n\t\t\t\tconst overflow = Math.max(0, actionElapsed - actionDuration);\n\t\t\t\tremaining = overflow;\n\t\t\t\tthis.currentIndex++;\n\t\t\t} else {\n\t\t\t\tremaining = 0;\n\t\t\t}\n\t\t}\n\n\t\tif (this.currentIndex >= this.actions.length) {\n\t\t\tthis.done = true;\n\t\t}\n\t}\n\n\treset(): void {\n\t\tthis.currentIndex = 0;\n\t\tthis.done = false;\n\t\tfor (const action of this.actions) {\n\t\t\taction.reset();\n\t\t}\n\t}\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Parallel -- runs all actions simultaneously\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Run all actions simultaneously. Done when all children are done.\n *\n * @example\n * ```ts\n * entity.runAction(parallel(\n * moveBy({ x: 5, duration: 1000 }),\n * rotateBy({ y: 360, duration: 1000 }),\n * ));\n * ```\n */\nexport function parallel(...actions: Action[]): Action {\n\treturn new ParallelAction(actions);\n}\n\nclass ParallelAction implements Action {\n\tpublic duration: number;\n\tpublic done: boolean = false;\n\tpublic persistent: boolean = false;\n\tprivate actions: Action[];\n\n\tconstructor(actions: Action[]) {\n\t\tthis.actions = actions;\n\t\tthis.duration = Math.max(0, ...actions.map(a => a.duration));\n\t}\n\n\ttick(entity: GameEntity<any>, delta: number): void {\n\t\tif (this.done) return;\n\n\t\tlet allDone = true;\n\t\tfor (const action of this.actions) {\n\t\t\tif (!action.done) {\n\t\t\t\taction.tick(entity, delta);\n\t\t\t}\n\t\t\tif (!action.done) {\n\t\t\t\tallDone = false;\n\t\t\t}\n\t\t}\n\n\t\tif (allDone) {\n\t\t\tthis.done = true;\n\t\t}\n\t}\n\n\treset(): void {\n\t\tthis.done = false;\n\t\tfor (const action of this.actions) {\n\t\t\taction.reset();\n\t\t}\n\t}\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Repeat -- repeats an action N times\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Repeat an action a fixed number of times.\n *\n * @example\n * ```ts\n * entity.runAction(repeat(moveBy({ x: 2, duration: 500 }), 5));\n * ```\n */\nexport function repeat(action: Action, times: number): Action {\n\treturn new RepeatAction(action, times);\n}\n\nclass RepeatAction implements Action {\n\tpublic duration: number;\n\tpublic done: boolean = false;\n\tpublic persistent: boolean = false;\n\tprivate action: Action;\n\tprivate totalTimes: number;\n\tprivate currentCount: number = 0;\n\n\tconstructor(action: Action, times: number) {\n\t\tthis.action = action;\n\t\tthis.totalTimes = times;\n\t\tthis.duration = action.duration * times;\n\t}\n\n\ttick(entity: GameEntity<any>, delta: number): void {\n\t\tif (this.done) return;\n\n\t\tthis.action.tick(entity, delta);\n\t\tif (this.action.done) {\n\t\t\tthis.currentCount++;\n\t\t\tif (this.currentCount >= this.totalTimes) {\n\t\t\t\tthis.done = true;\n\t\t\t} else {\n\t\t\t\tthis.action.reset();\n\t\t\t}\n\t\t}\n\t}\n\n\treset(): void {\n\t\tthis.currentCount = 0;\n\t\tthis.done = false;\n\t\tthis.action.reset();\n\t}\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// RepeatForever -- repeats an action indefinitely\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Repeat an action forever. Never completes.\n *\n * @example\n * ```ts\n * enemy.runAction(repeatForever(\n * sequence(moveBy({ x: 3, duration: 2000 }), moveBy({ x: -3, duration: 2000 })),\n * ));\n * ```\n */\nexport function repeatForever(action: Action): Action {\n\treturn new RepeatForeverAction(action);\n}\n\nclass RepeatForeverAction implements Action {\n\tpublic readonly duration = Infinity;\n\tpublic done: boolean = false;\n\tpublic persistent: boolean = false;\n\tprivate action: Action;\n\n\tconstructor(action: Action) {\n\t\tthis.action = action;\n\t}\n\n\ttick(entity: GameEntity<any>, delta: number): void {\n\t\tthis.action.tick(entity, delta);\n\t\tif (this.action.done) {\n\t\t\tthis.action.reset();\n\t\t}\n\t}\n\n\treset(): void {\n\t\tthis.done = false;\n\t\tthis.action.reset();\n\t}\n}\n","import { proxy } from 'valtio';\n\nexport type VelocityIntentMode = 'replace' | 'add';\n\nexport interface VelocityIntent {\n\tx?: number;\n\ty?: number;\n\tz?: number;\n\tmode: VelocityIntentMode;\n\tpriority?: number;\n}\n\n/**\n * Transform state managed by Valtio for batched physics updates.\n * \n * This store accumulates transformation intents during the frame,\n * then applies them all at once after onUpdate() callbacks complete.\n * \n * Design decisions:\n * - Position: Accumulated deltas (additive)\n * - Rotation: Last value wins (replacement)\n * - Velocity: Accumulated deltas (additive)\n * - Angular velocity: Accumulated deltas (additive)\n */\nexport interface TransformState {\n\t/** Position deltas to be applied (accumulated) */\n\tposition: { x: number; y: number; z: number };\n\t\n\t/** Rotation quaternion (last value wins) */\n\trotation: { x: number; y: number; z: number; w: number };\n\t\n\t/** Linear velocity (accumulated) */\n\tvelocity: { x: number; y: number; z: number };\n\t\n\t/** Angular velocity (accumulated) */\n\tangularVelocity: { x: number; y: number; z: number };\n\n\t/** Per-source velocity intents composed once per frame */\n\tvelocityChannels: Record<string, VelocityIntent>;\n\t\n\t/** Dirty flags to track what needs to be applied */\n\tdirty: {\n\t\tposition: boolean;\n\t\trotation: boolean;\n\t\t/** @deprecated Use per-axis velocityX/Y/Z flags for new code */\n\t\tvelocity: boolean;\n\t\tvelocityX: boolean;\n\t\tvelocityY: boolean;\n\t\tvelocityZ: boolean;\n\t\tvelocityChannels: boolean;\n\t\tangularVelocity: boolean;\n\t};\n}\n\n/**\n * Create a new transform store with optional initial values.\n * The store is a Valtio proxy, making it reactive for debugging/visualization.\n * \n * @param initial Optional initial state values\n * @returns Reactive transform state\n */\nexport function createTransformStore(initial?: Partial<TransformState>): TransformState {\n\tconst defaultState: TransformState = {\n\t\tposition: { x: 0, y: 0, z: 0 },\n\t\trotation: { x: 0, y: 0, z: 0, w: 1 },\n\t\tvelocity: { x: 0, y: 0, z: 0 },\n\t\tangularVelocity: { x: 0, y: 0, z: 0 },\n\t\tvelocityChannels: {},\n\t\tdirty: {\n\t\t\tposition: false,\n\t\t\trotation: false,\n\t\t\tvelocity: false,\n\t\t\tvelocityX: false,\n\t\t\tvelocityY: false,\n\t\t\tvelocityZ: false,\n\t\t\tvelocityChannels: false,\n\t\t\tangularVelocity: false,\n\t\t},\n\t};\n\n\treturn proxy({\n\t\t...defaultState,\n\t\t...initial,\n\t\t// Ensure dirty flags are properly initialized even if partial initial state\n\t\tdirty: {\n\t\t\t...defaultState.dirty,\n\t\t\t...initial?.dirty,\n\t\t},\n\t});\n}\n\n/**\n * Reset a transform store to its initial clean state.\n * Called after applying changes to prepare for the next frame.\n * \n * @param store The transform store to reset\n */\nexport function resetTransformStore(store: TransformState): void {\n\t// Reset position deltas\n\tstore.position.x = 0;\n\tstore.position.y = 0;\n\tstore.position.z = 0;\n\n\t// Reset rotation to identity quaternion\n\tstore.rotation.x = 0;\n\tstore.rotation.y = 0;\n\tstore.rotation.z = 0;\n\tstore.rotation.w = 1;\n\n\t// Reset velocity\n\tstore.velocity.x = 0;\n\tstore.velocity.y = 0;\n\tstore.velocity.z = 0;\n\n\t// Reset angular velocity\n\tstore.angularVelocity.x = 0;\n\tstore.angularVelocity.y = 0;\n\tstore.angularVelocity.z = 0;\n\n\t// Reset per-source velocity intents\n\tfor (const sourceId of Object.keys(store.velocityChannels)) {\n\t\tdelete store.velocityChannels[sourceId];\n\t}\n\n\t// Clear all dirty flags\n\tstore.dirty.position = false;\n\tstore.dirty.rotation = false;\n\tstore.dirty.velocity = false;\n\tstore.dirty.velocityX = false;\n\tstore.dirty.velocityY = false;\n\tstore.dirty.velocityZ = false;\n\tstore.dirty.velocityChannels = false;\n\tstore.dirty.angularVelocity = false;\n}\n","import { RigidBody } from '@dimforge/rapier3d-compat';\nimport { TransformState } from './transform-store';\n\ninterface ComposedAxis {\n\tvalue: number;\n\ttouched: boolean;\n}\n\ninterface ComposedVelocity {\n\tx: ComposedAxis;\n\ty: ComposedAxis;\n\tz: ComposedAxis;\n}\n\ninterface ResolvedIntent {\n\tsourceId: string;\n\tmode: 'replace' | 'add';\n\tpriority: number;\n\tx?: number;\n\ty?: number;\n\tz?: number;\n}\n\nfunction sortIntents(a: ResolvedIntent, b: ResolvedIntent): number {\n\tif (a.priority !== b.priority) return a.priority - b.priority;\n\treturn a.sourceId.localeCompare(b.sourceId);\n}\n\nfunction composeAxis(\n\tcurrent: number,\n\tintents: ResolvedIntent[],\n\taxis: 'x' | 'y' | 'z',\n): ComposedAxis {\n\tlet touched = false;\n\tlet addSum = 0;\n\tlet hasReplace = false;\n\tlet replaceValue = current;\n\tlet replacePriority = Number.NEGATIVE_INFINITY;\n\n\tfor (const intent of intents) {\n\t\tconst axisValue = intent[axis];\n\t\tif (axisValue == null) continue;\n\t\ttouched = true;\n\n\t\tif (intent.mode === 'add') {\n\t\t\taddSum += axisValue;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!hasReplace || intent.priority >= replacePriority) {\n\t\t\thasReplace = true;\n\t\t\treplacePriority = intent.priority;\n\t\t\treplaceValue = axisValue;\n\t\t}\n\t}\n\n\treturn {\n\t\ttouched,\n\t\tvalue: (hasReplace ? replaceValue : 0) + addSum,\n\t};\n}\n\nfunction composeVelocity(current: { x: number; y: number; z: number }, intents: ResolvedIntent[]): ComposedVelocity {\n\treturn {\n\t\tx: composeAxis(current.x, intents, 'x'),\n\t\ty: composeAxis(current.y, intents, 'y'),\n\t\tz: composeAxis(current.z, intents, 'z'),\n\t};\n}\n\nfunction clearVelocityChannels(store: TransformState): void {\n\tfor (const sourceId in store.velocityChannels) {\n\t\tdelete store.velocityChannels[sourceId];\n\t}\n\tstore.dirty.velocityChannels = false;\n}\n\n/**\n * Entity that can have transformations applied from a store\n */\nexport interface TransformableEntity {\n\tbody: RigidBody | null;\n\ttransformStore?: TransformState;\n}\n\n/**\n * Apply accumulated transformations from the store to the physics body.\n * \n * This is called automatically after onUpdate() callbacks complete,\n * flushing all pending transformations to the physics engine in a single batch.\n * \n * Flow:\n * 1. Check dirty flags to see what changed\n * 2. Apply changes to RigidBody\n * 3. Reset store for next frame\n * \n * @param entity Entity with physics body and transform store\n * @param store Transform store containing pending changes\n */\nexport function applyTransformChanges(\n\tentity: TransformableEntity,\n\tstore: TransformState\n): void {\n\tif (!entity.body) return;\n\n\tconst hasPosition = store.dirty.position;\n\tconst hasRotation = store.dirty.rotation;\n\tconst hasAngularVelocity = store.dirty.angularVelocity;\n\tconst hasPerAxis = store.dirty.velocityX || store.dirty.velocityY || store.dirty.velocityZ;\n\tconst hasLegacyVelocity = store.dirty.velocity;\n\tconst hasChannels = store.dirty.velocityChannels;\n\n\tif (!hasPosition && !hasRotation && !hasAngularVelocity && !hasPerAxis && !hasLegacyVelocity && !hasChannels) {\n\t\treturn;\n\t}\n\n\tconst intents: ResolvedIntent[] = [];\n\tif (hasChannels) {\n\t\tfor (const sourceId in store.velocityChannels) {\n\t\t\tconst intent = store.velocityChannels[sourceId];\n\t\t\tintents.push({\n\t\t\t\tsourceId,\n\t\t\t\tmode: intent.mode ?? 'replace',\n\t\t\t\tpriority: intent.priority ?? 0,\n\t\t\t\tx: intent.x,\n\t\t\t\ty: intent.y,\n\t\t\t\tz: intent.z,\n\t\t\t});\n\t\t}\n\t}\n\n\t// Legacy fallback mapped into the same composition pipeline.\n\tif (hasLegacyVelocity) {\n\t\tintents.push({\n\t\t\tsourceId: '__legacy_velocity__',\n\t\t\tmode: 'replace',\n\t\t\tpriority: -100,\n\t\t\tx: store.velocity.x,\n\t\t\ty: store.velocity.y,\n\t\t\tz: store.velocity.z,\n\t\t});\n\t} else if (hasPerAxis) {\n\t\tintents.push({\n\t\t\tsourceId: '__legacy_per_axis__',\n\t\t\tmode: 'replace',\n\t\t\tpriority: -100,\n\t\t\tx: store.dirty.velocityX ? store.velocity.x : undefined,\n\t\t\ty: store.dirty.velocityY ? store.velocity.y : undefined,\n\t\t\tz: store.dirty.velocityZ ? store.velocity.z : undefined,\n\t\t});\n\t}\n\n\tif (intents.length > 0) {\n\t\tconst current = entity.body.linvel();\n\t\tif (intents.length > 1) {\n\t\t\tintents.sort(sortIntents);\n\t\t}\n\t\tconst composed = composeVelocity(current, intents);\n\n\t\tentity.body.setLinvel(\n\t\t\t{\n\t\t\t\tx: composed.x.touched ? composed.x.value : current.x,\n\t\t\t\ty: composed.y.touched ? composed.y.value : current.y,\n\t\t\t\tz: composed.z.touched ? composed.z.value : current.z,\n\t\t\t},\n\t\t\ttrue,\n\t\t);\n\t}\n\n\tif (hasRotation) {\n\t\tentity.body.setRotation(store.rotation, true);\n\t}\n\n\tif (hasAngularVelocity) {\n\t\tentity.body.setAngvel(store.angularVelocity, true);\n\t}\n\n\tif (hasPosition) {\n\t\tconst current = entity.body.translation();\n\t\tentity.body.setTranslation(\n\t\t\t{\n\t\t\t\tx: current.x + store.position.x,\n\t\t\t\ty: current.y + store.position.y,\n\t\t\t\tz: current.z + store.position.z,\n\t\t\t},\n\t\t\ttrue,\n\t\t);\n\t}\n\n\t// Reset dirty flags for next frame\n\tstore.dirty.position = false;\n\tstore.dirty.rotation = false;\n\tstore.dirty.velocity = false;\n\tstore.dirty.velocityX = false;\n\tstore.dirty.velocityY = false;\n\tstore.dirty.velocityZ = false;\n\tclearVelocityChannels(store);\n\tstore.dirty.angularVelocity = false;\n}\n","import { Vector3 } from 'three';\nimport { RigidBody, Vector } from '@dimforge/rapier3d-compat';\nimport type { TransformState } from './transform-store';\nimport { createTransformStore } from './transform-store';\nimport { setVelocityIntent } from './velocity-intents';\n\nexport interface EntityWithBody {\n\tbody: RigidBody | null;\n\ttransformStore?: TransformState;\n}\n\n/**\n * Move an entity along the X axis, preserving other velocities.\n */\nexport function moveX(entity: EntityWithBody, delta: number): void {\n\tif (!entity.transformStore) return;\n\tsetVelocityIntent(entity.transformStore, 'actions', { x: delta }, { mode: 'replace' });\n}\n\n/**\n * Move an entity along the Y axis, preserving other velocities.\n */\nexport function moveY(entity: EntityWithBody, delta: number): void {\n\tif (!entity.transformStore) return;\n\tsetVelocityIntent(entity.transformStore, 'actions', { y: delta }, { mode: 'replace' });\n}\n\n/**\n * Move an entity along the Z axis, preserving other velocities.\n */\nexport function moveZ(entity: EntityWithBody, delta: number): void {\n\tif (!entity.transformStore) return;\n\tsetVelocityIntent(entity.transformStore, 'actions', { z: delta }, { mode: 'replace' });\n}\n\n/**\n * Move an entity along the X and Y axis, preserving Z velocity.\n */\nexport function moveXY(entity: EntityWithBody, deltaX: number, deltaY: number): void {\n\tif (!entity.transformStore) return;\n\tsetVelocityIntent(\n\t\tentity.transformStore,\n\t\t'actions',\n\t\t{ x: deltaX, y: deltaY },\n\t\t{ mode: 'replace' },\n\t);\n}\n\n/**\n * Move an entity along the X and Z axis, preserving Y velocity.\n */\nexport function moveXZ(entity: EntityWithBody, deltaX: number, deltaZ: number): void {\n\tif (!entity.transformStore) return;\n\tsetVelocityIntent(\n\t\tentity.transformStore,\n\t\t'actions',\n\t\t{ x: deltaX, z: deltaZ },\n\t\t{ mode: 'replace' },\n\t);\n}\n\n/**\n * Move entity based on a vector, adding to existing velocities.\n */\nexport function move(entity: EntityWithBody, vector: Vector3): void {\n\tif (!entity.transformStore) return;\n\tsetVelocityIntent(\n\t\tentity.transformStore,\n\t\t'actions',\n\t\t{ x: vector.x, y: vector.y, z: vector.z },\n\t\t{ mode: 'add' },\n\t);\n}\n\n/**\n * Reset entity velocity\n */\nexport function resetVelocity(entity: EntityWithBody): void {\n\tif (!entity.body) return;\n\tentity.body.setLinvel(new Vector3(0, 0, 0), true);\n\tentity.body.setLinearDamping(5);\n}\n\n/**\n * Move entity forward in 2D space, preserving Z velocity\n */\nexport function moveForwardXY(entity: EntityWithBody, delta: number, rotation2DAngle: number): void {\n\tconst deltaX = Math.sin(-rotation2DAngle) * delta;\n\tconst deltaY = Math.cos(-rotation2DAngle) * delta;\n\tmoveXY(entity, deltaX, deltaY);\n}\n\n/**\n * Get entity position\n */\nexport function getPosition(entity: EntityWithBody): Vector | null {\n\tif (!entity.body) return null;\n\treturn entity.body.translation();\n}\n\n/**\n * Get entity velocity\n */\nexport function getVelocity(entity: EntityWithBody): Vector | null {\n\tif (!entity.body) return null;\n\treturn entity.body.linvel();\n}\n\n/**\n * Set entity position\n */\nexport function setPosition(entity: EntityWithBody, x: number, y: number, z: number): void {\n\tif (!entity.body) return;\n\tentity.body.setTranslation({ x, y, z }, true);\n}\n\n/**\n * Set entity X position\n */\nexport function setPositionX(entity: EntityWithBody, x: number): void {\n\tif (!entity.body) return;\n\tconst { y, z } = entity.body.translation();\n\tentity.body.setTranslation({ x, y, z }, true);\n}\n\n/**\n * Set entity Y position\n */\nexport function setPositionY(entity: EntityWithBody, y: number): void {\n\tif (!entity.body) return;\n\tconst { x, z } = entity.body.translation();\n\tentity.body.setTranslation({ x, y, z }, true);\n}\n\n/**\n * Set entity Z position\n */\nexport function setPositionZ(entity: EntityWithBody, z: number): void {\n\tif (!entity.body) return;\n\tconst { x, y } = entity.body.translation();\n\tentity.body.setTranslation({ x, y, z }, true);\n}\n\n/**\n * Wrap entity around 2D bounds\n */\nexport function wrapAroundXY(entity: EntityWithBody, boundsX: number, boundsY: number): void {\n\tconst position = getPosition(entity);\n\tif (!position) return;\n\n\tconst { x, y } = position;\n\tconst newX = x > boundsX ? -boundsX : (x < -boundsX ? boundsX : x);\n\tconst newY = y > boundsY ? -boundsY : (y < -boundsY ? boundsY : y);\n\n\tif (newX !== x || newY !== y) {\n\t\tsetPosition(entity, newX, newY, 0);\n\t}\n}\n\n/**\n * Wrap entity around 3D bounds\n */\nexport function wrapAround3D(entity: EntityWithBody, boundsX: number, boundsY: number, boundsZ: number): void {\n\tconst position = getPosition(entity);\n\tif (!position) return;\n\n\tconst { x, y, z } = position;\n\tconst newX = x > boundsX ? -boundsX : (x < -boundsX ? boundsX : x);\n\tconst newY = y > boundsY ? -boundsY : (y < -boundsY ? boundsY : y);\n\tconst newZ = z > boundsZ ? -boundsZ : (z < -boundsZ ? boundsZ : z);\n\n\tif (newX !== x || newY !== y || newZ !== z) {\n\t\tsetPosition(entity, newX, newY, newZ);\n\t}\n}\n\n/**\n * Enhanced moveable entity with bound methods\n */\nexport interface MoveableEntity extends EntityWithBody {\n\tmoveX(delta: number): void;\n\tmoveY(delta: number): void;\n\tmoveZ(delta: number): void;\n\tmoveXY(deltaX: number, deltaY: number): void;\n\tmoveXZ(deltaX: number, deltaZ: number): void;\n\tmove(vector: Vector3): void;\n\tresetVelocity(): void;\n\tmoveForwardXY(delta: number, rotation2DAngle: number): void;\n\tgetPosition(): Vector | null;\n\tgetVelocity(): Vector | null;\n\tsetPosition(x: number, y: number, z: number): void;\n\tsetPositionX(x: number): void;\n\tsetPositionY(y: number): void;\n\tsetPositionZ(z: number): void;\n\twrapAroundXY(boundsX: number, boundsY: number): void;\n\twrapAround3D(boundsX: number, boundsY: number, boundsZ: number): void;\n}\n\n/**\n * Enhance an entity with additive movement methods.\n * Automatically creates a transform store if one doesn't exist.\n */\nexport function makeMoveable<T extends EntityWithBody>(entity: T): T & MoveableEntity {\n\tconst moveable = entity as T & MoveableEntity;\n\n\t// Create transform store if it doesn't exist\n\tif (!moveable.transformStore) {\n\t\tmoveable.transformStore = createTransformStore();\n\t}\n\n\tmoveable.moveX = (delta: number) => moveX(entity, delta);\n\tmoveable.moveY = (delta: number) => moveY(entity, delta);\n\tmoveable.moveZ = (delta: number) => moveZ(entity, delta);\n\tmoveable.moveXY = (deltaX: number, deltaY: number) => moveXY(entity, deltaX, deltaY);\n\tmoveable.moveXZ = (deltaX: number, deltaZ: number) => moveXZ(entity, deltaX, deltaZ);\n\tmoveable.move = (vector: Vector3) => move(entity, vector);\n\tmoveable.resetVelocity = () => resetVelocity(entity);\n\tmoveable.moveForwardXY = (delta: number, rotation2DAngle: number) => moveForwardXY(entity, delta, rotation2DAngle);\n\tmoveable.getPosition = () => getPosition(entity);\n\tmoveable.getVelocity = () => getVelocity(entity);\n\tmoveable.setPosition = (x: number, y: number, z: number) => setPosition(entity, x, y, z);\n\tmoveable.setPositionX = (x: number) => setPositionX(entity, x);\n\tmoveable.setPositionY = (y: number) => setPositionY(entity, y);\n\tmoveable.setPositionZ = (z: number) => setPositionZ(entity, z);\n\tmoveable.wrapAroundXY = (boundsX: number, boundsY: number) => wrapAroundXY(entity, boundsX, boundsY);\n\tmoveable.wrapAround3D = (boundsX: number, boundsY: number, boundsZ: number) => wrapAround3D(entity, boundsX, boundsY, boundsZ);\n\n\treturn moveable;\n}","import { Euler, Vector3, MathUtils, Quaternion } from 'three';\nimport { RigidBody } from '@dimforge/rapier3d-compat';\nimport type { TransformState } from './transform-store';\nimport { createTransformStore } from './transform-store';\n\nexport interface RotatableEntity {\n\tbody: RigidBody | null;\n\tgroup: any;\n\ttransformStore?: TransformState;\n}\n\nfunction syncImmediateRotation(entity: RotatableEntity): void {\n\tif (!entity.group || !entity.transformStore) return;\n\n\tentity.group.setRotationFromQuaternion(\n\t\tnew Quaternion(\n\t\t\tentity.transformStore.rotation.x,\n\t\t\tentity.transformStore.rotation.y,\n\t\t\tentity.transformStore.rotation.z,\n\t\t\tentity.transformStore.rotation.w,\n\t\t),\n\t);\n}\n\n/**\n * Rotate an entity in the direction of a movement vector\n */\nexport function rotateInDirection(entity: RotatableEntity, moveVector: Vector3): void {\n\tif (!entity.body) return;\n\tconst rotate = Math.atan2(-moveVector.x, moveVector.z);\n\trotateYEuler(entity, rotate);\n}\n\n/**\n * Rotate an entity around the Y axis using Euler angles\n */\nexport function rotateYEuler(entity: RotatableEntity, amount: number): void {\n\trotateEuler(entity, new Vector3(0, -amount, 0));\n}\n\n/**\n * Rotate an entity using Euler angles\n */\nexport function rotateEuler(entity: RotatableEntity, rotation: Vector3): void {\n\tif (!entity.transformStore) return;\n\tconst quat = new Quaternion().setFromEuler(\n\t\tnew Euler(rotation.x, rotation.y, rotation.z),\n\t);\n\tentity.transformStore.rotation.w = quat.w;\n\tentity.transformStore.rotation.x = quat.x;\n\tentity.transformStore.rotation.y = quat.y;\n\tentity.transformStore.rotation.z = quat.z;\n\tentity.transformStore.dirty.rotation = true;\n\tsyncImmediateRotation(entity);\n}\n\n/**\n * Rotate an entity around the Y axis.\n */\nexport function rotateY(entity: RotatableEntity, delta: number): void {\n\tif (!entity.transformStore) return;\n\t\n\t// Create delta rotation quaternion\n\tconst halfAngle = delta / 2;\n\tconst deltaW = Math.cos(halfAngle);\n\tconst deltaY = Math.sin(halfAngle);\n\t\n\t// Get current rotation from store\n\tconst q = entity.transformStore.rotation;\n\t\n\t// Multiply quaternions: q_new = q_current * q_delta\n\t// For Y-axis rotation: q_delta = (0, deltaY, 0, deltaW)\n\tconst newW = q.w * deltaW - q.y * deltaY;\n\tconst newX = q.x * deltaW + q.z * deltaY;\n\tconst newY = q.y * deltaW + q.w * deltaY;\n\tconst newZ = q.z * deltaW - q.x * deltaY;\n\t\n\tentity.transformStore.rotation.w = newW;\n\tentity.transformStore.rotation.x = newX;\n\tentity.transformStore.rotation.y = newY;\n\tentity.transformStore.rotation.z = newZ;\n\tentity.transformStore.dirty.rotation = true;\n\tsyncImmediateRotation(entity);\n}\n\n/**\n * Rotate an entity around the X axis.\n */\nexport function rotateX(entity: RotatableEntity, delta: number): void {\n\tif (!entity.transformStore) return;\n\t\n\t// Create delta rotation quaternion\n\tconst halfAngle = delta / 2;\n\tconst deltaW = Math.cos(halfAngle);\n\tconst deltaX = Math.sin(halfAngle);\n\t\n\t// Get current rotation from store\n\tconst q = entity.transformStore.rotation;\n\t\n\t// Multiply quaternions: q_new = q_current * q_delta\n\t// For X-axis rotation: q_delta = (deltaX, 0, 0, deltaW)\n\tconst newW = q.w * deltaW - q.x * deltaX;\n\tconst newX = q.x * deltaW + q.w * deltaX;\n\tconst newY = q.y * deltaW + q.z * deltaX;\n\tconst newZ = q.z * deltaW - q.y * deltaX;\n\t\n\tentity.transformStore.rotation.w = newW;\n\tentity.transformStore.rotation.x = newX;\n\tentity.transformStore.rotation.y = newY;\n\tentity.transformStore.rotation.z = newZ;\n\tentity.transformStore.dirty.rotation = true;\n\tsyncImmediateRotation(entity);\n}\n\n/**\n * Rotate an entity around the Z axis.\n */\nexport function rotateZ(entity: RotatableEntity, delta: number): void {\n\tif (!entity.transformStore) return;\n\t\n\t// Create delta rotation quaternion\n\tconst halfAngle = delta / 2;\n\tconst deltaW = Math.cos(halfAngle);\n\tconst deltaZ = Math.sin(halfAngle);\n\t\n\t// Get current rotation from store\n\tconst q = entity.transformStore.rotation;\n\t\n\t// Multiply quaternions: q_new = q_current * q_delta\n\t// For Z-axis rotation: q_delta = (0, 0, deltaZ, deltaW)\n\tconst newW = q.w * deltaW - q.z * deltaZ;\n\tconst newX = q.x * deltaW - q.y * deltaZ;\n\tconst newY = q.y * deltaW + q.x * deltaZ;\n\tconst newZ = q.z * deltaW + q.w * deltaZ;\n\t\n\tentity.transformStore.rotation.w = newW;\n\tentity.transformStore.rotation.x = newX;\n\tentity.transformStore.rotation.y = newY;\n\tentity.transformStore.rotation.z = newZ;\n\tentity.transformStore.dirty.rotation = true;\n\t(entity as any)._rotation2DAngle = ((entity as any)._rotation2DAngle ?? 0) + delta;\n\tsyncImmediateRotation(entity);\n}\n\n/**\n * Set rotation around Y axis.\n */\nexport function setRotationY(entity: RotatableEntity, y: number): void {\n\tif (!entity.transformStore) return;\n\tconst halfAngle = y / 2;\n\tconst w = Math.cos(halfAngle);\n\tconst yComponent = Math.sin(halfAngle);\n\tentity.transformStore.rotation.w = w;\n\tentity.transformStore.rotation.x = 0;\n\tentity.transformStore.rotation.y = yComponent;\n\tentity.transformStore.rotation.z = 0;\n\tentity.transformStore.dirty.rotation = true;\n\tsyncImmediateRotation(entity);\n}\n\n/**\n * Set rotation around Y axis\n */\nexport function setRotationDegreesY(entity: RotatableEntity, y: number): void {\n\tif (!entity.body) return;\n\tsetRotationY(entity, MathUtils.degToRad(y));\n}\n\n/**\n * Set rotation around X axis.\n */\nexport function setRotationX(entity: RotatableEntity, x: number): void {\n\tif (!entity.transformStore) return;\n\tconst halfAngle = x / 2;\n\tconst w = Math.cos(halfAngle);\n\tconst xComponent = Math.sin(halfAngle);\n\tentity.transformStore.rotation.w = w;\n\tentity.transformStore.rotation.x = xComponent;\n\tentity.transformStore.rotation.y = 0;\n\tentity.transformStore.rotation.z = 0;\n\tentity.transformStore.dirty.rotation = true;\n\tsyncImmediateRotation(entity);\n}\n\n/**\n * Set rotation around X axis\n */\nexport function setRotationDegreesX(entity: RotatableEntity, x: number): void {\n\tif (!entity.body) return;\n\tsetRotationX(entity, MathUtils.degToRad(x));\n}\n\n/**\n * Set rotation around Z axis.\n */\nexport function setRotationZ(entity: RotatableEntity, z: number): void {\n\tif (!entity.transformStore) return;\n\tconst halfAngle = z / 2;\n\tconst w = Math.cos(halfAngle);\n\tconst zComponent = Math.sin(halfAngle);\n\tentity.transformStore.rotation.w = w;\n\tentity.transformStore.rotation.x = 0;\n\tentity.transformStore.rotation.y = 0;\n\tentity.transformStore.rotation.z = zComponent;\n\tentity.transformStore.dirty.rotation = true;\n\t(entity as any)._rotation2DAngle = z;\n\tsyncImmediateRotation(entity);\n}\n\n/**\n * Set rotation around Z axis\n */\nexport function setRotationDegreesZ(entity: RotatableEntity, z: number): void {\n\tif (!entity.body) return;\n\tsetRotationZ(entity, MathUtils.degToRad(z));\n}\n\n/**\n * Set rotation for all axes.\n */\nexport function setRotation(entity: RotatableEntity, x: number, y: number, z: number): void {\n\tif (!entity.transformStore) return;\n\tconst quat = new Quaternion().setFromEuler(new Euler(x, y, z));\n\tentity.transformStore.rotation.w = quat.w;\n\tentity.transformStore.rotation.x = quat.x;\n\tentity.transformStore.rotation.y = quat.y;\n\tentity.transformStore.rotation.z = quat.z;\n\tentity.transformStore.dirty.rotation = true;\n\t(entity as any)._rotation2DAngle = z;\n\tsyncImmediateRotation(entity);\n}\n\n/**\n * Set rotation for all axes\n */\nexport function setRotationDegrees(entity: RotatableEntity, x: number, y: number, z: number): void {\n\tif (!entity.body) return;\n\tsetRotation(entity, MathUtils.degToRad(x), MathUtils.degToRad(y), MathUtils.degToRad(z));\n}\n\n/**\n * Get current rotation\n */\nexport function getRotation(entity: RotatableEntity): any {\n\tif (!entity.body) return null;\n\treturn entity.body.rotation();\n}\n\n/**\n * Rotatable entity API with bound methods\n */\nexport interface RotatableEntityAPI extends RotatableEntity {\n\trotateInDirection(moveVector: Vector3): void;\n\trotateYEuler(amount: number): void;\n\trotateEuler(rotation: Vector3): void;\n\trotateY(delta: number): void;\n\trotateX(delta: number): void;\n\trotateZ(delta: number): void;\n\tsetRotationY(y: number): void;\n\tsetRotationX(x: number): void;\n\tsetRotationZ(z: number): void;\n\tsetRotationDegrees(x: number, y: number, z: number): void;\n\tsetRotationDegreesY(y: number): void;\n\tsetRotationDegreesX(x: number): void;\n\tsetRotationDegreesZ(z: number): void;\n\tsetRotation(x: number, y: number, z: number): void;\n\tgetRotation(): any;\n}\n\n/**\n * Enhance an entity instance with rotatable methods.\n * Automatically creates a transform store if one doesn't exist.\n */\nexport function makeRotatable<T extends RotatableEntity>(entity: T): T & RotatableEntityAPI {\n\tconst rotatableEntity = entity as T & RotatableEntityAPI;\n\n\t// Create transform store if it doesn't exist\n\tif (!rotatableEntity.transformStore) {\n\t\trotatableEntity.transformStore = createTransformStore();\n\t}\n\n\trotatableEntity.rotateInDirection = (moveVector: Vector3) => rotateInDirection(entity, moveVector);\n\trotatableEntity.rotateYEuler = (amount: number) => rotateYEuler(entity, amount);\n\trotatableEntity.rotateEuler = (rotation: Vector3) => rotateEuler(entity, rotation);\n\trotatableEntity.rotateX = (delta: number) => rotateX(entity, delta);\n\trotatableEntity.rotateY = (delta: number) => rotateY(entity, delta);\n\trotatableEntity.rotateZ = (delta: number) => rotateZ(entity, delta);\n\trotatableEntity.setRotationY = (y: number) => setRotationY(entity, y);\n\trotatableEntity.setRotationX = (x: number) => setRotationX(entity, x);\n\trotatableEntity.setRotationZ = (z: number) => setRotationZ(entity, z);\n\trotatableEntity.setRotationDegreesY = (y: number) => setRotationDegreesY(entity, y);\n\trotatableEntity.setRotationDegreesX = (x: number) => setRotationDegreesX(entity, x);\n\trotatableEntity.setRotationDegreesZ = (z: number) => setRotationDegreesZ(entity, z);\n\trotatableEntity.setRotationDegrees = (x: number, y: number, z: number) => setRotationDegrees(entity, x, y, z);\n\trotatableEntity.setRotation = (x: number, y: number, z: number) => setRotation(entity, x, y, z);\n\trotatableEntity.getRotation = () => getRotation(entity);\n\n\treturn rotatableEntity;\n}\n","import { UpdateContext } from \"../core/base-node-life-cycle\";\n\n/**\n * Listen for a single global key change inside an onUpdate pipeline.\n * Usage: onUpdate(globalChange('p1Score', (value) => { ... }))\n */\nexport function globalChange<T = any>(\n\tkey: string,\n\tcallback: (value: T, ctx: UpdateContext<any>) => void\n) {\n\tlet previousValue: T | undefined = undefined;\n\treturn (ctx: UpdateContext<any>) => {\n\t\tconst currentValue = ctx.globals?.[key] as T;\n\t\tif (previousValue !== currentValue) {\n\t\t\t// Ignore the very first undefined->value transition only if both are undefined\n\t\t\tif (!(previousValue === undefined && currentValue === undefined)) {\n\t\t\t\tcallback(currentValue, ctx);\n\t\t\t}\n\t\t\tpreviousValue = currentValue;\n\t\t}\n\t};\n}\n\n/**\n * Listen for multiple global key changes inside an onUpdate pipeline.\n * Calls back when any of the provided keys changes.\n * Usage: onUpdate(globalChanges(['p1Score','p2Score'], ([p1,p2]) => { ... }))\n */\nexport function globalChanges<T = any>(\n\tkeys: string[],\n\tcallback: (values: T[], ctx: UpdateContext<any>) => void\n) {\n\tlet previousValues: (T | undefined)[] = new Array(keys.length).fill(undefined);\n\treturn (ctx: UpdateContext<any>) => {\n\t\tconst currentValues = keys.map((k) => ctx.globals?.[k] as T);\n\t\tconst hasAnyChange = currentValues.some((val, idx) => previousValues[idx] !== val);\n\t\tif (hasAnyChange) {\n\t\t\t// Ignore initial all-undefined state\n\t\t\tconst allPrevUndef = previousValues.every((v) => v === undefined);\n\t\t\tconst allCurrUndef = currentValues.every((v) => v === undefined);\n\t\t\tif (!(allPrevUndef && allCurrUndef)) {\n\t\t\t\tcallback(currentValues as T[], ctx);\n\t\t\t}\n\t\t\tpreviousValues = currentValues;\n\t\t}\n\t};\n}\n\n\n/**\n * Listen for a single stage variable change inside an onUpdate pipeline.\n * Usage: onUpdate(variableChange('score', (value, ctx) => { ... }))\n */\nexport function variableChange<T = any>(\n\tkey: string,\n\tcallback: (value: T, ctx: UpdateContext<any>) => void\n) {\n\tlet previousValue: T | undefined = undefined;\n\treturn (ctx: UpdateContext<any>) => {\n\t\t// @ts-ignore - stage is optional on UpdateContext\n\t\tconst currentValue = (ctx.stage?.getVariable?.(key) ?? undefined) as T;\n\t\tif (previousValue !== currentValue) {\n\t\t\tif (!(previousValue === undefined && currentValue === undefined)) {\n\t\t\t\tcallback(currentValue, ctx);\n\t\t\t}\n\t\t\tpreviousValue = currentValue;\n\t\t}\n\t};\n}\n\n/**\n * Listen for multiple stage variable changes; fires when any changes.\n * Usage: onUpdate(variableChanges(['a','b'], ([a,b], ctx) => { ... }))\n */\nexport function variableChanges<T = any>(\n\tkeys: string[],\n\tcallback: (values: T[], ctx: UpdateContext<any>) => void\n) {\n\tlet previousValues: (T | undefined)[] = new Array(keys.length).fill(undefined);\n\treturn (ctx: UpdateContext<any>) => {\n\t\t// @ts-ignore - stage is optional on UpdateContext\n\t\tconst reader = (k: string) => ctx.stage?.getVariable?.(k) as T;\n\t\tconst currentValues = keys.map(reader);\n\t\tconst hasAnyChange = currentValues.some((val, idx) => previousValues[idx] !== val);\n\t\tif (hasAnyChange) {\n\t\t\tconst allPrevUndef = previousValues.every((v) => v === undefined);\n\t\t\tconst allCurrUndef = currentValues.every((v) => v === undefined);\n\t\t\tif (!(allPrevUndef && allCurrUndef)) {\n\t\t\t\tcallback(currentValues as T[], ctx);\n\t\t\t}\n\t\t\tpreviousValues = currentValues;\n\t\t}\n\t};\n}\n\n\n"],"mappings":"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","names":["Vector3","Vector3"]}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { S as SyncStateMachine } from '../sync-state-machine-CZyspBpj.js';
|
|
2
|
+
import { c as BehaviorDescriptor } from '../behavior-descriptor-BXnVR8Ki.js';
|
|
3
|
+
import 'typescript-fsm';
|
|
4
|
+
import 'bitecs';
|
|
5
|
+
|
|
6
|
+
interface JumpConfig2D {
|
|
7
|
+
jumpHeight: number;
|
|
8
|
+
gravity: number;
|
|
9
|
+
maxFallSpeed?: number;
|
|
10
|
+
maxJumps: number;
|
|
11
|
+
resetJumpsOnGround: boolean;
|
|
12
|
+
coyoteTimeMs: number;
|
|
13
|
+
jumpBufferMs: number;
|
|
14
|
+
minTimeBetweenJumpsMs?: number;
|
|
15
|
+
variableJump?: {
|
|
16
|
+
enabled: boolean;
|
|
17
|
+
cutGravityMultiplier: number;
|
|
18
|
+
maxHoldMs?: number;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
declare function createJumpConfig2D(options?: Partial<JumpConfig2D>): JumpConfig2D;
|
|
22
|
+
interface JumpInput2D {
|
|
23
|
+
jumpPressed: boolean;
|
|
24
|
+
jumpHeld: boolean;
|
|
25
|
+
jumpReleased: boolean;
|
|
26
|
+
fastFall?: boolean;
|
|
27
|
+
}
|
|
28
|
+
declare function createJumpInput2D(): JumpInput2D;
|
|
29
|
+
interface JumpContext2D {
|
|
30
|
+
dt: number;
|
|
31
|
+
velocityY: number;
|
|
32
|
+
isGrounded: boolean;
|
|
33
|
+
timeSinceGroundedMs: number;
|
|
34
|
+
setVerticalVelocity(y: number): void;
|
|
35
|
+
}
|
|
36
|
+
interface JumpState2D {
|
|
37
|
+
jumpsUsed: number;
|
|
38
|
+
lastJumpTimeMs: number;
|
|
39
|
+
bufferedJumpMs: number;
|
|
40
|
+
coyoteMs: number;
|
|
41
|
+
isJumping: boolean;
|
|
42
|
+
jumpHoldMs: number;
|
|
43
|
+
jumpCutApplied: boolean;
|
|
44
|
+
}
|
|
45
|
+
declare function createJumpState2D(): JumpState2D;
|
|
46
|
+
|
|
47
|
+
declare enum Jumper2DTickEvent {
|
|
48
|
+
None = "none",
|
|
49
|
+
Jump = "jump",
|
|
50
|
+
Fall = "fall",
|
|
51
|
+
Land = "land"
|
|
52
|
+
}
|
|
53
|
+
interface Jumper2DTickResult {
|
|
54
|
+
event: Jumper2DTickEvent;
|
|
55
|
+
}
|
|
56
|
+
declare class Jumper2DBehavior {
|
|
57
|
+
tick(config: JumpConfig2D, input: JumpInput2D, ctx: JumpContext2D, state: JumpState2D): Jumper2DTickResult;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
declare enum Jumper2DState {
|
|
61
|
+
Grounded = "grounded",
|
|
62
|
+
Jumping = "jumping",
|
|
63
|
+
Falling = "falling"
|
|
64
|
+
}
|
|
65
|
+
declare enum Jumper2DEvent {
|
|
66
|
+
Jump = "jump",
|
|
67
|
+
Fall = "fall",
|
|
68
|
+
Land = "land"
|
|
69
|
+
}
|
|
70
|
+
interface Jumper2DFSMContext {
|
|
71
|
+
state: JumpState2D;
|
|
72
|
+
}
|
|
73
|
+
declare class Jumper2DFSM {
|
|
74
|
+
private ctx;
|
|
75
|
+
machine: SyncStateMachine<Jumper2DState, Jumper2DEvent, never>;
|
|
76
|
+
constructor(ctx: Jumper2DFSMContext);
|
|
77
|
+
getState(): Jumper2DState;
|
|
78
|
+
dispatch(event: Jumper2DEvent): void;
|
|
79
|
+
isJumping(): boolean;
|
|
80
|
+
isGrounded(): boolean;
|
|
81
|
+
getJumpsUsed(): number;
|
|
82
|
+
applyTickEvent(tickEvent: string, isGrounded: boolean): void;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
interface Jumper2DBehaviorOptions {
|
|
86
|
+
jumpHeight?: number;
|
|
87
|
+
gravity?: number;
|
|
88
|
+
maxFallSpeed?: number;
|
|
89
|
+
maxJumps?: number;
|
|
90
|
+
resetJumpsOnGround?: boolean;
|
|
91
|
+
coyoteTimeMs?: number;
|
|
92
|
+
jumpBufferMs?: number;
|
|
93
|
+
minTimeBetweenJumpsMs?: number;
|
|
94
|
+
variableJump?: JumpConfig2D['variableJump'];
|
|
95
|
+
groundRayLength?: number;
|
|
96
|
+
snapToGroundDistance?: number;
|
|
97
|
+
debugGroundProbe?: boolean;
|
|
98
|
+
}
|
|
99
|
+
interface Jumper2DEntity {
|
|
100
|
+
uuid: string;
|
|
101
|
+
body: any;
|
|
102
|
+
transformStore: any;
|
|
103
|
+
jumper2d: JumpConfig2D;
|
|
104
|
+
$jumper2d: JumpInput2D;
|
|
105
|
+
jumper2dState: JumpState2D;
|
|
106
|
+
}
|
|
107
|
+
declare const Jumper2D: BehaviorDescriptor<Jumper2DBehaviorOptions, {
|
|
108
|
+
getState: () => Jumper2DState;
|
|
109
|
+
isJumping: () => boolean;
|
|
110
|
+
getJumpsUsed: () => number;
|
|
111
|
+
getJumpsRemaining: () => number;
|
|
112
|
+
}, Jumper2DEntity>;
|
|
113
|
+
|
|
114
|
+
export { type JumpConfig2D, type JumpContext2D, type JumpInput2D, type JumpState2D, Jumper2D, Jumper2DBehavior, type Jumper2DBehaviorOptions, type Jumper2DEntity, Jumper2DEvent, Jumper2DFSM, Jumper2DState, Jumper2DTickEvent, type Jumper2DTickResult, createJumpConfig2D, createJumpInput2D, createJumpState2D };
|