@zylem/behaviors 0.4.1 → 0.5.2
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/README.md +13 -1
- package/dist/{behavior-system-y8SGrGWy.d.ts → behavior-system-CldqNUgr.d.ts} +334 -19
- package/dist/{chunk-GFARAJV2.js → chunk-2PAHQZZD.js} +44 -73
- package/dist/chunk-2PAHQZZD.js.map +1 -0
- package/dist/chunk-4CEW64HO.js +210 -0
- package/dist/chunk-4CEW64HO.js.map +1 -0
- package/dist/chunk-4LLK2FJI.js +344 -0
- package/dist/chunk-4LLK2FJI.js.map +1 -0
- package/dist/chunk-4MZ57HPD.js +323 -0
- package/dist/chunk-4MZ57HPD.js.map +1 -0
- package/dist/chunk-52FJIDGL.js +30 -0
- package/dist/chunk-52FJIDGL.js.map +1 -0
- package/dist/{chunk-UYFOLI2X.js → chunk-5A5AYP2C.js} +1 -1
- package/dist/{chunk-UYFOLI2X.js.map → chunk-5A5AYP2C.js.map} +1 -1
- package/dist/chunk-5WRI5ZAA.js +31 -0
- package/dist/chunk-5WRI5ZAA.js.map +1 -0
- package/dist/{chunk-ZRQWSURL.js → chunk-5XPPLTC7.js} +2 -2
- package/dist/chunk-5XPPLTC7.js.map +1 -0
- package/dist/{chunk-VMQHNJJI.js → chunk-7DGXUAPV.js} +17 -23
- package/dist/chunk-7DGXUAPV.js.map +1 -0
- package/dist/{chunk-5SM5SH5O.js → chunk-7JLWZXPA.js} +2 -2
- package/dist/{chunk-QDCJLJUV.js → chunk-7W3KY55G.js} +22 -13
- package/dist/chunk-7W3KY55G.js.map +1 -0
- package/dist/chunk-AS3HR7AS.js +188 -0
- package/dist/chunk-AS3HR7AS.js.map +1 -0
- package/dist/chunk-BAXJ7XHJ.js +155 -0
- package/dist/chunk-BAXJ7XHJ.js.map +1 -0
- package/dist/chunk-DZ4LLQON.js +867 -0
- package/dist/chunk-DZ4LLQON.js.map +1 -0
- package/dist/{chunk-4L36OPY2.js → chunk-G2KUXVU6.js} +81 -135
- package/dist/chunk-G2KUXVU6.js.map +1 -0
- package/dist/{chunk-BXP4K7RF.js → chunk-JVADRLWX.js} +57 -186
- package/dist/chunk-JVADRLWX.js.map +1 -0
- package/dist/{chunk-ZA43LPRM.js → chunk-KIYUUSPK.js} +2 -2
- package/dist/chunk-LZIHSJZR.js +191 -0
- package/dist/chunk-LZIHSJZR.js.map +1 -0
- package/dist/{chunk-WWVD5DR5.js → chunk-M3QDKKDF.js} +2 -8
- package/dist/chunk-M3QDKKDF.js.map +1 -0
- package/dist/{chunk-FTDZAKZW.js → chunk-NQPYMUDF.js} +7 -5
- package/dist/{chunk-FTDZAKZW.js.map → chunk-NQPYMUDF.js.map} +1 -1
- package/dist/{chunk-XP3ASVGJ.js → chunk-NW4SE5XW.js} +2 -2
- package/dist/{chunk-KDY5N6J4.js → chunk-OPZ2LVS2.js} +1 -22
- package/dist/{chunk-KDY5N6J4.js.map → chunk-OPZ2LVS2.js.map} +1 -1
- package/dist/{chunk-5XZWTFGF.js → chunk-PLIXVF7B.js} +3 -3
- package/dist/chunk-PLIXVF7B.js.map +1 -0
- package/dist/{chunk-27UPCP5B.js → chunk-QS3VXWVO.js} +22 -13
- package/dist/chunk-QS3VXWVO.js.map +1 -0
- package/dist/{chunk-Y7YIAWPW.js → chunk-SGHIGTVZ.js} +1 -11
- package/dist/{chunk-Y7YIAWPW.js.map → chunk-SGHIGTVZ.js.map} +1 -1
- package/dist/{chunk-UL6Q5HDP.js → chunk-TBUKJKY6.js} +3 -25
- package/dist/chunk-TBUKJKY6.js.map +1 -0
- package/dist/{chunk-Z72F2ENM.js → chunk-WU6XXL3D.js} +17 -26
- package/dist/chunk-WU6XXL3D.js.map +1 -0
- package/dist/{chunk-A4CA35DQ.js → chunk-Y4PHT2HG.js} +25 -45
- package/dist/chunk-Y4PHT2HG.js.map +1 -0
- package/dist/components-BmievOxF.d.ts +30 -0
- package/dist/cooldown.d.ts +2 -2
- package/dist/cooldown.js +2 -1
- package/dist/coordinators/boundary-ricochet-3d.d.ts +4 -4
- package/dist/coordinators/boundary-ricochet-3d.js +1 -0
- package/dist/coordinators/boundary-ricochet.d.ts +4 -4
- package/dist/coordinators/boundary-ricochet.js +3 -2
- package/dist/coordinators/first-person-shooter.js +1 -0
- package/dist/coordinators/multidirectional-space-shooter.d.ts +3 -3
- package/dist/coordinators/multidirectional-space-shooter.js +5 -3
- package/dist/coordinators/top-down-shooter.d.ts +3 -3
- package/dist/coordinators/top-down-shooter.js +5 -3
- package/dist/core.d.ts +298 -8
- package/dist/core.js +502 -14
- package/dist/core.js.map +1 -1
- package/dist/destructible-3d.d.ts +32 -4
- package/dist/destructible-3d.js +2 -1
- package/dist/destructible-prebake.js +1 -0
- package/dist/{entity-types-CmtQ-IBp.d.ts → entity-types-DhfsZbSK.d.ts} +5 -3
- package/dist/first-person.d.ts +27 -38
- package/dist/first-person.js +8 -8
- package/dist/index.d.ts +14 -15
- package/dist/index.js +38 -53
- package/dist/jumper-2d.d.ts +3 -17
- package/dist/jumper-2d.js +4 -9
- package/dist/jumper-3d.d.ts +5 -39
- package/dist/jumper-3d.js +5 -10
- package/dist/{moveable-Cl9h-_w7.d.ts → moveable-rdIvWiak.d.ts} +21 -4
- package/dist/particle-emitter/presets.js +1 -0
- package/dist/particle-emitter.d.ts +3 -3
- package/dist/particle-emitter.js +3 -2
- package/dist/platformer-2d.d.ts +21 -82
- package/dist/platformer-2d.js +4 -5
- package/dist/platformer-3d.d.ts +21 -78
- package/dist/platformer-3d.js +4 -5
- package/dist/ricochet-2d.d.ts +3 -3
- package/dist/ricochet-2d.js +4 -3
- package/dist/ricochet-3d.d.ts +3 -3
- package/dist/ricochet-3d.js +4 -3
- package/dist/runtime-2d.d.ts +2 -2
- package/dist/runtime-2d.js +1 -0
- package/dist/runtime-2d.js.map +1 -1
- package/dist/screen-visibility.d.ts +2 -2
- package/dist/screen-visibility.js +1 -0
- package/dist/screen-wrap.d.ts +2 -2
- package/dist/screen-wrap.js +4 -3
- package/dist/shooter-2d.d.ts +3 -3
- package/dist/shooter-2d.js +4 -3
- package/dist/thruster.d.ts +5 -34
- package/dist/thruster.js +4 -6
- package/dist/top-down-movement.d.ts +9 -27
- package/dist/top-down-movement.js +6 -6
- package/dist/{vector-Bm65-FOG.d.ts → vector-DjhLUrak.d.ts} +2 -4
- package/dist/world-boundary-2d.d.ts +2 -2
- package/dist/world-boundary-2d.js +5 -4
- package/dist/world-boundary-3d.d.ts +2 -2
- package/dist/world-boundary-3d.js +4 -3
- package/package.json +4 -5
- package/dist/chunk-27UPCP5B.js.map +0 -1
- package/dist/chunk-4L36OPY2.js.map +0 -1
- package/dist/chunk-4URF65BR.js +0 -44
- package/dist/chunk-4URF65BR.js.map +0 -1
- package/dist/chunk-5XZWTFGF.js.map +0 -1
- package/dist/chunk-67XLJ26X.js +0 -536
- package/dist/chunk-67XLJ26X.js.map +0 -1
- package/dist/chunk-75FJIUIG.js +0 -240
- package/dist/chunk-75FJIUIG.js.map +0 -1
- package/dist/chunk-A4CA35DQ.js.map +0 -1
- package/dist/chunk-BXP4K7RF.js.map +0 -1
- package/dist/chunk-F72WX4MH.js +0 -422
- package/dist/chunk-F72WX4MH.js.map +0 -1
- package/dist/chunk-GFARAJV2.js.map +0 -1
- package/dist/chunk-JXMF4NUI.js +0 -309
- package/dist/chunk-JXMF4NUI.js.map +0 -1
- package/dist/chunk-OTLSUYQQ.js +0 -29
- package/dist/chunk-OTLSUYQQ.js.map +0 -1
- package/dist/chunk-Q75LKHDF.js +0 -56
- package/dist/chunk-Q75LKHDF.js.map +0 -1
- package/dist/chunk-QDCJLJUV.js.map +0 -1
- package/dist/chunk-RDHSHEZM.js +0 -479
- package/dist/chunk-RDHSHEZM.js.map +0 -1
- package/dist/chunk-UIG7MWS2.js +0 -518
- package/dist/chunk-UIG7MWS2.js.map +0 -1
- package/dist/chunk-UL6Q5HDP.js.map +0 -1
- package/dist/chunk-VMQHNJJI.js.map +0 -1
- package/dist/chunk-WWVD5DR5.js.map +0 -1
- package/dist/chunk-Z72F2ENM.js.map +0 -1
- package/dist/chunk-ZRQWSURL.js.map +0 -1
- package/dist/components-BWG1GkZA.d.ts +0 -20
- package/dist/use-behavior-B85d5u7-.d.ts +0 -13
- /package/dist/{chunk-5SM5SH5O.js.map → chunk-7JLWZXPA.js.map} +0 -0
- /package/dist/{chunk-ZA43LPRM.js.map → chunk-KIYUUSPK.js.map} +0 -0
- /package/dist/{chunk-XP3ASVGJ.js.map → chunk-NW4SE5XW.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/behaviors/screen-wrap/screen-wrap-fsm.ts","../src/lib/behaviors/screen-wrap/screen-wrap.descriptor.ts"],"sourcesContent":["/**\n * ScreenWrapFSM\n * \n * State machine for screen wrap behavior.\n * Reports position relative to bounds edges.\n */\n\nimport { SyncStateMachine, t } from '../../core/utility/sync-state-machine';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// FSM State Model\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport enum ScreenWrapState {\n\tCenter = 'center',\n\tNearEdgeLeft = 'near-edge-left',\n\tNearEdgeRight = 'near-edge-right',\n\tNearEdgeTop = 'near-edge-top',\n\tNearEdgeBottom = 'near-edge-bottom',\n\tWrapped = 'wrapped',\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// FSM Events\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport enum ScreenWrapEvent {\n\tEnterCenter = 'enter-center',\n\tApproachLeft = 'approach-left',\n\tApproachRight = 'approach-right',\n\tApproachTop = 'approach-top',\n\tApproachBottom = 'approach-bottom',\n\tWrap = 'wrap',\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ScreenWrapFSM\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport class ScreenWrapFSM {\n\tmachine: SyncStateMachine<ScreenWrapState, ScreenWrapEvent, never>;\n\n\tconstructor() {\n\t\tthis.machine = new SyncStateMachine<ScreenWrapState, ScreenWrapEvent, never>(\n\t\t\tScreenWrapState.Center,\n\t\t\t[\n\t\t\t\t// From Center\n\t\t\t\tt(ScreenWrapState.Center, ScreenWrapEvent.ApproachLeft, ScreenWrapState.NearEdgeLeft),\n\t\t\t\tt(ScreenWrapState.Center, ScreenWrapEvent.ApproachRight, ScreenWrapState.NearEdgeRight),\n\t\t\t\tt(ScreenWrapState.Center, ScreenWrapEvent.ApproachTop, ScreenWrapState.NearEdgeTop),\n\t\t\t\tt(ScreenWrapState.Center, ScreenWrapEvent.ApproachBottom, ScreenWrapState.NearEdgeBottom),\n\t\t\t\t\n\t\t\t\t// From NearEdge to Wrapped\n\t\t\t\tt(ScreenWrapState.NearEdgeLeft, ScreenWrapEvent.Wrap, ScreenWrapState.Wrapped),\n\t\t\t\tt(ScreenWrapState.NearEdgeRight, ScreenWrapEvent.Wrap, ScreenWrapState.Wrapped),\n\t\t\t\tt(ScreenWrapState.NearEdgeTop, ScreenWrapEvent.Wrap, ScreenWrapState.Wrapped),\n\t\t\t\tt(ScreenWrapState.NearEdgeBottom, ScreenWrapEvent.Wrap, ScreenWrapState.Wrapped),\n\n\t\t\t\t// From NearEdge back to Center\n\t\t\t\tt(ScreenWrapState.NearEdgeLeft, ScreenWrapEvent.EnterCenter, ScreenWrapState.Center),\n\t\t\t\tt(ScreenWrapState.NearEdgeRight, ScreenWrapEvent.EnterCenter, ScreenWrapState.Center),\n\t\t\t\tt(ScreenWrapState.NearEdgeTop, ScreenWrapEvent.EnterCenter, ScreenWrapState.Center),\n\t\t\t\tt(ScreenWrapState.NearEdgeBottom, ScreenWrapEvent.EnterCenter, ScreenWrapState.Center),\n\n\t\t\t\t// From Wrapped back to Center\n\t\t\t\tt(ScreenWrapState.Wrapped, ScreenWrapEvent.EnterCenter, ScreenWrapState.Center),\n\n\t\t\t\t// From Wrapped to NearEdge (landed near opposite edge)\n\t\t\t\tt(ScreenWrapState.Wrapped, ScreenWrapEvent.ApproachLeft, ScreenWrapState.NearEdgeLeft),\n\t\t\t\tt(ScreenWrapState.Wrapped, ScreenWrapEvent.ApproachRight, ScreenWrapState.NearEdgeRight),\n\t\t\t\tt(ScreenWrapState.Wrapped, ScreenWrapEvent.ApproachTop, ScreenWrapState.NearEdgeTop),\n\t\t\t\tt(ScreenWrapState.Wrapped, ScreenWrapEvent.ApproachBottom, ScreenWrapState.NearEdgeBottom),\n\n\t\t\t\t// Self-transitions (no-ops for redundant events)\n\t\t\t\tt(ScreenWrapState.Center, ScreenWrapEvent.EnterCenter, ScreenWrapState.Center),\n\t\t\t\tt(ScreenWrapState.NearEdgeLeft, ScreenWrapEvent.ApproachLeft, ScreenWrapState.NearEdgeLeft),\n\t\t\t\tt(ScreenWrapState.NearEdgeRight, ScreenWrapEvent.ApproachRight, ScreenWrapState.NearEdgeRight),\n\t\t\t\tt(ScreenWrapState.NearEdgeTop, ScreenWrapEvent.ApproachTop, ScreenWrapState.NearEdgeTop),\n\t\t\t\tt(ScreenWrapState.NearEdgeBottom, ScreenWrapEvent.ApproachBottom, ScreenWrapState.NearEdgeBottom),\n\t\t\t]\n\t\t);\n\t}\n\n\tgetState(): ScreenWrapState {\n\t\treturn this.machine.getState();\n\t}\n\n\tdispatch(event: ScreenWrapEvent): void {\n\t\tif (this.machine.can(event)) {\n\t\t\tthis.machine.syncDispatch(event);\n\t\t}\n\t}\n\n\t/**\n\t * Update FSM based on entity position relative to bounds\n\t */\n\tupdate(position: { x: number; y: number }, bounds: {\n\t\tminX: number; maxX: number; minY: number; maxY: number;\n\t\tedgeThreshold: number;\n\t}, wrapped: boolean): void {\n\t\tconst { x, y } = position;\n\t\tconst { minX, maxX, minY, maxY, edgeThreshold } = bounds;\n\n\t\tif (wrapped) {\n\t\t\tthis.dispatch(ScreenWrapEvent.Wrap);\n\t\t\treturn;\n\t\t}\n\n\t\t// Check if near edges\n\t\tconst nearLeft = x < minX + edgeThreshold;\n\t\tconst nearRight = x > maxX - edgeThreshold;\n\t\tconst nearBottom = y < minY + edgeThreshold;\n\t\tconst nearTop = y > maxY - edgeThreshold;\n\n\t\tif (nearLeft) {\n\t\t\tthis.dispatch(ScreenWrapEvent.ApproachLeft);\n\t\t} else if (nearRight) {\n\t\t\tthis.dispatch(ScreenWrapEvent.ApproachRight);\n\t\t} else if (nearTop) {\n\t\t\tthis.dispatch(ScreenWrapEvent.ApproachTop);\n\t\t} else if (nearBottom) {\n\t\t\tthis.dispatch(ScreenWrapEvent.ApproachBottom);\n\t\t} else {\n\t\t\tthis.dispatch(ScreenWrapEvent.EnterCenter);\n\t\t}\n\t}\n}\n","/**\n * ScreenWrapBehavior\n * \n * When an entity exits the defined 2D bounds, it wraps around to the opposite edge.\n * Asteroids-style screen wrapping with FSM for edge detection.\n */\n\nimport { defineBehavior } from '../behavior-descriptor';\nimport type { BehaviorEntityLink, BehaviorSystem } from '../behavior-system';\nimport { ScreenWrapFSM } from './screen-wrap-fsm';\nimport { createBounds2DRect } from '../shared/bounds-2d';\nimport type { BehaviorRuntime } from '../../runtime/wasm-stage-runtime';\n\n/**\n * Screen wrap options (typed for entity.use() autocomplete)\n */\nexport interface ScreenWrapOptions {\n\t/** Width of the wrapping area (default: 20) */\n\twidth: number;\n\t/** Height of the wrapping area (default: 15) */\n\theight: number;\n\t/** Center X position (default: 0) */\n\tcenterX: number;\n\t/** Center Y position (default: 0) */\n\tcenterY: number;\n\t/** Distance from edge to trigger NearEdge state (default: 2) */\n\tedgeThreshold: number;\n}\n\nconst defaultOptions: ScreenWrapOptions = {\n\twidth: 20,\n\theight: 15,\n\tcenterX: 0,\n\tcenterY: 0,\n\tedgeThreshold: 2,\n};\n\nconst SCREEN_WRAP_BEHAVIOR_KEY = Symbol.for('zylem:behavior:screen-wrap');\n\n/**\n * ScreenWrapSystem - Wraps entities around 2D bounds\n *\n * The Rust `screen_wrap` Stage behavior owns the wrap; this system attaches\n * it and mirrors the queried state into the local FSM so handles can observe\n * edge/wrap states.\n */\nclass ScreenWrapSystem implements BehaviorSystem {\n\tprivate attachedRuntimeSlots = new Set<number>();\n\n\tconstructor(\n\t\tprivate wasmStage: BehaviorRuntime | null,\n\t\tprivate getBehaviorLinks?: (key: symbol) => Iterable<BehaviorEntityLink>,\n\t) {}\n\n\tupdate(_ecs: unknown, _delta: number): void {\n\t\tconst links = this.getBehaviorLinks?.(SCREEN_WRAP_BEHAVIOR_KEY);\n\t\tif (!links) return;\n\n\t\tfor (const link of links) {\n\t\t\tconst gameEntity = link.entity as any;\n\t\t\tconst wrapRef = link.ref as any;\n\t\t\tconst options = wrapRef.options as ScreenWrapOptions;\n\n\t\t\tif (!wrapRef.fsm) {\n\t\t\t\twrapRef.fsm = new ScreenWrapFSM();\n\t\t\t}\n\n\t\t\tconst handle = (gameEntity.runtimeHandle ?? -1) as number;\n\t\t\tif (!this.wasmStage || handle < 0) continue;\n\n\t\t\tthis.ensureWasmAttached(handle, options);\n\t\t\tconst pose = this.wasmStage.getPose(handle);\n\t\t\tconst query = this.wasmStage.queryScreenWrap(handle);\n\t\t\tif (pose && query) {\n\t\t\t\tconst bounds = createBounds2DRect(options);\n\t\t\t\twrapRef.fsm.update(\n\t\t\t\t\t{ x: pose.position[0], y: pose.position[1] },\n\t\t\t\t\t{\n\t\t\t\t\t\tminX: bounds.minX,\n\t\t\t\t\t\tmaxX: bounds.maxX,\n\t\t\t\t\t\tminY: bounds.minY,\n\t\t\t\t\t\tmaxY: bounds.maxY,\n\t\t\t\t\t\tedgeThreshold: options.edgeThreshold,\n\t\t\t\t\t},\n\t\t\t\t\tquery.lastWrappedAxes !== 0,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate ensureWasmAttached(handle: number, options: ScreenWrapOptions): void {\n\t\tif (!this.wasmStage || this.attachedRuntimeSlots.has(handle)) return;\n\t\tthis.wasmStage.attachScreenWrap(handle, {\n\t\t\twidth: options.width,\n\t\t\theight: options.height,\n\t\t\tcenterX: options.centerX,\n\t\t\tcenterY: options.centerY,\n\t\t\tedgeThreshold: options.edgeThreshold,\n\t\t});\n\t\tthis.attachedRuntimeSlots.add(handle);\n\t}\n\n\tdestroy(_ecs: unknown): void {\n\t\tthis.attachedRuntimeSlots.clear();\n\t}\n}\n\n/**\n * ScreenWrapBehavior - Wraps entities around 2D bounds\n * \n * @example\n * ```typescript\n * import { ScreenWrapBehavior } from \"@zylem/game-lib/behavior\";\n * \n * const ship = createSprite({ ... });\n * const wrapRef = ship.use(ScreenWrapBehavior, { width: 20, height: 15 });\n * \n * // Access FSM to observe edge state\n * const fsm = wrapRef.getFSM();\n * console.log(fsm?.getState()); // 'center', 'near-edge-left', 'wrapped', etc.\n * ```\n */\nexport const ScreenWrapBehavior = defineBehavior({\n\tname: 'screen-wrap',\n\tdefaultOptions,\n\tsystemFactory: (ctx) =>\n\t\tnew ScreenWrapSystem(ctx.wasmStage ?? null, ctx.getBehaviorLinks),\n});\n"],"mappings":";;;;;;;;;;;;AAaO,IAAK,kBAAL,kBAAKA,qBAAL;AACN,EAAAA,iBAAA,YAAS;AACT,EAAAA,iBAAA,kBAAe;AACf,EAAAA,iBAAA,mBAAgB;AAChB,EAAAA,iBAAA,iBAAc;AACd,EAAAA,iBAAA,oBAAiB;AACjB,EAAAA,iBAAA,aAAU;AANC,SAAAA;AAAA,GAAA;AAaL,IAAK,kBAAL,kBAAKC,qBAAL;AACN,EAAAA,iBAAA,iBAAc;AACd,EAAAA,iBAAA,kBAAe;AACf,EAAAA,iBAAA,mBAAgB;AAChB,EAAAA,iBAAA,iBAAc;AACd,EAAAA,iBAAA,oBAAiB;AACjB,EAAAA,iBAAA,UAAO;AANI,SAAAA;AAAA,GAAA;AAaL,IAAM,gBAAN,MAAoB;AAAA,EAC1B;AAAA,EAEA,cAAc;AACb,SAAK,UAAU,IAAI;AAAA,MAClB;AAAA,MACA;AAAA;AAAA,YAEC,yBAAE,uBAAwB,oCAA8B,mCAA4B;AAAA,YACpF,yBAAE,uBAAwB,sCAA+B,qCAA6B;AAAA,YACtF,yBAAE,uBAAwB,kCAA6B,iCAA2B;AAAA,YAClF,yBAAE,uBAAwB,wCAAgC,uCAA8B;AAAA;AAAA,YAGxF,yBAAE,qCAA8B,mBAAsB,uBAAuB;AAAA,YAC7E,yBAAE,uCAA+B,mBAAsB,uBAAuB;AAAA,YAC9E,yBAAE,mCAA6B,mBAAsB,uBAAuB;AAAA,YAC5E,yBAAE,yCAAgC,mBAAsB,uBAAuB;AAAA;AAAA,YAG/E,yBAAE,qCAA8B,kCAA6B,qBAAsB;AAAA,YACnF,yBAAE,uCAA+B,kCAA6B,qBAAsB;AAAA,YACpF,yBAAE,mCAA6B,kCAA6B,qBAAsB;AAAA,YAClF,yBAAE,yCAAgC,kCAA6B,qBAAsB;AAAA;AAAA,YAGrF,yBAAE,yBAAyB,kCAA6B,qBAAsB;AAAA;AAAA,YAG9E,yBAAE,yBAAyB,oCAA8B,mCAA4B;AAAA,YACrF,yBAAE,yBAAyB,sCAA+B,qCAA6B;AAAA,YACvF,yBAAE,yBAAyB,kCAA6B,iCAA2B;AAAA,YACnF,yBAAE,yBAAyB,wCAAgC,uCAA8B;AAAA;AAAA,YAGzF,yBAAE,uBAAwB,kCAA6B,qBAAsB;AAAA,YAC7E,yBAAE,qCAA8B,oCAA8B,mCAA4B;AAAA,YAC1F,yBAAE,uCAA+B,sCAA+B,qCAA6B;AAAA,YAC7F,yBAAE,mCAA6B,kCAA6B,iCAA2B;AAAA,YACvF,yBAAE,yCAAgC,wCAAgC,uCAA8B;AAAA,MACjG;AAAA,IACD;AAAA,EACD;AAAA,EAEA,WAA4B;AAC3B,WAAO,KAAK,QAAQ,SAAS;AAAA,EAC9B;AAAA,EAEA,SAAS,OAA8B;AACtC,QAAI,KAAK,QAAQ,IAAI,KAAK,GAAG;AAC5B,WAAK,QAAQ,aAAa,KAAK;AAAA,IAChC;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,UAAoC,QAGxC,SAAwB;AAC1B,UAAM,EAAE,GAAG,EAAE,IAAI;AACjB,UAAM,EAAE,MAAM,MAAM,MAAM,MAAM,cAAc,IAAI;AAElD,QAAI,SAAS;AACZ,WAAK,SAAS,iBAAoB;AAClC;AAAA,IACD;AAGA,UAAM,WAAW,IAAI,OAAO;AAC5B,UAAM,YAAY,IAAI,OAAO;AAC7B,UAAM,aAAa,IAAI,OAAO;AAC9B,UAAM,UAAU,IAAI,OAAO;AAE3B,QAAI,UAAU;AACb,WAAK,SAAS,kCAA4B;AAAA,IAC3C,WAAW,WAAW;AACrB,WAAK,SAAS,oCAA6B;AAAA,IAC5C,WAAW,SAAS;AACnB,WAAK,SAAS,gCAA2B;AAAA,IAC1C,WAAW,YAAY;AACtB,WAAK,SAAS,sCAA8B;AAAA,IAC7C,OAAO;AACN,WAAK,SAAS,gCAA2B;AAAA,IAC1C;AAAA,EACD;AACD;;;ACjGA,IAAM,iBAAoC;AAAA,EACzC,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,SAAS;AAAA,EACT,eAAe;AAChB;AAEA,IAAM,2BAA2B,uBAAO,IAAI,4BAA4B;AASxE,IAAM,mBAAN,MAAiD;AAAA,EAGhD,YACS,WACA,kBACP;AAFO;AACA;AAAA,EACN;AAAA,EAFM;AAAA,EACA;AAAA,EAJD,uBAAuB,oBAAI,IAAY;AAAA,EAO/C,OAAO,MAAe,QAAsB;AAC3C,UAAM,QAAQ,KAAK,mBAAmB,wBAAwB;AAC9D,QAAI,CAAC,MAAO;AAEZ,eAAW,QAAQ,OAAO;AACzB,YAAM,aAAa,KAAK;AACxB,YAAM,UAAU,KAAK;AACrB,YAAM,UAAU,QAAQ;AAExB,UAAI,CAAC,QAAQ,KAAK;AACjB,gBAAQ,MAAM,IAAI,cAAc;AAAA,MACjC;AAEA,YAAM,SAAU,WAAW,iBAAiB;AAC5C,UAAI,CAAC,KAAK,aAAa,SAAS,EAAG;AAEnC,WAAK,mBAAmB,QAAQ,OAAO;AACvC,YAAM,OAAO,KAAK,UAAU,QAAQ,MAAM;AAC1C,YAAM,QAAQ,KAAK,UAAU,gBAAgB,MAAM;AACnD,UAAI,QAAQ,OAAO;AAClB,cAAM,SAAS,mBAAmB,OAAO;AACzC,gBAAQ,IAAI;AAAA,UACX,EAAE,GAAG,KAAK,SAAS,CAAC,GAAG,GAAG,KAAK,SAAS,CAAC,EAAE;AAAA,UAC3C;AAAA,YACC,MAAM,OAAO;AAAA,YACb,MAAM,OAAO;AAAA,YACb,MAAM,OAAO;AAAA,YACb,MAAM,OAAO;AAAA,YACb,eAAe,QAAQ;AAAA,UACxB;AAAA,UACA,MAAM,oBAAoB;AAAA,QAC3B;AAAA,MACD;AAAA,IACD;AAAA,EACD;AAAA,EAEQ,mBAAmB,QAAgB,SAAkC;AAC5E,QAAI,CAAC,KAAK,aAAa,KAAK,qBAAqB,IAAI,MAAM,EAAG;AAC9D,SAAK,UAAU,iBAAiB,QAAQ;AAAA,MACvC,OAAO,QAAQ;AAAA,MACf,QAAQ,QAAQ;AAAA,MAChB,SAAS,QAAQ;AAAA,MACjB,SAAS,QAAQ;AAAA,MACjB,eAAe,QAAQ;AAAA,IACxB,CAAC;AACD,SAAK,qBAAqB,IAAI,MAAM;AAAA,EACrC;AAAA,EAEA,QAAQ,MAAqB;AAC5B,SAAK,qBAAqB,MAAM;AAAA,EACjC;AACD;AAiBO,IAAM,qBAAqB,eAAe;AAAA,EAChD,MAAM;AAAA,EACN;AAAA,EACA,eAAe,CAAC,QACf,IAAI,iBAAiB,IAAI,aAAa,MAAM,IAAI,gBAAgB;AAClE,CAAC;","names":["ScreenWrapState","ScreenWrapEvent"]}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SyncStateMachine,
|
|
3
|
+
import_typescript_fsm
|
|
4
|
+
} from "./chunk-AS3HR7AS.js";
|
|
5
|
+
import {
|
|
6
|
+
STAGE_INPUT_BUTTON_JUMP,
|
|
7
|
+
readBehaviorSnapshot
|
|
8
|
+
} from "./chunk-DZ4LLQON.js";
|
|
9
|
+
import {
|
|
10
|
+
defineBehavior
|
|
11
|
+
} from "./chunk-EZNCCO4L.js";
|
|
12
|
+
|
|
13
|
+
// src/lib/behaviors/jumper-2d/components.ts
|
|
14
|
+
function createJumpConfig2D(options = {}) {
|
|
15
|
+
return {
|
|
16
|
+
jumpHeight: options.jumpHeight ?? 2.5,
|
|
17
|
+
gravity: options.gravity ?? 20,
|
|
18
|
+
maxFallSpeed: options.maxFallSpeed,
|
|
19
|
+
maxJumps: options.maxJumps ?? 1,
|
|
20
|
+
resetJumpsOnGround: options.resetJumpsOnGround ?? true,
|
|
21
|
+
coyoteTimeMs: options.coyoteTimeMs ?? 100,
|
|
22
|
+
jumpBufferMs: options.jumpBufferMs ?? 80,
|
|
23
|
+
minTimeBetweenJumpsMs: options.minTimeBetweenJumpsMs,
|
|
24
|
+
variableJump: options.variableJump
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function createJumpInput2D() {
|
|
28
|
+
return {
|
|
29
|
+
jumpPressed: false,
|
|
30
|
+
jumpHeld: false,
|
|
31
|
+
jumpReleased: false,
|
|
32
|
+
fastFall: false
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function createJumpState2D() {
|
|
36
|
+
return {
|
|
37
|
+
jumpsUsed: 0,
|
|
38
|
+
lastJumpTimeMs: 0,
|
|
39
|
+
bufferedJumpMs: 0,
|
|
40
|
+
coyoteMs: 0,
|
|
41
|
+
isJumping: false,
|
|
42
|
+
jumpHoldMs: 0,
|
|
43
|
+
jumpCutApplied: false
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// src/lib/behaviors/jumper-2d/jumper-2d-fsm.ts
|
|
48
|
+
var Jumper2DState = /* @__PURE__ */ ((Jumper2DState2) => {
|
|
49
|
+
Jumper2DState2["Grounded"] = "grounded";
|
|
50
|
+
Jumper2DState2["Jumping"] = "jumping";
|
|
51
|
+
Jumper2DState2["Falling"] = "falling";
|
|
52
|
+
return Jumper2DState2;
|
|
53
|
+
})(Jumper2DState || {});
|
|
54
|
+
var Jumper2DEvent = /* @__PURE__ */ ((Jumper2DEvent2) => {
|
|
55
|
+
Jumper2DEvent2["Jump"] = "jump";
|
|
56
|
+
Jumper2DEvent2["Fall"] = "fall";
|
|
57
|
+
Jumper2DEvent2["Land"] = "land";
|
|
58
|
+
return Jumper2DEvent2;
|
|
59
|
+
})(Jumper2DEvent || {});
|
|
60
|
+
var Jumper2DFSM = class {
|
|
61
|
+
constructor(ctx) {
|
|
62
|
+
this.ctx = ctx;
|
|
63
|
+
this.machine = new SyncStateMachine(
|
|
64
|
+
"grounded" /* Grounded */,
|
|
65
|
+
[
|
|
66
|
+
(0, import_typescript_fsm.t)("grounded" /* Grounded */, "jump" /* Jump */, "jumping" /* Jumping */),
|
|
67
|
+
(0, import_typescript_fsm.t)("grounded" /* Grounded */, "fall" /* Fall */, "falling" /* Falling */),
|
|
68
|
+
(0, import_typescript_fsm.t)("grounded" /* Grounded */, "land" /* Land */, "grounded" /* Grounded */),
|
|
69
|
+
(0, import_typescript_fsm.t)("jumping" /* Jumping */, "fall" /* Fall */, "falling" /* Falling */),
|
|
70
|
+
(0, import_typescript_fsm.t)("jumping" /* Jumping */, "land" /* Land */, "grounded" /* Grounded */),
|
|
71
|
+
(0, import_typescript_fsm.t)("jumping" /* Jumping */, "jump" /* Jump */, "jumping" /* Jumping */),
|
|
72
|
+
(0, import_typescript_fsm.t)("falling" /* Falling */, "land" /* Land */, "grounded" /* Grounded */),
|
|
73
|
+
(0, import_typescript_fsm.t)("falling" /* Falling */, "jump" /* Jump */, "jumping" /* Jumping */),
|
|
74
|
+
(0, import_typescript_fsm.t)("falling" /* Falling */, "fall" /* Fall */, "falling" /* Falling */)
|
|
75
|
+
]
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
ctx;
|
|
79
|
+
machine;
|
|
80
|
+
getState() {
|
|
81
|
+
return this.machine.getState();
|
|
82
|
+
}
|
|
83
|
+
dispatch(event) {
|
|
84
|
+
if (this.machine.can(event)) {
|
|
85
|
+
this.machine.syncDispatch(event);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
isJumping() {
|
|
89
|
+
return this.getState() === "jumping" /* Jumping */;
|
|
90
|
+
}
|
|
91
|
+
isGrounded() {
|
|
92
|
+
return this.getState() === "grounded" /* Grounded */;
|
|
93
|
+
}
|
|
94
|
+
getJumpsUsed() {
|
|
95
|
+
return this.ctx.state.jumpsUsed;
|
|
96
|
+
}
|
|
97
|
+
applyTickEvent(tickEvent, isGrounded) {
|
|
98
|
+
switch (tickEvent) {
|
|
99
|
+
case "jump":
|
|
100
|
+
this.dispatch("jump" /* Jump */);
|
|
101
|
+
break;
|
|
102
|
+
case "fall":
|
|
103
|
+
this.dispatch("fall" /* Fall */);
|
|
104
|
+
break;
|
|
105
|
+
case "land":
|
|
106
|
+
this.dispatch("land" /* Land */);
|
|
107
|
+
break;
|
|
108
|
+
default:
|
|
109
|
+
if (!isGrounded && this.getState() === "grounded" /* Grounded */) {
|
|
110
|
+
this.dispatch("fall" /* Fall */);
|
|
111
|
+
}
|
|
112
|
+
if (isGrounded && this.getState() !== "grounded" /* Grounded */) {
|
|
113
|
+
this.dispatch("land" /* Land */);
|
|
114
|
+
}
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// src/lib/behaviors/jumper-2d/jumper-2d.descriptor.ts
|
|
121
|
+
var defaultOptions = {
|
|
122
|
+
jumpHeight: 2.5,
|
|
123
|
+
gravity: 20,
|
|
124
|
+
maxJumps: 1,
|
|
125
|
+
resetJumpsOnGround: true,
|
|
126
|
+
coyoteTimeMs: 100,
|
|
127
|
+
jumpBufferMs: 80,
|
|
128
|
+
groundRayLength: 1,
|
|
129
|
+
debugGroundProbe: false
|
|
130
|
+
};
|
|
131
|
+
var JUMPER_2D_BEHAVIOR_KEY = /* @__PURE__ */ Symbol.for("zylem:behavior:jumper-2d");
|
|
132
|
+
var Jumper2DBehaviorSystem = class {
|
|
133
|
+
constructor(wasmStage, getBehaviorLinks) {
|
|
134
|
+
this.wasmStage = wasmStage;
|
|
135
|
+
this.getBehaviorLinks = getBehaviorLinks;
|
|
136
|
+
}
|
|
137
|
+
wasmStage;
|
|
138
|
+
getBehaviorLinks;
|
|
139
|
+
attachedRuntimeSlots = /* @__PURE__ */ new Set();
|
|
140
|
+
update(_ecs, _delta) {
|
|
141
|
+
const links = this.getBehaviorLinks?.(JUMPER_2D_BEHAVIOR_KEY);
|
|
142
|
+
if (!links) return;
|
|
143
|
+
for (const link of links) {
|
|
144
|
+
const gameEntity = link.entity;
|
|
145
|
+
const jumperRef = link.ref;
|
|
146
|
+
const options = jumperRef.options;
|
|
147
|
+
const handle = gameEntity.runtimeHandle ?? -1;
|
|
148
|
+
if (!this.wasmStage || handle < 0) continue;
|
|
149
|
+
if (!gameEntity.$jumper2d) {
|
|
150
|
+
gameEntity.$jumper2d = createJumpInput2D();
|
|
151
|
+
}
|
|
152
|
+
this.ensureWasmAttached(handle, options);
|
|
153
|
+
jumperRef.__wasmStage = this.wasmStage;
|
|
154
|
+
jumperRef.__entity = gameEntity;
|
|
155
|
+
const input = gameEntity.$jumper2d;
|
|
156
|
+
const jumpPressed = !!(input.jumpPressed || input.jumpHeld);
|
|
157
|
+
this.wasmStage.writeInputButton(handle, STAGE_INPUT_BUTTON_JUMP, jumpPressed);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
ensureWasmAttached(handle, options) {
|
|
161
|
+
if (!this.wasmStage || this.attachedRuntimeSlots.has(handle)) return;
|
|
162
|
+
const jumpForce = Math.sqrt(2 * (options.gravity ?? 20) * (options.jumpHeight ?? 2.5));
|
|
163
|
+
this.wasmStage.attachJumper2D(handle, {
|
|
164
|
+
jumpForce,
|
|
165
|
+
maxJumps: options.maxJumps ?? 1,
|
|
166
|
+
jumpBufferTime: (options.jumpBufferMs ?? 80) / 1e3,
|
|
167
|
+
coyoteTime: (options.coyoteTimeMs ?? 100) / 1e3,
|
|
168
|
+
gravity: options.gravity ?? 20
|
|
169
|
+
});
|
|
170
|
+
this.attachedRuntimeSlots.add(handle);
|
|
171
|
+
}
|
|
172
|
+
destroy(_ecs) {
|
|
173
|
+
this.attachedRuntimeSlots.clear();
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
var Jumper2DBehavior = defineBehavior({
|
|
177
|
+
name: "jumper-2d",
|
|
178
|
+
defaultOptions,
|
|
179
|
+
systemFactory: (ctx) => new Jumper2DBehaviorSystem(ctx.wasmStage ?? null, ctx.getBehaviorLinks),
|
|
180
|
+
// Handles read the runtime snapshot buffer (previous-step values). The
|
|
181
|
+
// Rust behavior is the single source of truth.
|
|
182
|
+
createHandle: (ref) => {
|
|
183
|
+
const jumpsUsed = () => readBehaviorSnapshot(ref)?.jumpCount ?? 0;
|
|
184
|
+
return {
|
|
185
|
+
getState: () => {
|
|
186
|
+
const snapshot = readBehaviorSnapshot(ref);
|
|
187
|
+
if (!snapshot) return "grounded" /* Grounded */;
|
|
188
|
+
if (snapshot.grounded) return "grounded" /* Grounded */;
|
|
189
|
+
return snapshot.verticalVelocity > 0 ? "jumping" /* Jumping */ : "falling" /* Falling */;
|
|
190
|
+
},
|
|
191
|
+
isJumping: () => {
|
|
192
|
+
const snapshot = readBehaviorSnapshot(ref);
|
|
193
|
+
return snapshot ? !snapshot.grounded && snapshot.verticalVelocity > 0 : false;
|
|
194
|
+
},
|
|
195
|
+
getJumpsUsed: jumpsUsed,
|
|
196
|
+
getJumpsRemaining: () => (ref.options.maxJumps ?? 1) - jumpsUsed()
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
export {
|
|
202
|
+
createJumpConfig2D,
|
|
203
|
+
createJumpInput2D,
|
|
204
|
+
createJumpState2D,
|
|
205
|
+
Jumper2DState,
|
|
206
|
+
Jumper2DEvent,
|
|
207
|
+
Jumper2DFSM,
|
|
208
|
+
Jumper2DBehavior
|
|
209
|
+
};
|
|
210
|
+
//# sourceMappingURL=chunk-4CEW64HO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/behaviors/jumper-2d/components.ts","../src/lib/behaviors/jumper-2d/jumper-2d-fsm.ts","../src/lib/behaviors/jumper-2d/jumper-2d.descriptor.ts"],"sourcesContent":["export interface JumpConfig2D {\n\tjumpHeight: number;\n\tgravity: number;\n\tmaxFallSpeed?: number;\n\tmaxJumps: number;\n\tresetJumpsOnGround: boolean;\n\tcoyoteTimeMs: number;\n\tjumpBufferMs: number;\n\tminTimeBetweenJumpsMs?: number;\n\tvariableJump?: {\n\t\tenabled: boolean;\n\t\tcutGravityMultiplier: number;\n\t\tmaxHoldMs?: number;\n\t};\n}\n\nexport function createJumpConfig2D(\n\toptions: Partial<JumpConfig2D> = {},\n): JumpConfig2D {\n\treturn {\n\t\tjumpHeight: options.jumpHeight ?? 2.5,\n\t\tgravity: options.gravity ?? 20,\n\t\tmaxFallSpeed: options.maxFallSpeed,\n\t\tmaxJumps: options.maxJumps ?? 1,\n\t\tresetJumpsOnGround: options.resetJumpsOnGround ?? true,\n\t\tcoyoteTimeMs: options.coyoteTimeMs ?? 100,\n\t\tjumpBufferMs: options.jumpBufferMs ?? 80,\n\t\tminTimeBetweenJumpsMs: options.minTimeBetweenJumpsMs,\n\t\tvariableJump: options.variableJump,\n\t};\n}\n\nexport interface JumpInput2D {\n\tjumpPressed: boolean;\n\tjumpHeld: boolean;\n\tjumpReleased: boolean;\n\tfastFall?: boolean;\n}\n\nexport function createJumpInput2D(): JumpInput2D {\n\treturn {\n\t\tjumpPressed: false,\n\t\tjumpHeld: false,\n\t\tjumpReleased: false,\n\t\tfastFall: false,\n\t};\n}\n\nexport interface JumpContext2D {\n\tdt: number;\n\tvelocityY: number;\n\tisGrounded: boolean;\n\ttimeSinceGroundedMs: number;\n\tsetVerticalVelocity(y: number): void;\n}\n\nexport interface JumpState2D {\n\tjumpsUsed: number;\n\tlastJumpTimeMs: number;\n\tbufferedJumpMs: number;\n\tcoyoteMs: number;\n\tisJumping: boolean;\n\tjumpHoldMs: number;\n\tjumpCutApplied: boolean;\n}\n\nexport function createJumpState2D(): JumpState2D {\n\treturn {\n\t\tjumpsUsed: 0,\n\t\tlastJumpTimeMs: 0,\n\t\tbufferedJumpMs: 0,\n\t\tcoyoteMs: 0,\n\t\tisJumping: false,\n\t\tjumpHoldMs: 0,\n\t\tjumpCutApplied: false,\n\t};\n}\n","import { SyncStateMachine, t } from '../../core/utility/sync-state-machine';\nimport type { JumpState2D } from './components';\n\nexport enum Jumper2DState {\n\tGrounded = 'grounded',\n\tJumping = 'jumping',\n\tFalling = 'falling',\n}\n\nexport enum Jumper2DEvent {\n\tJump = 'jump',\n\tFall = 'fall',\n\tLand = 'land',\n}\n\nexport interface Jumper2DFSMContext {\n\tstate: JumpState2D;\n}\n\nexport class Jumper2DFSM {\n\tmachine: SyncStateMachine<Jumper2DState, Jumper2DEvent, never>;\n\n\tconstructor(private ctx: Jumper2DFSMContext) {\n\t\tthis.machine = new SyncStateMachine<Jumper2DState, Jumper2DEvent, never>(\n\t\t\tJumper2DState.Grounded,\n\t\t\t[\n\t\t\t\tt(Jumper2DState.Grounded, Jumper2DEvent.Jump, Jumper2DState.Jumping),\n\t\t\t\tt(Jumper2DState.Grounded, Jumper2DEvent.Fall, Jumper2DState.Falling),\n\t\t\t\tt(Jumper2DState.Grounded, Jumper2DEvent.Land, Jumper2DState.Grounded),\n\t\t\t\tt(Jumper2DState.Jumping, Jumper2DEvent.Fall, Jumper2DState.Falling),\n\t\t\t\tt(Jumper2DState.Jumping, Jumper2DEvent.Land, Jumper2DState.Grounded),\n\t\t\t\tt(Jumper2DState.Jumping, Jumper2DEvent.Jump, Jumper2DState.Jumping),\n\t\t\t\tt(Jumper2DState.Falling, Jumper2DEvent.Land, Jumper2DState.Grounded),\n\t\t\t\tt(Jumper2DState.Falling, Jumper2DEvent.Jump, Jumper2DState.Jumping),\n\t\t\t\tt(Jumper2DState.Falling, Jumper2DEvent.Fall, Jumper2DState.Falling),\n\t\t\t],\n\t\t);\n\t}\n\n\tgetState(): Jumper2DState {\n\t\treturn this.machine.getState();\n\t}\n\n\tdispatch(event: Jumper2DEvent): void {\n\t\tif (this.machine.can(event)) {\n\t\t\tthis.machine.syncDispatch(event);\n\t\t}\n\t}\n\n\tisJumping(): boolean {\n\t\treturn this.getState() === Jumper2DState.Jumping;\n\t}\n\n\tisGrounded(): boolean {\n\t\treturn this.getState() === Jumper2DState.Grounded;\n\t}\n\n\tgetJumpsUsed(): number {\n\t\treturn this.ctx.state.jumpsUsed;\n\t}\n\n\tapplyTickEvent(tickEvent: string, isGrounded: boolean): void {\n\t\tswitch (tickEvent) {\n\t\t\tcase 'jump':\n\t\t\t\tthis.dispatch(Jumper2DEvent.Jump);\n\t\t\t\tbreak;\n\t\t\tcase 'fall':\n\t\t\t\tthis.dispatch(Jumper2DEvent.Fall);\n\t\t\t\tbreak;\n\t\t\tcase 'land':\n\t\t\t\tthis.dispatch(Jumper2DEvent.Land);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tif (!isGrounded && this.getState() === Jumper2DState.Grounded) {\n\t\t\t\t\tthis.dispatch(Jumper2DEvent.Fall);\n\t\t\t\t}\n\t\t\t\tif (isGrounded && this.getState() !== Jumper2DState.Grounded) {\n\t\t\t\t\tthis.dispatch(Jumper2DEvent.Land);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t}\n}\n","import { defineBehavior } from '../behavior-descriptor';\nimport type { BehaviorEntityLink, BehaviorSystem } from '../behavior-system';\nimport {\n\tcreateJumpInput2D,\n\ttype JumpConfig2D,\n\ttype JumpInput2D,\n\ttype JumpState2D,\n} from './components';\nimport { Jumper2DState } from './jumper-2d-fsm';\nimport {\n\tSTAGE_INPUT_BUTTON_JUMP,\n\treadBehaviorSnapshot,\n\ttype BehaviorRuntime,\n} from '../../runtime/wasm-stage-runtime';\n\nexport interface Jumper2DBehaviorOptions {\n\tjumpHeight?: number;\n\tgravity?: number;\n\tmaxFallSpeed?: number;\n\tmaxJumps?: number;\n\tresetJumpsOnGround?: boolean;\n\tcoyoteTimeMs?: number;\n\tjumpBufferMs?: number;\n\tminTimeBetweenJumpsMs?: number;\n\tvariableJump?: JumpConfig2D['variableJump'];\n\tgroundRayLength?: number;\n\tsnapToGroundDistance?: number;\n\tdebugGroundProbe?: boolean;\n}\n\nconst defaultOptions: Jumper2DBehaviorOptions = {\n\tjumpHeight: 2.5,\n\tgravity: 20,\n\tmaxJumps: 1,\n\tresetJumpsOnGround: true,\n\tcoyoteTimeMs: 100,\n\tjumpBufferMs: 80,\n\tgroundRayLength: 1,\n\tdebugGroundProbe: false,\n};\n\nconst JUMPER_2D_BEHAVIOR_KEY = Symbol.for('zylem:behavior:jumper-2d');\n\nexport function isJumper2DGrounded(params: {\n\tnearGround?: boolean;\n\tvelocityY: number;\n\tsupportToi?: number | null;\n\tsnapToGroundDistance?: number;\n}): boolean {\n\tif (params.velocityY > 0) {\n\t\treturn false;\n\t}\n\n\tif (params.snapToGroundDistance != null) {\n\t\treturn (\n\t\t\tparams.supportToi != null &&\n\t\t\tparams.supportToi <= params.snapToGroundDistance\n\t\t);\n\t}\n\n\treturn params.nearGround === true;\n}\n\nexport interface Jumper2DEntity {\n\tuuid: string;\n\ttransformStore: import('../../capabilities/transform-store').TransformState;\n\tjumper2d: JumpConfig2D;\n\t$jumper2d: JumpInput2D;\n\tjumper2dState: JumpState2D;\n}\n\n/**\n * Forwards jump input into the wasm runtime. The Rust `jumper_2d` Stage\n * behavior (ground probe + jump core) is the single simulation path; handles\n * read its state back via the snapshot buffer.\n */\nclass Jumper2DBehaviorSystem implements BehaviorSystem {\n\tprivate attachedRuntimeSlots = new Set<number>();\n\n\tconstructor(\n\t\tprivate wasmStage: BehaviorRuntime | null,\n\t\tprivate getBehaviorLinks?: (key: symbol) => Iterable<BehaviorEntityLink>,\n\t) {}\n\n\tupdate(_ecs: unknown, _delta: number): void {\n\t\tconst links = this.getBehaviorLinks?.(JUMPER_2D_BEHAVIOR_KEY);\n\t\tif (!links) return;\n\n\t\tfor (const link of links) {\n\t\t\tconst gameEntity = link.entity as any;\n\t\t\tconst jumperRef = link.ref as any;\n\n\t\t\tconst options = jumperRef.options as Jumper2DBehaviorOptions;\n\t\t\tconst handle = (gameEntity.runtimeHandle ?? -1) as number;\n\t\t\tif (!this.wasmStage || handle < 0) continue;\n\n\t\t\tif (!gameEntity.$jumper2d) {\n\t\t\t\tgameEntity.$jumper2d = createJumpInput2D();\n\t\t\t}\n\n\t\t\tthis.ensureWasmAttached(handle, options);\n\t\t\tjumperRef.__wasmStage = this.wasmStage;\n\t\t\tjumperRef.__entity = gameEntity;\n\t\t\tconst input = gameEntity.$jumper2d as JumpInput2D;\n\t\t\tconst jumpPressed = !!(input.jumpPressed || input.jumpHeld);\n\t\t\tthis.wasmStage.writeInputButton(handle, STAGE_INPUT_BUTTON_JUMP, jumpPressed);\n\t\t}\n\t}\n\n\tprivate ensureWasmAttached(handle: number, options: Jumper2DBehaviorOptions): void {\n\t\tif (!this.wasmStage || this.attachedRuntimeSlots.has(handle)) return;\n\t\tconst jumpForce = Math.sqrt(2 * (options.gravity ?? 20) * (options.jumpHeight ?? 2.5));\n\t\tthis.wasmStage.attachJumper2D(handle, {\n\t\t\tjumpForce,\n\t\t\tmaxJumps: options.maxJumps ?? 1,\n\t\t\tjumpBufferTime: (options.jumpBufferMs ?? 80) / 1000,\n\t\t\tcoyoteTime: (options.coyoteTimeMs ?? 100) / 1000,\n\t\t\tgravity: options.gravity ?? 20,\n\t\t});\n\t\tthis.attachedRuntimeSlots.add(handle);\n\t}\n\n\tdestroy(_ecs: unknown): void {\n\t\tthis.attachedRuntimeSlots.clear();\n\t}\n}\n\nexport const Jumper2DBehavior = defineBehavior<\n\tJumper2DBehaviorOptions,\n\t{\n\t\tgetState: () => Jumper2DState;\n\t\tisJumping: () => boolean;\n\t\tgetJumpsUsed: () => number;\n\t\tgetJumpsRemaining: () => number;\n\t},\n\tJumper2DEntity\n>({\n\tname: 'jumper-2d',\n\tdefaultOptions,\n\tsystemFactory: (ctx) =>\n\t\tnew Jumper2DBehaviorSystem(ctx.wasmStage ?? null, ctx.getBehaviorLinks),\n\t// Handles read the runtime snapshot buffer (previous-step values). The\n\t// Rust behavior is the single source of truth.\n\tcreateHandle: (ref) => {\n\t\tconst jumpsUsed = () => readBehaviorSnapshot(ref)?.jumpCount ?? 0;\n\t\treturn {\n\t\t\tgetState: () => {\n\t\t\t\tconst snapshot = readBehaviorSnapshot(ref);\n\t\t\t\tif (!snapshot) return Jumper2DState.Grounded;\n\t\t\t\tif (snapshot.grounded) return Jumper2DState.Grounded;\n\t\t\t\treturn snapshot.verticalVelocity > 0\n\t\t\t\t\t? Jumper2DState.Jumping\n\t\t\t\t\t: Jumper2DState.Falling;\n\t\t\t},\n\t\t\tisJumping: () => {\n\t\t\t\tconst snapshot = readBehaviorSnapshot(ref);\n\t\t\t\treturn snapshot ? !snapshot.grounded && snapshot.verticalVelocity > 0 : false;\n\t\t\t},\n\t\t\tgetJumpsUsed: jumpsUsed,\n\t\t\tgetJumpsRemaining: () => (ref.options.maxJumps ?? 1) - jumpsUsed(),\n\t\t};\n\t},\n});\n"],"mappings":"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","names":["Jumper2DState","Jumper2DEvent"]}
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SyncStateMachine,
|
|
3
|
+
import_typescript_fsm
|
|
4
|
+
} from "./chunk-AS3HR7AS.js";
|
|
5
|
+
import {
|
|
6
|
+
STAGE_INPUT_BUTTON_JUMP,
|
|
7
|
+
STAGE_INPUT_BUTTON_RUN,
|
|
8
|
+
StagePlatformerShapeSource,
|
|
9
|
+
readBehaviorSnapshot
|
|
10
|
+
} from "./chunk-DZ4LLQON.js";
|
|
11
|
+
import {
|
|
12
|
+
defineBehavior
|
|
13
|
+
} from "./chunk-EZNCCO4L.js";
|
|
14
|
+
|
|
15
|
+
// src/lib/behaviors/platformer-3d/components.ts
|
|
16
|
+
function createPlatformer3DMovementComponent(options = {}) {
|
|
17
|
+
return {
|
|
18
|
+
walkSpeed: options.walkSpeed ?? 12,
|
|
19
|
+
runSpeed: options.runSpeed ?? 24,
|
|
20
|
+
jumpForce: options.jumpForce ?? 12,
|
|
21
|
+
maxJumps: options.maxJumps ?? 1,
|
|
22
|
+
gravity: options.gravity ?? 9.82,
|
|
23
|
+
coyoteTime: options.coyoteTime ?? 0.1,
|
|
24
|
+
jumpBufferTime: options.jumpBufferTime ?? 0.1,
|
|
25
|
+
jumpCutMultiplier: options.jumpCutMultiplier ?? 0.5,
|
|
26
|
+
multiJumpWindowTime: options.multiJumpWindowTime ?? 0.15,
|
|
27
|
+
// 150ms default
|
|
28
|
+
slopeClimbAngle: options.slopeClimbAngle ?? 50,
|
|
29
|
+
slopeSlideAngle: options.slopeSlideAngle ?? 40,
|
|
30
|
+
autostep: options.autostep ?? { maxHeight: 0.3, minWidth: 0.1 },
|
|
31
|
+
snapToGroundDistance: options.snapToGroundDistance ?? 0.3,
|
|
32
|
+
characterOffset: options.characterOffset ?? 0.05
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function createPlatformer3DInputComponent() {
|
|
36
|
+
return {
|
|
37
|
+
moveX: 0,
|
|
38
|
+
moveZ: 0,
|
|
39
|
+
jump: false,
|
|
40
|
+
run: false
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function createPlatformer3DStateComponent() {
|
|
44
|
+
return {
|
|
45
|
+
grounded: false,
|
|
46
|
+
jumping: false,
|
|
47
|
+
falling: false,
|
|
48
|
+
jumpCount: 0,
|
|
49
|
+
jumpStartHeight: 0,
|
|
50
|
+
currentSpeed: 0,
|
|
51
|
+
lastGroundedY: 0,
|
|
52
|
+
jumpPressedLastFrame: false,
|
|
53
|
+
collisionGrounded: false,
|
|
54
|
+
groundedCollisionTime: 0,
|
|
55
|
+
timeSinceGrounded: 0,
|
|
56
|
+
jumpBuffered: false,
|
|
57
|
+
jumpBufferTimer: 0,
|
|
58
|
+
jumpHeld: false,
|
|
59
|
+
jumpCutApplied: false,
|
|
60
|
+
jumpReleasedSinceLastJump: true,
|
|
61
|
+
timeSinceJump: 0,
|
|
62
|
+
jumpedThisFrame: false,
|
|
63
|
+
jumpCutRampTimer: 0,
|
|
64
|
+
verticalVelocity: 0
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// src/lib/behaviors/platformer-3d/platformer-3d-fsm.ts
|
|
69
|
+
var Platformer3DState = /* @__PURE__ */ ((Platformer3DState2) => {
|
|
70
|
+
Platformer3DState2["Idle"] = "idle";
|
|
71
|
+
Platformer3DState2["Walking"] = "walking";
|
|
72
|
+
Platformer3DState2["Running"] = "running";
|
|
73
|
+
Platformer3DState2["Jumping"] = "jumping";
|
|
74
|
+
Platformer3DState2["Falling"] = "falling";
|
|
75
|
+
Platformer3DState2["Landing"] = "landing";
|
|
76
|
+
return Platformer3DState2;
|
|
77
|
+
})(Platformer3DState || {});
|
|
78
|
+
var Platformer3DEvent = /* @__PURE__ */ ((Platformer3DEvent2) => {
|
|
79
|
+
Platformer3DEvent2["Walk"] = "walk";
|
|
80
|
+
Platformer3DEvent2["Run"] = "run";
|
|
81
|
+
Platformer3DEvent2["Jump"] = "jump";
|
|
82
|
+
Platformer3DEvent2["Fall"] = "fall";
|
|
83
|
+
Platformer3DEvent2["Land"] = "land";
|
|
84
|
+
Platformer3DEvent2["Stop"] = "stop";
|
|
85
|
+
return Platformer3DEvent2;
|
|
86
|
+
})(Platformer3DEvent || {});
|
|
87
|
+
var Platformer3DFSM = class {
|
|
88
|
+
constructor(ctx) {
|
|
89
|
+
this.ctx = ctx;
|
|
90
|
+
this.machine = new SyncStateMachine(
|
|
91
|
+
"idle" /* Idle */,
|
|
92
|
+
[
|
|
93
|
+
// Idle transitions
|
|
94
|
+
(0, import_typescript_fsm.t)("idle" /* Idle */, "walk" /* Walk */, "walking" /* Walking */),
|
|
95
|
+
(0, import_typescript_fsm.t)("idle" /* Idle */, "run" /* Run */, "running" /* Running */),
|
|
96
|
+
(0, import_typescript_fsm.t)("idle" /* Idle */, "jump" /* Jump */, "jumping" /* Jumping */),
|
|
97
|
+
(0, import_typescript_fsm.t)("idle" /* Idle */, "fall" /* Fall */, "falling" /* Falling */),
|
|
98
|
+
// Walking transitions
|
|
99
|
+
(0, import_typescript_fsm.t)("walking" /* Walking */, "run" /* Run */, "running" /* Running */),
|
|
100
|
+
(0, import_typescript_fsm.t)("walking" /* Walking */, "jump" /* Jump */, "jumping" /* Jumping */),
|
|
101
|
+
(0, import_typescript_fsm.t)("walking" /* Walking */, "stop" /* Stop */, "idle" /* Idle */),
|
|
102
|
+
(0, import_typescript_fsm.t)("walking" /* Walking */, "fall" /* Fall */, "falling" /* Falling */),
|
|
103
|
+
// Running transitions
|
|
104
|
+
(0, import_typescript_fsm.t)("running" /* Running */, "walk" /* Walk */, "walking" /* Walking */),
|
|
105
|
+
(0, import_typescript_fsm.t)("running" /* Running */, "jump" /* Jump */, "jumping" /* Jumping */),
|
|
106
|
+
(0, import_typescript_fsm.t)("running" /* Running */, "stop" /* Stop */, "idle" /* Idle */),
|
|
107
|
+
(0, import_typescript_fsm.t)("running" /* Running */, "fall" /* Fall */, "falling" /* Falling */),
|
|
108
|
+
// Jumping transitions
|
|
109
|
+
(0, import_typescript_fsm.t)("jumping" /* Jumping */, "fall" /* Fall */, "falling" /* Falling */),
|
|
110
|
+
(0, import_typescript_fsm.t)("jumping" /* Jumping */, "land" /* Land */, "landing" /* Landing */),
|
|
111
|
+
(0, import_typescript_fsm.t)("jumping" /* Jumping */, "jump" /* Jump */, "jumping" /* Jumping */),
|
|
112
|
+
// Multi-jump
|
|
113
|
+
// Falling transitions
|
|
114
|
+
(0, import_typescript_fsm.t)("falling" /* Falling */, "land" /* Land */, "landing" /* Landing */),
|
|
115
|
+
// Landing transitions
|
|
116
|
+
(0, import_typescript_fsm.t)("landing" /* Landing */, "walk" /* Walk */, "walking" /* Walking */),
|
|
117
|
+
(0, import_typescript_fsm.t)("landing" /* Landing */, "run" /* Run */, "running" /* Running */),
|
|
118
|
+
(0, import_typescript_fsm.t)("landing" /* Landing */, "stop" /* Stop */, "idle" /* Idle */),
|
|
119
|
+
// Self-transitions (no-ops)
|
|
120
|
+
(0, import_typescript_fsm.t)("idle" /* Idle */, "stop" /* Stop */, "idle" /* Idle */)
|
|
121
|
+
]
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
ctx;
|
|
125
|
+
machine;
|
|
126
|
+
/**
|
|
127
|
+
* Get the current state
|
|
128
|
+
*/
|
|
129
|
+
getState() {
|
|
130
|
+
return this.machine.getState();
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Dispatch an event to the FSM
|
|
134
|
+
*/
|
|
135
|
+
dispatch(event) {
|
|
136
|
+
if (this.machine.can(event)) {
|
|
137
|
+
this.machine.syncDispatch(event);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Check if grounded
|
|
142
|
+
*/
|
|
143
|
+
isGrounded() {
|
|
144
|
+
const state = this.getState();
|
|
145
|
+
return state === "idle" /* Idle */ || state === "walking" /* Walking */ || state === "running" /* Running */ || state === "landing" /* Landing */;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Get current jump count from context
|
|
149
|
+
*/
|
|
150
|
+
getJumpCount() {
|
|
151
|
+
return this.ctx.state.jumpCount;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Handle collision event to update ground state
|
|
155
|
+
*/
|
|
156
|
+
handleCollision(ctx) {
|
|
157
|
+
if (ctx.contact.normal.y > 0.5) {
|
|
158
|
+
this.ctx.state.collisionGrounded = true;
|
|
159
|
+
this.ctx.state.groundedCollisionTime = performance.now();
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Update FSM based on current state
|
|
164
|
+
*/
|
|
165
|
+
update(input, state) {
|
|
166
|
+
this.ctx.input = input;
|
|
167
|
+
this.ctx.state = state;
|
|
168
|
+
const currentState = this.getState();
|
|
169
|
+
const hasInput = Math.abs(input.moveX) > 0.1 || Math.abs(input.moveZ) > 0.1;
|
|
170
|
+
const isRunning = input.run;
|
|
171
|
+
if (currentState === "falling" /* Falling */ && state.grounded) {
|
|
172
|
+
this.dispatch("land" /* Land */);
|
|
173
|
+
}
|
|
174
|
+
if (state.grounded) {
|
|
175
|
+
if (hasInput) {
|
|
176
|
+
if (isRunning) {
|
|
177
|
+
this.dispatch("run" /* Run */);
|
|
178
|
+
} else {
|
|
179
|
+
this.dispatch("walk" /* Walk */);
|
|
180
|
+
}
|
|
181
|
+
} else {
|
|
182
|
+
this.dispatch("stop" /* Stop */);
|
|
183
|
+
}
|
|
184
|
+
} else {
|
|
185
|
+
if (state.falling) {
|
|
186
|
+
this.dispatch("fall" /* Fall */);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (input.jump && !state.jumpPressedLastFrame) {
|
|
190
|
+
this.dispatch("jump" /* Jump */);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
// src/lib/behaviors/platformer-3d/platformer-3d.descriptor.ts
|
|
196
|
+
var FSM_STATE_BY_INDEX = [
|
|
197
|
+
"idle" /* Idle */,
|
|
198
|
+
"walking" /* Walking */,
|
|
199
|
+
"running" /* Running */,
|
|
200
|
+
"jumping" /* Jumping */,
|
|
201
|
+
"falling" /* Falling */,
|
|
202
|
+
"landing" /* Landing */
|
|
203
|
+
];
|
|
204
|
+
var defaultOptions = {
|
|
205
|
+
walkSpeed: 12,
|
|
206
|
+
runSpeed: 24,
|
|
207
|
+
jumpForce: 12,
|
|
208
|
+
maxJumps: 1,
|
|
209
|
+
gravity: 9.82,
|
|
210
|
+
slopeClimbAngle: 50,
|
|
211
|
+
slopeSlideAngle: 40,
|
|
212
|
+
autostep: { maxHeight: 0.3, minWidth: 0.1 },
|
|
213
|
+
snapToGroundDistance: 0.3,
|
|
214
|
+
characterOffset: 0.05
|
|
215
|
+
};
|
|
216
|
+
var PLATFORMER_BEHAVIOR_KEY = /* @__PURE__ */ Symbol.for("zylem:behavior:platformer-3d");
|
|
217
|
+
function resolvePlatformerAttachShape(options, gameEntity) {
|
|
218
|
+
const fallback = {
|
|
219
|
+
halfHeight: 0.9,
|
|
220
|
+
radius: 0.4,
|
|
221
|
+
offsetX: 0,
|
|
222
|
+
offsetY: 0,
|
|
223
|
+
offsetZ: 0,
|
|
224
|
+
shapeSource: StagePlatformerShapeSource.Auto
|
|
225
|
+
};
|
|
226
|
+
if (options.useAttachedCollider === false && options.shape) {
|
|
227
|
+
const offset = options.shape.offset ?? [0, 0, 0];
|
|
228
|
+
return {
|
|
229
|
+
halfHeight: options.shape.halfHeight,
|
|
230
|
+
radius: options.shape.radius,
|
|
231
|
+
offsetX: offset[0],
|
|
232
|
+
offsetY: offset[1],
|
|
233
|
+
offsetZ: offset[2],
|
|
234
|
+
shapeSource: StagePlatformerShapeSource.Override
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
const collider = gameEntity?.colliderDesc;
|
|
238
|
+
if (collider?.shape?.type === "capsule") {
|
|
239
|
+
const offset = collider.offset ?? [0, 0, 0];
|
|
240
|
+
return {
|
|
241
|
+
halfHeight: collider.shape.halfHeight ?? fallback.halfHeight,
|
|
242
|
+
radius: collider.shape.radius ?? fallback.radius,
|
|
243
|
+
offsetX: offset[0],
|
|
244
|
+
offsetY: offset[1],
|
|
245
|
+
offsetZ: offset[2],
|
|
246
|
+
shapeSource: StagePlatformerShapeSource.Auto
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
return fallback;
|
|
250
|
+
}
|
|
251
|
+
var Platformer3DBehaviorSystem = class {
|
|
252
|
+
constructor(wasmStage, getBehaviorLinks) {
|
|
253
|
+
this.wasmStage = wasmStage;
|
|
254
|
+
this.getBehaviorLinks = getBehaviorLinks;
|
|
255
|
+
}
|
|
256
|
+
wasmStage;
|
|
257
|
+
getBehaviorLinks;
|
|
258
|
+
attachedRuntimeSlots = /* @__PURE__ */ new Set();
|
|
259
|
+
update(_ecs, _delta) {
|
|
260
|
+
const links = this.getBehaviorLinks?.(PLATFORMER_BEHAVIOR_KEY);
|
|
261
|
+
if (!links) return;
|
|
262
|
+
for (const link of links) {
|
|
263
|
+
const gameEntity = link.entity;
|
|
264
|
+
const platformerRef = link.ref;
|
|
265
|
+
const options = platformerRef.options;
|
|
266
|
+
if (!gameEntity.platformer) {
|
|
267
|
+
gameEntity.platformer = createPlatformer3DMovementComponent(options);
|
|
268
|
+
}
|
|
269
|
+
if (!gameEntity.$platformer) {
|
|
270
|
+
gameEntity.$platformer = createPlatformer3DInputComponent();
|
|
271
|
+
}
|
|
272
|
+
if (!gameEntity.platformerState) {
|
|
273
|
+
gameEntity.platformerState = createPlatformer3DStateComponent();
|
|
274
|
+
}
|
|
275
|
+
const handle = gameEntity.runtimeHandle ?? -1;
|
|
276
|
+
if (!this.wasmStage || handle < 0) continue;
|
|
277
|
+
this.ensureWasmAttached(handle, options, gameEntity);
|
|
278
|
+
platformerRef.__wasmStage = this.wasmStage;
|
|
279
|
+
platformerRef.__entity = gameEntity;
|
|
280
|
+
const input = gameEntity.$platformer;
|
|
281
|
+
if (input) {
|
|
282
|
+
this.wasmStage.writeInputAxes(handle, input.moveX ?? 0, input.moveZ ?? 0);
|
|
283
|
+
this.wasmStage.writeInputButton(handle, STAGE_INPUT_BUTTON_JUMP, !!input.jump);
|
|
284
|
+
this.wasmStage.writeInputButton(handle, STAGE_INPUT_BUTTON_RUN, !!input.run);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
ensureWasmAttached(handle, options, gameEntity) {
|
|
289
|
+
if (!this.wasmStage || this.attachedRuntimeSlots.has(handle)) return;
|
|
290
|
+
const autostep = options.autostep ?? defaultOptions.autostep;
|
|
291
|
+
const shape = resolvePlatformerAttachShape(options, gameEntity);
|
|
292
|
+
this.wasmStage.attachPlatformer3D(handle, {
|
|
293
|
+
...shape,
|
|
294
|
+
walkSpeed: options.walkSpeed ?? defaultOptions.walkSpeed,
|
|
295
|
+
runSpeed: options.runSpeed ?? defaultOptions.runSpeed,
|
|
296
|
+
jumpForce: options.jumpForce ?? defaultOptions.jumpForce,
|
|
297
|
+
maxJumps: options.maxJumps ?? defaultOptions.maxJumps,
|
|
298
|
+
gravity: options.gravity ?? defaultOptions.gravity,
|
|
299
|
+
slopeClimbAngle: options.slopeClimbAngle ?? defaultOptions.slopeClimbAngle,
|
|
300
|
+
slopeSlideAngle: options.slopeSlideAngle ?? defaultOptions.slopeSlideAngle,
|
|
301
|
+
autostepHeight: autostep ? autostep.maxHeight : 0,
|
|
302
|
+
autostepMinWidth: autostep ? autostep.minWidth : 0.1,
|
|
303
|
+
snapToGroundDistance: options.snapToGroundDistance ?? defaultOptions.snapToGroundDistance,
|
|
304
|
+
characterOffset: options.characterOffset ?? defaultOptions.characterOffset
|
|
305
|
+
});
|
|
306
|
+
this.attachedRuntimeSlots.add(handle);
|
|
307
|
+
}
|
|
308
|
+
destroy(_ecs) {
|
|
309
|
+
this.attachedRuntimeSlots.clear();
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
var Platformer3DBehavior = defineBehavior({
|
|
313
|
+
name: "platformer-3d",
|
|
314
|
+
defaultOptions,
|
|
315
|
+
systemFactory: (ctx) => new Platformer3DBehaviorSystem(
|
|
316
|
+
ctx.wasmStage ?? null,
|
|
317
|
+
ctx.getBehaviorLinks
|
|
318
|
+
),
|
|
319
|
+
// Handles read the runtime snapshot buffer (values are from the end of
|
|
320
|
+
// the previous step — one-frame lag). The Rust FSM is the single source
|
|
321
|
+
// of truth.
|
|
322
|
+
createHandle: (ref) => ({
|
|
323
|
+
getState: () => {
|
|
324
|
+
const snapshot = readBehaviorSnapshot(ref);
|
|
325
|
+
if (snapshot) return FSM_STATE_BY_INDEX[snapshot.fsmState] ?? "idle" /* Idle */;
|
|
326
|
+
return "idle" /* Idle */;
|
|
327
|
+
},
|
|
328
|
+
isGrounded: () => readBehaviorSnapshot(ref)?.grounded ?? false,
|
|
329
|
+
getJumpCount: () => readBehaviorSnapshot(ref)?.jumpCount ?? 0,
|
|
330
|
+
onPlatformCollision: (_ctx) => {
|
|
331
|
+
}
|
|
332
|
+
})
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
export {
|
|
336
|
+
createPlatformer3DMovementComponent,
|
|
337
|
+
createPlatformer3DInputComponent,
|
|
338
|
+
createPlatformer3DStateComponent,
|
|
339
|
+
Platformer3DState,
|
|
340
|
+
Platformer3DEvent,
|
|
341
|
+
Platformer3DFSM,
|
|
342
|
+
Platformer3DBehavior
|
|
343
|
+
};
|
|
344
|
+
//# sourceMappingURL=chunk-4LLK2FJI.js.map
|