@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.
Files changed (148) hide show
  1. package/README.md +13 -1
  2. package/dist/{behavior-system-y8SGrGWy.d.ts → behavior-system-CldqNUgr.d.ts} +334 -19
  3. package/dist/{chunk-GFARAJV2.js → chunk-2PAHQZZD.js} +44 -73
  4. package/dist/chunk-2PAHQZZD.js.map +1 -0
  5. package/dist/chunk-4CEW64HO.js +210 -0
  6. package/dist/chunk-4CEW64HO.js.map +1 -0
  7. package/dist/chunk-4LLK2FJI.js +344 -0
  8. package/dist/chunk-4LLK2FJI.js.map +1 -0
  9. package/dist/chunk-4MZ57HPD.js +323 -0
  10. package/dist/chunk-4MZ57HPD.js.map +1 -0
  11. package/dist/chunk-52FJIDGL.js +30 -0
  12. package/dist/chunk-52FJIDGL.js.map +1 -0
  13. package/dist/{chunk-UYFOLI2X.js → chunk-5A5AYP2C.js} +1 -1
  14. package/dist/{chunk-UYFOLI2X.js.map → chunk-5A5AYP2C.js.map} +1 -1
  15. package/dist/chunk-5WRI5ZAA.js +31 -0
  16. package/dist/chunk-5WRI5ZAA.js.map +1 -0
  17. package/dist/{chunk-ZRQWSURL.js → chunk-5XPPLTC7.js} +2 -2
  18. package/dist/chunk-5XPPLTC7.js.map +1 -0
  19. package/dist/{chunk-VMQHNJJI.js → chunk-7DGXUAPV.js} +17 -23
  20. package/dist/chunk-7DGXUAPV.js.map +1 -0
  21. package/dist/{chunk-5SM5SH5O.js → chunk-7JLWZXPA.js} +2 -2
  22. package/dist/{chunk-QDCJLJUV.js → chunk-7W3KY55G.js} +22 -13
  23. package/dist/chunk-7W3KY55G.js.map +1 -0
  24. package/dist/chunk-AS3HR7AS.js +188 -0
  25. package/dist/chunk-AS3HR7AS.js.map +1 -0
  26. package/dist/chunk-BAXJ7XHJ.js +155 -0
  27. package/dist/chunk-BAXJ7XHJ.js.map +1 -0
  28. package/dist/chunk-DZ4LLQON.js +867 -0
  29. package/dist/chunk-DZ4LLQON.js.map +1 -0
  30. package/dist/{chunk-4L36OPY2.js → chunk-G2KUXVU6.js} +81 -135
  31. package/dist/chunk-G2KUXVU6.js.map +1 -0
  32. package/dist/{chunk-BXP4K7RF.js → chunk-JVADRLWX.js} +57 -186
  33. package/dist/chunk-JVADRLWX.js.map +1 -0
  34. package/dist/{chunk-ZA43LPRM.js → chunk-KIYUUSPK.js} +2 -2
  35. package/dist/chunk-LZIHSJZR.js +191 -0
  36. package/dist/chunk-LZIHSJZR.js.map +1 -0
  37. package/dist/{chunk-WWVD5DR5.js → chunk-M3QDKKDF.js} +2 -8
  38. package/dist/chunk-M3QDKKDF.js.map +1 -0
  39. package/dist/{chunk-FTDZAKZW.js → chunk-NQPYMUDF.js} +7 -5
  40. package/dist/{chunk-FTDZAKZW.js.map → chunk-NQPYMUDF.js.map} +1 -1
  41. package/dist/{chunk-XP3ASVGJ.js → chunk-NW4SE5XW.js} +2 -2
  42. package/dist/{chunk-KDY5N6J4.js → chunk-OPZ2LVS2.js} +1 -22
  43. package/dist/{chunk-KDY5N6J4.js.map → chunk-OPZ2LVS2.js.map} +1 -1
  44. package/dist/{chunk-5XZWTFGF.js → chunk-PLIXVF7B.js} +3 -3
  45. package/dist/chunk-PLIXVF7B.js.map +1 -0
  46. package/dist/{chunk-27UPCP5B.js → chunk-QS3VXWVO.js} +22 -13
  47. package/dist/chunk-QS3VXWVO.js.map +1 -0
  48. package/dist/{chunk-Y7YIAWPW.js → chunk-SGHIGTVZ.js} +1 -11
  49. package/dist/{chunk-Y7YIAWPW.js.map → chunk-SGHIGTVZ.js.map} +1 -1
  50. package/dist/{chunk-UL6Q5HDP.js → chunk-TBUKJKY6.js} +3 -25
  51. package/dist/chunk-TBUKJKY6.js.map +1 -0
  52. package/dist/{chunk-Z72F2ENM.js → chunk-WU6XXL3D.js} +17 -26
  53. package/dist/chunk-WU6XXL3D.js.map +1 -0
  54. package/dist/{chunk-A4CA35DQ.js → chunk-Y4PHT2HG.js} +25 -45
  55. package/dist/chunk-Y4PHT2HG.js.map +1 -0
  56. package/dist/components-BmievOxF.d.ts +30 -0
  57. package/dist/cooldown.d.ts +2 -2
  58. package/dist/cooldown.js +2 -1
  59. package/dist/coordinators/boundary-ricochet-3d.d.ts +4 -4
  60. package/dist/coordinators/boundary-ricochet-3d.js +1 -0
  61. package/dist/coordinators/boundary-ricochet.d.ts +4 -4
  62. package/dist/coordinators/boundary-ricochet.js +3 -2
  63. package/dist/coordinators/first-person-shooter.js +1 -0
  64. package/dist/coordinators/multidirectional-space-shooter.d.ts +3 -3
  65. package/dist/coordinators/multidirectional-space-shooter.js +5 -3
  66. package/dist/coordinators/top-down-shooter.d.ts +3 -3
  67. package/dist/coordinators/top-down-shooter.js +5 -3
  68. package/dist/core.d.ts +298 -8
  69. package/dist/core.js +502 -14
  70. package/dist/core.js.map +1 -1
  71. package/dist/destructible-3d.d.ts +32 -4
  72. package/dist/destructible-3d.js +2 -1
  73. package/dist/destructible-prebake.js +1 -0
  74. package/dist/{entity-types-CmtQ-IBp.d.ts → entity-types-DhfsZbSK.d.ts} +5 -3
  75. package/dist/first-person.d.ts +27 -38
  76. package/dist/first-person.js +8 -8
  77. package/dist/index.d.ts +14 -15
  78. package/dist/index.js +38 -53
  79. package/dist/jumper-2d.d.ts +3 -17
  80. package/dist/jumper-2d.js +4 -9
  81. package/dist/jumper-3d.d.ts +5 -39
  82. package/dist/jumper-3d.js +5 -10
  83. package/dist/{moveable-Cl9h-_w7.d.ts → moveable-rdIvWiak.d.ts} +21 -4
  84. package/dist/particle-emitter/presets.js +1 -0
  85. package/dist/particle-emitter.d.ts +3 -3
  86. package/dist/particle-emitter.js +3 -2
  87. package/dist/platformer-2d.d.ts +21 -82
  88. package/dist/platformer-2d.js +4 -5
  89. package/dist/platformer-3d.d.ts +21 -78
  90. package/dist/platformer-3d.js +4 -5
  91. package/dist/ricochet-2d.d.ts +3 -3
  92. package/dist/ricochet-2d.js +4 -3
  93. package/dist/ricochet-3d.d.ts +3 -3
  94. package/dist/ricochet-3d.js +4 -3
  95. package/dist/runtime-2d.d.ts +2 -2
  96. package/dist/runtime-2d.js +1 -0
  97. package/dist/runtime-2d.js.map +1 -1
  98. package/dist/screen-visibility.d.ts +2 -2
  99. package/dist/screen-visibility.js +1 -0
  100. package/dist/screen-wrap.d.ts +2 -2
  101. package/dist/screen-wrap.js +4 -3
  102. package/dist/shooter-2d.d.ts +3 -3
  103. package/dist/shooter-2d.js +4 -3
  104. package/dist/thruster.d.ts +5 -34
  105. package/dist/thruster.js +4 -6
  106. package/dist/top-down-movement.d.ts +9 -27
  107. package/dist/top-down-movement.js +6 -6
  108. package/dist/{vector-Bm65-FOG.d.ts → vector-DjhLUrak.d.ts} +2 -4
  109. package/dist/world-boundary-2d.d.ts +2 -2
  110. package/dist/world-boundary-2d.js +5 -4
  111. package/dist/world-boundary-3d.d.ts +2 -2
  112. package/dist/world-boundary-3d.js +4 -3
  113. package/package.json +4 -5
  114. package/dist/chunk-27UPCP5B.js.map +0 -1
  115. package/dist/chunk-4L36OPY2.js.map +0 -1
  116. package/dist/chunk-4URF65BR.js +0 -44
  117. package/dist/chunk-4URF65BR.js.map +0 -1
  118. package/dist/chunk-5XZWTFGF.js.map +0 -1
  119. package/dist/chunk-67XLJ26X.js +0 -536
  120. package/dist/chunk-67XLJ26X.js.map +0 -1
  121. package/dist/chunk-75FJIUIG.js +0 -240
  122. package/dist/chunk-75FJIUIG.js.map +0 -1
  123. package/dist/chunk-A4CA35DQ.js.map +0 -1
  124. package/dist/chunk-BXP4K7RF.js.map +0 -1
  125. package/dist/chunk-F72WX4MH.js +0 -422
  126. package/dist/chunk-F72WX4MH.js.map +0 -1
  127. package/dist/chunk-GFARAJV2.js.map +0 -1
  128. package/dist/chunk-JXMF4NUI.js +0 -309
  129. package/dist/chunk-JXMF4NUI.js.map +0 -1
  130. package/dist/chunk-OTLSUYQQ.js +0 -29
  131. package/dist/chunk-OTLSUYQQ.js.map +0 -1
  132. package/dist/chunk-Q75LKHDF.js +0 -56
  133. package/dist/chunk-Q75LKHDF.js.map +0 -1
  134. package/dist/chunk-QDCJLJUV.js.map +0 -1
  135. package/dist/chunk-RDHSHEZM.js +0 -479
  136. package/dist/chunk-RDHSHEZM.js.map +0 -1
  137. package/dist/chunk-UIG7MWS2.js +0 -518
  138. package/dist/chunk-UIG7MWS2.js.map +0 -1
  139. package/dist/chunk-UL6Q5HDP.js.map +0 -1
  140. package/dist/chunk-VMQHNJJI.js.map +0 -1
  141. package/dist/chunk-WWVD5DR5.js.map +0 -1
  142. package/dist/chunk-Z72F2ENM.js.map +0 -1
  143. package/dist/chunk-ZRQWSURL.js.map +0 -1
  144. package/dist/components-BWG1GkZA.d.ts +0 -20
  145. package/dist/use-behavior-B85d5u7-.d.ts +0 -13
  146. /package/dist/{chunk-5SM5SH5O.js.map → chunk-7JLWZXPA.js.map} +0 -0
  147. /package/dist/{chunk-ZA43LPRM.js.map → chunk-KIYUUSPK.js.map} +0 -0
  148. /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":"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","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