@zylem/behaviors 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{behavior-system-DYbx-x_-.d.ts → behavior-system-DSNek0W9.d.ts} +205 -29
- package/dist/{chunk-Y6UJOUE5.js → chunk-4V7TIRO2.js} +1 -1
- package/dist/chunk-4V7TIRO2.js.map +1 -0
- package/dist/{chunk-DYLH6K7B.js → chunk-4WH2ZN7P.js} +1 -1
- package/dist/{chunk-DYLH6K7B.js.map → chunk-4WH2ZN7P.js.map} +1 -1
- package/dist/{chunk-ZPSAXX73.js → chunk-5SM5SH5O.js} +26 -26
- package/dist/chunk-5SM5SH5O.js.map +1 -0
- package/dist/{chunk-STCCNLPC.js → chunk-5XZWTFGF.js} +5 -3
- package/dist/chunk-5XZWTFGF.js.map +1 -0
- package/dist/{chunk-OYWFPIHF.js → chunk-64HKCSXD.js} +19 -16
- package/dist/chunk-64HKCSXD.js.map +1 -0
- package/dist/{chunk-JAN34G6S.js → chunk-FRJDQM23.js} +108 -154
- package/dist/chunk-FRJDQM23.js.map +1 -0
- package/dist/{chunk-RTRVBEIB.js → chunk-FTDZAKZW.js} +5 -18
- package/dist/chunk-FTDZAKZW.js.map +1 -0
- package/dist/{chunk-XIJNE67Y.js → chunk-GFARAJV2.js} +3 -5
- package/dist/{chunk-XIJNE67Y.js.map → chunk-GFARAJV2.js.map} +1 -1
- package/dist/{chunk-SS7ELMJ7.js → chunk-GTLA6LOJ.js} +16 -22
- package/dist/chunk-GTLA6LOJ.js.map +1 -0
- package/dist/{chunk-VAN4WW3F.js → chunk-H4TJ7X4O.js} +4 -7
- package/dist/chunk-H4TJ7X4O.js.map +1 -0
- package/dist/{chunk-4I6E6DGV.js → chunk-HNESASBB.js} +4 -6
- package/dist/{chunk-4I6E6DGV.js.map → chunk-HNESASBB.js.map} +1 -1
- package/dist/{chunk-KS72RZHX.js → chunk-IZJFUAXB.js} +1 -1
- package/dist/{chunk-KS72RZHX.js.map → chunk-IZJFUAXB.js.map} +1 -1
- package/dist/{chunk-SVLNF434.js → chunk-JXMF4NUI.js} +51 -3
- package/dist/chunk-JXMF4NUI.js.map +1 -0
- package/dist/chunk-KFFETUAL.js +40 -0
- package/dist/chunk-KFFETUAL.js.map +1 -0
- package/dist/chunk-LL4FLHIY.js +12 -0
- package/dist/chunk-LL4FLHIY.js.map +1 -0
- package/dist/{chunk-2CLT36VB.js → chunk-MJFMNRMZ.js} +13 -14
- package/dist/chunk-MJFMNRMZ.js.map +1 -0
- package/dist/{chunk-RTVRUNYR.js → chunk-OTLSUYQQ.js} +2 -2
- package/dist/chunk-OTLSUYQQ.js.map +1 -0
- package/dist/{chunk-XG2YMCJW.js → chunk-PWF757B6.js} +4 -6
- package/dist/{chunk-XG2YMCJW.js.map → chunk-PWF757B6.js.map} +1 -1
- package/dist/{chunk-LD5SMIB3.js → chunk-QVCMJ4Q2.js} +1 -1
- package/dist/{chunk-LD5SMIB3.js.map → chunk-QVCMJ4Q2.js.map} +1 -1
- package/dist/{chunk-F3KRX2V4.js → chunk-RONH2KDI.js} +10 -12
- package/dist/chunk-RONH2KDI.js.map +1 -0
- package/dist/chunk-RTRFJVCZ.js +454 -0
- package/dist/chunk-RTRFJVCZ.js.map +1 -0
- package/dist/{chunk-6C6NXNEW.js → chunk-UL6Q5HDP.js} +25 -3
- package/dist/chunk-UL6Q5HDP.js.map +1 -0
- package/dist/{chunk-KOZUNIIE.js → chunk-V74N7D6Q.js} +7 -7
- package/dist/{chunk-KOZUNIIE.js.map → chunk-V74N7D6Q.js.map} +1 -1
- package/dist/{chunk-YUO6SKJD.js → chunk-WXXXJ22R.js} +11 -13
- package/dist/chunk-WXXXJ22R.js.map +1 -0
- package/dist/{chunk-ARJWUNRE.js → chunk-XLUJDKCT.js} +7 -7
- package/dist/{chunk-ARJWUNRE.js.map → chunk-XLUJDKCT.js.map} +1 -1
- package/dist/{chunk-GCNRXAKH.js → chunk-XP3ASVGJ.js} +13 -10
- package/dist/chunk-XP3ASVGJ.js.map +1 -0
- package/dist/{chunk-ID5QJYF5.js → chunk-Y7YIAWPW.js} +11 -1
- package/dist/chunk-Y7YIAWPW.js.map +1 -0
- package/dist/{chunk-FPTWVCPN.js → chunk-ZA43LPRM.js} +4 -24
- package/dist/chunk-ZA43LPRM.js.map +1 -0
- package/dist/{chunk-K3UNLQ7W.js → chunk-ZRQWSURL.js} +2 -2
- package/dist/chunk-ZRQWSURL.js.map +1 -0
- package/dist/cooldown.d.ts +2 -1
- package/dist/cooldown.js +1 -1
- package/dist/coordinators/boundary-ricochet-3d.d.ts +12 -5
- package/dist/coordinators/boundary-ricochet-3d.js +2 -1
- package/dist/coordinators/boundary-ricochet.d.ts +9 -7
- package/dist/coordinators/boundary-ricochet.js +2 -1
- package/dist/coordinators/first-person-shooter.d.ts +9 -8
- package/dist/coordinators/first-person-shooter.js +3 -3
- package/dist/coordinators/multidirectional-space-shooter.d.ts +12 -9
- package/dist/coordinators/multidirectional-space-shooter.js +4 -4
- package/dist/coordinators/top-down-shooter.d.ts +9 -8
- package/dist/coordinators/top-down-shooter.js +3 -3
- package/dist/core.d.ts +4 -4
- package/dist/core.js +4 -32
- package/dist/core.js.map +1 -1
- package/dist/destructible-3d.d.ts +2 -1
- package/dist/first-person.d.ts +12 -12
- package/dist/first-person.js +8 -8
- package/dist/index.d.ts +37 -14
- package/dist/index.js +73 -44
- package/dist/jumper-2d.d.ts +7 -6
- package/dist/jumper-2d.js +5 -5
- package/dist/jumper-3d.d.ts +12 -11
- package/dist/jumper-3d.js +6 -6
- package/dist/{moveable-BCRpjAQb.d.ts → moveable-BLTHO7Jq.d.ts} +2 -78
- package/dist/particle-emitter.d.ts +1 -1
- package/dist/particle-emitter.js +1 -1
- package/dist/platformer-2d.d.ts +306 -0
- package/dist/platformer-2d.js +23 -0
- package/dist/platformer-3d.d.ts +74 -87
- package/dist/platformer-3d.js +4 -4
- package/dist/ricochet-2d.d.ts +1 -1
- package/dist/ricochet-2d.js +2 -2
- package/dist/ricochet-3d.d.ts +1 -1
- package/dist/ricochet-3d.js +2 -2
- package/dist/runtime-2d.d.ts +93 -2
- package/dist/runtime-2d.js +145 -22
- package/dist/runtime-2d.js.map +1 -1
- package/dist/screen-visibility.d.ts +2 -1
- package/dist/screen-wrap.d.ts +2 -1
- package/dist/screen-wrap.js +1 -1
- package/dist/shooter-2d.d.ts +5 -3
- package/dist/shooter-2d.js +2 -2
- package/dist/shooter-fire-DGripl0z.d.ts +55 -0
- package/dist/thruster.d.ts +8 -18
- package/dist/thruster.js +6 -6
- package/dist/top-down-movement.d.ts +7 -4
- package/dist/top-down-movement.js +5 -5
- package/dist/{use-behavior-D_82jAL3.d.ts → use-behavior-h9-T5FSv.d.ts} +1 -1
- package/dist/world-boundary-2d.d.ts +2 -1
- package/dist/world-boundary-2d.js +2 -2
- package/dist/world-boundary-3d.d.ts +2 -1
- package/dist/world-boundary-3d.js +2 -2
- package/package.json +5 -5
- package/dist/chunk-2C3Z5GE7.js +0 -176
- package/dist/chunk-2C3Z5GE7.js.map +0 -1
- package/dist/chunk-2CLT36VB.js.map +0 -1
- package/dist/chunk-3PYGRHAL.js +0 -35
- package/dist/chunk-3PYGRHAL.js.map +0 -1
- package/dist/chunk-6C6NXNEW.js.map +0 -1
- package/dist/chunk-F3KRX2V4.js.map +0 -1
- package/dist/chunk-FPTWVCPN.js.map +0 -1
- package/dist/chunk-GCNRXAKH.js.map +0 -1
- package/dist/chunk-ID5QJYF5.js.map +0 -1
- package/dist/chunk-JAN34G6S.js.map +0 -1
- package/dist/chunk-K3UNLQ7W.js.map +0 -1
- package/dist/chunk-OYWFPIHF.js.map +0 -1
- package/dist/chunk-RTRVBEIB.js.map +0 -1
- package/dist/chunk-RTVRUNYR.js.map +0 -1
- package/dist/chunk-SS7ELMJ7.js.map +0 -1
- package/dist/chunk-STCCNLPC.js.map +0 -1
- package/dist/chunk-SVLNF434.js.map +0 -1
- package/dist/chunk-VAN4WW3F.js.map +0 -1
- package/dist/chunk-Y6UJOUE5.js.map +0 -1
- package/dist/chunk-YUO6SKJD.js.map +0 -1
- package/dist/chunk-ZPSAXX73.js.map +0 -1
- package/dist/runtime-pong.d.ts +0 -92
- package/dist/runtime-pong.js +0 -38
- /package/dist/{runtime-pong.js.map → platformer-2d.js.map} +0 -0
|
@@ -112,12 +112,10 @@ var defaultOptions = {
|
|
|
112
112
|
};
|
|
113
113
|
var SCREEN_WRAP_BEHAVIOR_KEY = /* @__PURE__ */ Symbol.for("zylem:behavior:screen-wrap");
|
|
114
114
|
var ScreenWrapSystem = class {
|
|
115
|
-
constructor(
|
|
116
|
-
this.world = world;
|
|
115
|
+
constructor(wasmStage, getBehaviorLinks) {
|
|
117
116
|
this.wasmStage = wasmStage;
|
|
118
117
|
this.getBehaviorLinks = getBehaviorLinks;
|
|
119
118
|
}
|
|
120
|
-
world;
|
|
121
119
|
wasmStage;
|
|
122
120
|
getBehaviorLinks;
|
|
123
121
|
attachedRuntimeSlots = /* @__PURE__ */ new Set();
|
|
@@ -198,7 +196,7 @@ var ScreenWrapSystem = class {
|
|
|
198
196
|
var ScreenWrapBehavior = defineBehavior({
|
|
199
197
|
name: "screen-wrap",
|
|
200
198
|
defaultOptions,
|
|
201
|
-
systemFactory: (ctx) => new ScreenWrapSystem(ctx.
|
|
199
|
+
systemFactory: (ctx) => new ScreenWrapSystem(ctx.wasmStage ?? null, ctx.getBehaviorLinks)
|
|
202
200
|
});
|
|
203
201
|
|
|
204
202
|
export {
|
|
@@ -207,4 +205,4 @@ export {
|
|
|
207
205
|
ScreenWrapFSM,
|
|
208
206
|
ScreenWrapBehavior
|
|
209
207
|
};
|
|
210
|
-
//# sourceMappingURL=chunk-
|
|
208
|
+
//# sourceMappingURL=chunk-GFARAJV2.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/behaviors/screen-wrap/screen-wrap-fsm.ts","../src/lib/behaviors/screen-wrap/screen-wrap.descriptor.ts"],"sourcesContent":["/**\n * ScreenWrapFSM\n * \n * State machine for screen wrap behavior.\n * Reports position relative to bounds edges.\n */\n\nimport { SyncStateMachine, t } from '../../core/utility/sync-state-machine';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// FSM State Model\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport enum ScreenWrapState {\n\tCenter = 'center',\n\tNearEdgeLeft = 'near-edge-left',\n\tNearEdgeRight = 'near-edge-right',\n\tNearEdgeTop = 'near-edge-top',\n\tNearEdgeBottom = 'near-edge-bottom',\n\tWrapped = 'wrapped',\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// FSM Events\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport enum ScreenWrapEvent {\n\tEnterCenter = 'enter-center',\n\tApproachLeft = 'approach-left',\n\tApproachRight = 'approach-right',\n\tApproachTop = 'approach-top',\n\tApproachBottom = 'approach-bottom',\n\tWrap = 'wrap',\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ScreenWrapFSM\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport class ScreenWrapFSM {\n\tmachine: SyncStateMachine<ScreenWrapState, ScreenWrapEvent, never>;\n\n\tconstructor() {\n\t\tthis.machine = new SyncStateMachine<ScreenWrapState, ScreenWrapEvent, never>(\n\t\t\tScreenWrapState.Center,\n\t\t\t[\n\t\t\t\t// From Center\n\t\t\t\tt(ScreenWrapState.Center, ScreenWrapEvent.ApproachLeft, ScreenWrapState.NearEdgeLeft),\n\t\t\t\tt(ScreenWrapState.Center, ScreenWrapEvent.ApproachRight, ScreenWrapState.NearEdgeRight),\n\t\t\t\tt(ScreenWrapState.Center, ScreenWrapEvent.ApproachTop, ScreenWrapState.NearEdgeTop),\n\t\t\t\tt(ScreenWrapState.Center, ScreenWrapEvent.ApproachBottom, ScreenWrapState.NearEdgeBottom),\n\t\t\t\t\n\t\t\t\t// From NearEdge to Wrapped\n\t\t\t\tt(ScreenWrapState.NearEdgeLeft, ScreenWrapEvent.Wrap, ScreenWrapState.Wrapped),\n\t\t\t\tt(ScreenWrapState.NearEdgeRight, ScreenWrapEvent.Wrap, ScreenWrapState.Wrapped),\n\t\t\t\tt(ScreenWrapState.NearEdgeTop, ScreenWrapEvent.Wrap, ScreenWrapState.Wrapped),\n\t\t\t\tt(ScreenWrapState.NearEdgeBottom, ScreenWrapEvent.Wrap, ScreenWrapState.Wrapped),\n\n\t\t\t\t// From NearEdge back to Center\n\t\t\t\tt(ScreenWrapState.NearEdgeLeft, ScreenWrapEvent.EnterCenter, ScreenWrapState.Center),\n\t\t\t\tt(ScreenWrapState.NearEdgeRight, ScreenWrapEvent.EnterCenter, ScreenWrapState.Center),\n\t\t\t\tt(ScreenWrapState.NearEdgeTop, ScreenWrapEvent.EnterCenter, ScreenWrapState.Center),\n\t\t\t\tt(ScreenWrapState.NearEdgeBottom, ScreenWrapEvent.EnterCenter, ScreenWrapState.Center),\n\n\t\t\t\t// From Wrapped back to Center\n\t\t\t\tt(ScreenWrapState.Wrapped, ScreenWrapEvent.EnterCenter, ScreenWrapState.Center),\n\n\t\t\t\t// From Wrapped to NearEdge (landed near opposite edge)\n\t\t\t\tt(ScreenWrapState.Wrapped, ScreenWrapEvent.ApproachLeft, ScreenWrapState.NearEdgeLeft),\n\t\t\t\tt(ScreenWrapState.Wrapped, ScreenWrapEvent.ApproachRight, ScreenWrapState.NearEdgeRight),\n\t\t\t\tt(ScreenWrapState.Wrapped, ScreenWrapEvent.ApproachTop, ScreenWrapState.NearEdgeTop),\n\t\t\t\tt(ScreenWrapState.Wrapped, ScreenWrapEvent.ApproachBottom, ScreenWrapState.NearEdgeBottom),\n\n\t\t\t\t// Self-transitions (no-ops for redundant events)\n\t\t\t\tt(ScreenWrapState.Center, ScreenWrapEvent.EnterCenter, ScreenWrapState.Center),\n\t\t\t\tt(ScreenWrapState.NearEdgeLeft, ScreenWrapEvent.ApproachLeft, ScreenWrapState.NearEdgeLeft),\n\t\t\t\tt(ScreenWrapState.NearEdgeRight, ScreenWrapEvent.ApproachRight, ScreenWrapState.NearEdgeRight),\n\t\t\t\tt(ScreenWrapState.NearEdgeTop, ScreenWrapEvent.ApproachTop, ScreenWrapState.NearEdgeTop),\n\t\t\t\tt(ScreenWrapState.NearEdgeBottom, ScreenWrapEvent.ApproachBottom, ScreenWrapState.NearEdgeBottom),\n\t\t\t]\n\t\t);\n\t}\n\n\tgetState(): ScreenWrapState {\n\t\treturn this.machine.getState();\n\t}\n\n\tdispatch(event: ScreenWrapEvent): void {\n\t\tif (this.machine.can(event)) {\n\t\t\tthis.machine.syncDispatch(event);\n\t\t}\n\t}\n\n\t/**\n\t * Update FSM based on entity position relative to bounds\n\t */\n\tupdate(position: { x: number; y: number }, bounds: {\n\t\tminX: number; maxX: number; minY: number; maxY: number;\n\t\tedgeThreshold: number;\n\t}, wrapped: boolean): void {\n\t\tconst { x, y } = position;\n\t\tconst { minX, maxX, minY, maxY, edgeThreshold } = bounds;\n\n\t\tif (wrapped) {\n\t\t\tthis.dispatch(ScreenWrapEvent.Wrap);\n\t\t\treturn;\n\t\t}\n\n\t\t// Check if near edges\n\t\tconst nearLeft = x < minX + edgeThreshold;\n\t\tconst nearRight = x > maxX - edgeThreshold;\n\t\tconst nearBottom = y < minY + edgeThreshold;\n\t\tconst nearTop = y > maxY - edgeThreshold;\n\n\t\tif (nearLeft) {\n\t\t\tthis.dispatch(ScreenWrapEvent.ApproachLeft);\n\t\t} else if (nearRight) {\n\t\t\tthis.dispatch(ScreenWrapEvent.ApproachRight);\n\t\t} else if (nearTop) {\n\t\t\tthis.dispatch(ScreenWrapEvent.ApproachTop);\n\t\t} else if (nearBottom) {\n\t\t\tthis.dispatch(ScreenWrapEvent.ApproachBottom);\n\t\t} else {\n\t\t\tthis.dispatch(ScreenWrapEvent.EnterCenter);\n\t\t}\n\t}\n}\n","/**\n * ScreenWrapBehavior\n * \n * When an entity exits the defined 2D bounds, it wraps around to the opposite edge.\n * Asteroids-style screen wrapping with FSM for edge detection.\n */\n\nimport { defineBehavior } from '../behavior-descriptor';\nimport type { BehaviorEntityLink, BehaviorSystem } from '../behavior-system';\nimport { ScreenWrapFSM } from './screen-wrap-fsm';\nimport { createBounds2DRect, wrapPoint2D } from '../shared/bounds-2d';\nimport type { WasmStageRuntime } from '../../runtime/wasm-stage-runtime';\n\n/**\n * Screen wrap options (typed for entity.use() autocomplete)\n */\nexport interface ScreenWrapOptions {\n\t/** Width of the wrapping area (default: 20) */\n\twidth: number;\n\t/** Height of the wrapping area (default: 15) */\n\theight: number;\n\t/** Center X position (default: 0) */\n\tcenterX: number;\n\t/** Center Y position (default: 0) */\n\tcenterY: number;\n\t/** Distance from edge to trigger NearEdge state (default: 2) */\n\tedgeThreshold: number;\n}\n\nconst defaultOptions: ScreenWrapOptions = {\n\twidth: 20,\n\theight: 15,\n\tcenterX: 0,\n\tcenterY: 0,\n\tedgeThreshold: 2,\n};\n\nconst SCREEN_WRAP_BEHAVIOR_KEY = Symbol.for('zylem:behavior:screen-wrap');\n\n/**\n * ScreenWrapSystem - Wraps entities around 2D bounds\n *\n * Mirrors the Rust `screen_wrap` Stage behavior: when `wasmStage` is present\n * and the entity has a `runtimeHandle`, the wasm runtime owns the wrap and\n * this system only mirrors the queried state into the local FSM. Otherwise\n * the legacy TS path runs against the Rapier `RigidBody` translation.\n */\nclass ScreenWrapSystem implements BehaviorSystem {\n\tprivate attachedRuntimeSlots = new Set<number>();\n\n\tconstructor(\n\t\tprivate world: any,\n\t\tprivate wasmStage: WasmStageRuntime | null,\n\t\tprivate getBehaviorLinks?: (key: symbol) => Iterable<BehaviorEntityLink>,\n\t) {}\n\n\tupdate(_ecs: unknown, _delta: number): void {\n\t\tconst links = this.getBehaviorLinks?.(SCREEN_WRAP_BEHAVIOR_KEY);\n\t\tif (!links) return;\n\n\t\tfor (const link of links) {\n\t\t\tconst gameEntity = link.entity as any;\n\t\t\tconst wrapRef = link.ref as any;\n\t\t\tconst options = wrapRef.options as ScreenWrapOptions;\n\n\t\t\tif (!wrapRef.fsm) {\n\t\t\t\twrapRef.fsm = new ScreenWrapFSM();\n\t\t\t}\n\n\t\t\tconst handle = (gameEntity.runtimeHandle ?? -1) as number;\n\t\t\tif (this.wasmStage && handle >= 0) {\n\t\t\t\tthis.ensureWasmAttached(handle, options);\n\t\t\t\tconst pose = this.wasmStage.getPose(handle);\n\t\t\t\tconst query = this.wasmStage.queryScreenWrap(handle);\n\t\t\t\tif (pose && query) {\n\t\t\t\t\tconst bounds = createBounds2DRect(options);\n\t\t\t\t\twrapRef.fsm.update(\n\t\t\t\t\t\t{ x: pose.position[0], y: pose.position[1] },\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tminX: bounds.minX,\n\t\t\t\t\t\t\tmaxX: bounds.maxX,\n\t\t\t\t\t\t\tminY: bounds.minY,\n\t\t\t\t\t\t\tmaxY: bounds.maxY,\n\t\t\t\t\t\t\tedgeThreshold: options.edgeThreshold,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tquery.lastWrappedAxes !== 0,\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!gameEntity.body) continue;\n\t\t\tconst wrapped = this.wrapEntity(gameEntity, options);\n\t\t\tconst pos = gameEntity.body.translation();\n\t\t\tconst bounds = createBounds2DRect(options);\n\t\t\twrapRef.fsm.update(\n\t\t\t\t{ x: pos.x, y: pos.y },\n\t\t\t\t{\n\t\t\t\t\tminX: bounds.minX,\n\t\t\t\t\tmaxX: bounds.maxX,\n\t\t\t\t\tminY: bounds.minY,\n\t\t\t\t\tmaxY: bounds.maxY,\n\t\t\t\t\tedgeThreshold: options.edgeThreshold,\n\t\t\t\t},\n\t\t\t\twrapped,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate ensureWasmAttached(handle: number, options: ScreenWrapOptions): void {\n\t\tif (!this.wasmStage || this.attachedRuntimeSlots.has(handle)) return;\n\t\tthis.wasmStage.attachScreenWrap(handle, {\n\t\t\twidth: options.width,\n\t\t\theight: options.height,\n\t\t\tcenterX: options.centerX,\n\t\t\tcenterY: options.centerY,\n\t\t\tedgeThreshold: options.edgeThreshold,\n\t\t});\n\t\tthis.attachedRuntimeSlots.add(handle);\n\t}\n\n\tprivate wrapEntity(entity: any, options: ScreenWrapOptions): boolean {\n\t\tconst body = entity.body;\n\t\tif (!body) return false;\n\n\t\tconst pos = body.translation();\n\t\tconst bounds = createBounds2DRect(options);\n\t\tconst result = wrapPoint2D({ x: pos.x, y: pos.y }, bounds);\n\n\t\tif (result.wrapped) {\n\t\t\tbody.setTranslation({ x: result.x, y: result.y, z: pos.z }, true);\n\t\t}\n\n\t\treturn result.wrapped;\n\t}\n\n\tdestroy(_ecs: unknown): void {\n\t\tthis.attachedRuntimeSlots.clear();\n\t}\n}\n\n/**\n * ScreenWrapBehavior - Wraps entities around 2D bounds\n * \n * @example\n * ```typescript\n * import { ScreenWrapBehavior } from \"@zylem/game-lib/behavior\";\n * \n * const ship = createSprite({ ... });\n * const wrapRef = ship.use(ScreenWrapBehavior, { width: 20, height: 15 });\n * \n * // Access FSM to observe edge state\n * const fsm = wrapRef.getFSM();\n * console.log(fsm?.getState()); // 'center', 'near-edge-left', 'wrapped', etc.\n * ```\n */\nexport const ScreenWrapBehavior = defineBehavior({\n\tname: 'screen-wrap',\n\tdefaultOptions,\n\tsystemFactory: (ctx) =>\n\t\tnew ScreenWrapSystem(ctx.world, ctx.wasmStage ?? null, ctx.getBehaviorLinks),\n});\n"],"mappings":"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","names":["ScreenWrapState","ScreenWrapEvent","bounds"]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/behaviors/screen-wrap/screen-wrap-fsm.ts","../src/lib/behaviors/screen-wrap/screen-wrap.descriptor.ts"],"sourcesContent":["/**\n * ScreenWrapFSM\n * \n * State machine for screen wrap behavior.\n * Reports position relative to bounds edges.\n */\n\nimport { SyncStateMachine, t } from '../../core/utility/sync-state-machine';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// FSM State Model\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport enum ScreenWrapState {\n\tCenter = 'center',\n\tNearEdgeLeft = 'near-edge-left',\n\tNearEdgeRight = 'near-edge-right',\n\tNearEdgeTop = 'near-edge-top',\n\tNearEdgeBottom = 'near-edge-bottom',\n\tWrapped = 'wrapped',\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// FSM Events\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport enum ScreenWrapEvent {\n\tEnterCenter = 'enter-center',\n\tApproachLeft = 'approach-left',\n\tApproachRight = 'approach-right',\n\tApproachTop = 'approach-top',\n\tApproachBottom = 'approach-bottom',\n\tWrap = 'wrap',\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ScreenWrapFSM\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport class ScreenWrapFSM {\n\tmachine: SyncStateMachine<ScreenWrapState, ScreenWrapEvent, never>;\n\n\tconstructor() {\n\t\tthis.machine = new SyncStateMachine<ScreenWrapState, ScreenWrapEvent, never>(\n\t\t\tScreenWrapState.Center,\n\t\t\t[\n\t\t\t\t// From Center\n\t\t\t\tt(ScreenWrapState.Center, ScreenWrapEvent.ApproachLeft, ScreenWrapState.NearEdgeLeft),\n\t\t\t\tt(ScreenWrapState.Center, ScreenWrapEvent.ApproachRight, ScreenWrapState.NearEdgeRight),\n\t\t\t\tt(ScreenWrapState.Center, ScreenWrapEvent.ApproachTop, ScreenWrapState.NearEdgeTop),\n\t\t\t\tt(ScreenWrapState.Center, ScreenWrapEvent.ApproachBottom, ScreenWrapState.NearEdgeBottom),\n\t\t\t\t\n\t\t\t\t// From NearEdge to Wrapped\n\t\t\t\tt(ScreenWrapState.NearEdgeLeft, ScreenWrapEvent.Wrap, ScreenWrapState.Wrapped),\n\t\t\t\tt(ScreenWrapState.NearEdgeRight, ScreenWrapEvent.Wrap, ScreenWrapState.Wrapped),\n\t\t\t\tt(ScreenWrapState.NearEdgeTop, ScreenWrapEvent.Wrap, ScreenWrapState.Wrapped),\n\t\t\t\tt(ScreenWrapState.NearEdgeBottom, ScreenWrapEvent.Wrap, ScreenWrapState.Wrapped),\n\n\t\t\t\t// From NearEdge back to Center\n\t\t\t\tt(ScreenWrapState.NearEdgeLeft, ScreenWrapEvent.EnterCenter, ScreenWrapState.Center),\n\t\t\t\tt(ScreenWrapState.NearEdgeRight, ScreenWrapEvent.EnterCenter, ScreenWrapState.Center),\n\t\t\t\tt(ScreenWrapState.NearEdgeTop, ScreenWrapEvent.EnterCenter, ScreenWrapState.Center),\n\t\t\t\tt(ScreenWrapState.NearEdgeBottom, ScreenWrapEvent.EnterCenter, ScreenWrapState.Center),\n\n\t\t\t\t// From Wrapped back to Center\n\t\t\t\tt(ScreenWrapState.Wrapped, ScreenWrapEvent.EnterCenter, ScreenWrapState.Center),\n\n\t\t\t\t// From Wrapped to NearEdge (landed near opposite edge)\n\t\t\t\tt(ScreenWrapState.Wrapped, ScreenWrapEvent.ApproachLeft, ScreenWrapState.NearEdgeLeft),\n\t\t\t\tt(ScreenWrapState.Wrapped, ScreenWrapEvent.ApproachRight, ScreenWrapState.NearEdgeRight),\n\t\t\t\tt(ScreenWrapState.Wrapped, ScreenWrapEvent.ApproachTop, ScreenWrapState.NearEdgeTop),\n\t\t\t\tt(ScreenWrapState.Wrapped, ScreenWrapEvent.ApproachBottom, ScreenWrapState.NearEdgeBottom),\n\n\t\t\t\t// Self-transitions (no-ops for redundant events)\n\t\t\t\tt(ScreenWrapState.Center, ScreenWrapEvent.EnterCenter, ScreenWrapState.Center),\n\t\t\t\tt(ScreenWrapState.NearEdgeLeft, ScreenWrapEvent.ApproachLeft, ScreenWrapState.NearEdgeLeft),\n\t\t\t\tt(ScreenWrapState.NearEdgeRight, ScreenWrapEvent.ApproachRight, ScreenWrapState.NearEdgeRight),\n\t\t\t\tt(ScreenWrapState.NearEdgeTop, ScreenWrapEvent.ApproachTop, ScreenWrapState.NearEdgeTop),\n\t\t\t\tt(ScreenWrapState.NearEdgeBottom, ScreenWrapEvent.ApproachBottom, ScreenWrapState.NearEdgeBottom),\n\t\t\t]\n\t\t);\n\t}\n\n\tgetState(): ScreenWrapState {\n\t\treturn this.machine.getState();\n\t}\n\n\tdispatch(event: ScreenWrapEvent): void {\n\t\tif (this.machine.can(event)) {\n\t\t\tthis.machine.syncDispatch(event);\n\t\t}\n\t}\n\n\t/**\n\t * Update FSM based on entity position relative to bounds\n\t */\n\tupdate(position: { x: number; y: number }, bounds: {\n\t\tminX: number; maxX: number; minY: number; maxY: number;\n\t\tedgeThreshold: number;\n\t}, wrapped: boolean): void {\n\t\tconst { x, y } = position;\n\t\tconst { minX, maxX, minY, maxY, edgeThreshold } = bounds;\n\n\t\tif (wrapped) {\n\t\t\tthis.dispatch(ScreenWrapEvent.Wrap);\n\t\t\treturn;\n\t\t}\n\n\t\t// Check if near edges\n\t\tconst nearLeft = x < minX + edgeThreshold;\n\t\tconst nearRight = x > maxX - edgeThreshold;\n\t\tconst nearBottom = y < minY + edgeThreshold;\n\t\tconst nearTop = y > maxY - edgeThreshold;\n\n\t\tif (nearLeft) {\n\t\t\tthis.dispatch(ScreenWrapEvent.ApproachLeft);\n\t\t} else if (nearRight) {\n\t\t\tthis.dispatch(ScreenWrapEvent.ApproachRight);\n\t\t} else if (nearTop) {\n\t\t\tthis.dispatch(ScreenWrapEvent.ApproachTop);\n\t\t} else if (nearBottom) {\n\t\t\tthis.dispatch(ScreenWrapEvent.ApproachBottom);\n\t\t} else {\n\t\t\tthis.dispatch(ScreenWrapEvent.EnterCenter);\n\t\t}\n\t}\n}\n","/**\n * ScreenWrapBehavior\n * \n * When an entity exits the defined 2D bounds, it wraps around to the opposite edge.\n * Asteroids-style screen wrapping with FSM for edge detection.\n */\n\nimport { defineBehavior } from '../behavior-descriptor';\nimport type { BehaviorEntityLink, BehaviorSystem } from '../behavior-system';\nimport { ScreenWrapFSM } from './screen-wrap-fsm';\nimport { createBounds2DRect, wrapPoint2D } from '../shared/bounds-2d';\nimport type { WasmStageRuntime } from '../../runtime/wasm-stage-runtime';\n\n/**\n * Screen wrap options (typed for entity.use() autocomplete)\n */\nexport interface ScreenWrapOptions {\n\t/** Width of the wrapping area (default: 20) */\n\twidth: number;\n\t/** Height of the wrapping area (default: 15) */\n\theight: number;\n\t/** Center X position (default: 0) */\n\tcenterX: number;\n\t/** Center Y position (default: 0) */\n\tcenterY: number;\n\t/** Distance from edge to trigger NearEdge state (default: 2) */\n\tedgeThreshold: number;\n}\n\nconst defaultOptions: ScreenWrapOptions = {\n\twidth: 20,\n\theight: 15,\n\tcenterX: 0,\n\tcenterY: 0,\n\tedgeThreshold: 2,\n};\n\nconst SCREEN_WRAP_BEHAVIOR_KEY = Symbol.for('zylem:behavior:screen-wrap');\n\n/**\n * ScreenWrapSystem - Wraps entities around 2D bounds\n *\n * Mirrors the Rust `screen_wrap` Stage behavior: when `wasmStage` is present\n * and the entity has a `runtimeHandle`, the wasm runtime owns the wrap and\n * this system only mirrors the queried state into the local FSM. Otherwise\n * the legacy TS path runs against the Rapier `RigidBody` translation.\n */\nclass ScreenWrapSystem implements BehaviorSystem {\n\tprivate attachedRuntimeSlots = new Set<number>();\n\n\tconstructor(\n\t\tprivate wasmStage: WasmStageRuntime | null,\n\t\tprivate getBehaviorLinks?: (key: symbol) => Iterable<BehaviorEntityLink>,\n\t) {}\n\n\tupdate(_ecs: unknown, _delta: number): void {\n\t\tconst links = this.getBehaviorLinks?.(SCREEN_WRAP_BEHAVIOR_KEY);\n\t\tif (!links) return;\n\n\t\tfor (const link of links) {\n\t\t\tconst gameEntity = link.entity as any;\n\t\t\tconst wrapRef = link.ref as any;\n\t\t\tconst options = wrapRef.options as ScreenWrapOptions;\n\n\t\t\tif (!wrapRef.fsm) {\n\t\t\t\twrapRef.fsm = new ScreenWrapFSM();\n\t\t\t}\n\n\t\t\tconst handle = (gameEntity.runtimeHandle ?? -1) as number;\n\t\t\tif (this.wasmStage && handle >= 0) {\n\t\t\t\tthis.ensureWasmAttached(handle, options);\n\t\t\t\tconst pose = this.wasmStage.getPose(handle);\n\t\t\t\tconst query = this.wasmStage.queryScreenWrap(handle);\n\t\t\t\tif (pose && query) {\n\t\t\t\t\tconst bounds = createBounds2DRect(options);\n\t\t\t\t\twrapRef.fsm.update(\n\t\t\t\t\t\t{ x: pose.position[0], y: pose.position[1] },\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tminX: bounds.minX,\n\t\t\t\t\t\t\tmaxX: bounds.maxX,\n\t\t\t\t\t\t\tminY: bounds.minY,\n\t\t\t\t\t\t\tmaxY: bounds.maxY,\n\t\t\t\t\t\t\tedgeThreshold: options.edgeThreshold,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tquery.lastWrappedAxes !== 0,\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!gameEntity.body) continue;\n\t\t\tconst wrapped = this.wrapEntity(gameEntity, options);\n\t\t\tconst pos = gameEntity.body.translation();\n\t\t\tconst bounds = createBounds2DRect(options);\n\t\t\twrapRef.fsm.update(\n\t\t\t\t{ x: pos.x, y: pos.y },\n\t\t\t\t{\n\t\t\t\t\tminX: bounds.minX,\n\t\t\t\t\tmaxX: bounds.maxX,\n\t\t\t\t\tminY: bounds.minY,\n\t\t\t\t\tmaxY: bounds.maxY,\n\t\t\t\t\tedgeThreshold: options.edgeThreshold,\n\t\t\t\t},\n\t\t\t\twrapped,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate ensureWasmAttached(handle: number, options: ScreenWrapOptions): void {\n\t\tif (!this.wasmStage || this.attachedRuntimeSlots.has(handle)) return;\n\t\tthis.wasmStage.attachScreenWrap(handle, {\n\t\t\twidth: options.width,\n\t\t\theight: options.height,\n\t\t\tcenterX: options.centerX,\n\t\t\tcenterY: options.centerY,\n\t\t\tedgeThreshold: options.edgeThreshold,\n\t\t});\n\t\tthis.attachedRuntimeSlots.add(handle);\n\t}\n\n\tprivate wrapEntity(entity: any, options: ScreenWrapOptions): boolean {\n\t\tconst body = entity.body;\n\t\tif (!body) return false;\n\n\t\tconst pos = body.translation();\n\t\tconst bounds = createBounds2DRect(options);\n\t\tconst result = wrapPoint2D({ x: pos.x, y: pos.y }, bounds);\n\n\t\tif (result.wrapped) {\n\t\t\tbody.setTranslation({ x: result.x, y: result.y, z: pos.z }, true);\n\t\t}\n\n\t\treturn result.wrapped;\n\t}\n\n\tdestroy(_ecs: unknown): void {\n\t\tthis.attachedRuntimeSlots.clear();\n\t}\n}\n\n/**\n * ScreenWrapBehavior - Wraps entities around 2D bounds\n * \n * @example\n * ```typescript\n * import { ScreenWrapBehavior } from \"@zylem/game-lib/behavior\";\n * \n * const ship = createSprite({ ... });\n * const wrapRef = ship.use(ScreenWrapBehavior, { width: 20, height: 15 });\n * \n * // Access FSM to observe edge state\n * const fsm = wrapRef.getFSM();\n * console.log(fsm?.getState()); // 'center', 'near-edge-left', 'wrapped', etc.\n * ```\n */\nexport const ScreenWrapBehavior = defineBehavior({\n\tname: 'screen-wrap',\n\tdefaultOptions,\n\tsystemFactory: (ctx) =>\n\t\tnew ScreenWrapSystem(ctx.wasmStage ?? null, ctx.getBehaviorLinks),\n});\n"],"mappings":"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","names":["ScreenWrapState","ScreenWrapEvent","bounds"]}
|
|
@@ -2,10 +2,10 @@ import {
|
|
|
2
2
|
GroundProbe3D,
|
|
3
3
|
getGroundAnchorOffsetY,
|
|
4
4
|
getGroundSnapTargetY
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-JXMF4NUI.js";
|
|
6
6
|
import {
|
|
7
7
|
setVelocityIntent
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-OTLSUYQQ.js";
|
|
9
9
|
import {
|
|
10
10
|
SyncStateMachine,
|
|
11
11
|
t
|
|
@@ -48,7 +48,7 @@ function createJumpState2D() {
|
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
// src/lib/behaviors/jumper-2d/jumper-2d.
|
|
51
|
+
// src/lib/behaviors/jumper-2d/jumper-2d.system.ts
|
|
52
52
|
function jumpVelocityFromHeight(gravity, height) {
|
|
53
53
|
return Math.sqrt(2 * gravity * height);
|
|
54
54
|
}
|
|
@@ -59,7 +59,7 @@ var Jumper2DTickEvent = /* @__PURE__ */ ((Jumper2DTickEvent3) => {
|
|
|
59
59
|
Jumper2DTickEvent3["Land"] = "land";
|
|
60
60
|
return Jumper2DTickEvent3;
|
|
61
61
|
})(Jumper2DTickEvent || {});
|
|
62
|
-
var
|
|
62
|
+
var Jumper2DSystem = class {
|
|
63
63
|
tick(config, input, ctx, state) {
|
|
64
64
|
const dtMs = ctx.dt * 1e3;
|
|
65
65
|
const now = performance.now();
|
|
@@ -218,11 +218,6 @@ var defaultOptions = {
|
|
|
218
218
|
debugGroundProbe: false
|
|
219
219
|
};
|
|
220
220
|
var JUMPER_2D_BEHAVIOR_KEY = /* @__PURE__ */ Symbol.for("zylem:behavior:jumper-2d");
|
|
221
|
-
var JUMPER_2D_OFFSETS = [
|
|
222
|
-
{ x: 0, z: 0 },
|
|
223
|
-
{ x: 0.35, z: 0 },
|
|
224
|
-
{ x: -0.35, z: 0 }
|
|
225
|
-
];
|
|
226
221
|
function isJumper2DGrounded(params) {
|
|
227
222
|
if (params.velocityY > 0) {
|
|
228
223
|
return false;
|
|
@@ -237,22 +232,22 @@ function shouldSnapJumper2DToGround(support, options, velocityY) {
|
|
|
237
232
|
}
|
|
238
233
|
var Jumper2DBehaviorSystem = class {
|
|
239
234
|
constructor(world, scene, wasmStage, getBehaviorLinks) {
|
|
240
|
-
this.world = world;
|
|
241
235
|
this.scene = scene;
|
|
242
236
|
this.wasmStage = wasmStage;
|
|
243
237
|
this.getBehaviorLinks = getBehaviorLinks;
|
|
244
238
|
this.groundProbe = new GroundProbe3D(world);
|
|
245
239
|
}
|
|
246
|
-
world;
|
|
247
240
|
scene;
|
|
248
241
|
wasmStage;
|
|
249
242
|
getBehaviorLinks;
|
|
250
|
-
behavior = new
|
|
243
|
+
behavior = new Jumper2DSystem();
|
|
251
244
|
groundProbe;
|
|
252
245
|
initializedEntities = /* @__PURE__ */ new Set();
|
|
253
246
|
timeSinceGroundedMs = /* @__PURE__ */ new Map();
|
|
254
247
|
wasJumpHeld = /* @__PURE__ */ new Map();
|
|
255
248
|
attachedRuntimeSlots = /* @__PURE__ */ new Set();
|
|
249
|
+
/** Reused per-entity per-frame instead of spreading a fresh input object. */
|
|
250
|
+
effectiveInput = createJumpInput2D();
|
|
256
251
|
update(_ecs, delta) {
|
|
257
252
|
const links = this.getBehaviorLinks?.(JUMPER_2D_BEHAVIOR_KEY);
|
|
258
253
|
if (!links) return;
|
|
@@ -292,7 +287,6 @@ var Jumper2DBehaviorSystem = class {
|
|
|
292
287
|
const probeOriginYOffset = 0.05 - getGroundAnchorOffsetY(gameEntity);
|
|
293
288
|
const support = this.groundProbe.probeSupport(gameEntity, {
|
|
294
289
|
rayLength,
|
|
295
|
-
offsets: JUMPER_2D_OFFSETS,
|
|
296
290
|
mode: "any",
|
|
297
291
|
debug: options.debugGroundProbe ?? false,
|
|
298
292
|
scene: this.scene,
|
|
@@ -328,11 +322,11 @@ var Jumper2DBehaviorSystem = class {
|
|
|
328
322
|
const state = gameEntity.jumper2dState;
|
|
329
323
|
const previousHeld = this.wasJumpHeld.get(gameEntity.uuid) ?? false;
|
|
330
324
|
const held = input.jumpHeld === true;
|
|
331
|
-
const effectiveInput =
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
325
|
+
const effectiveInput = this.effectiveInput;
|
|
326
|
+
effectiveInput.jumpPressed = input.jumpPressed || held && !previousHeld;
|
|
327
|
+
effectiveInput.jumpHeld = held;
|
|
328
|
+
effectiveInput.jumpReleased = input.jumpReleased || !held && previousHeld;
|
|
329
|
+
effectiveInput.fastFall = input.fastFall;
|
|
336
330
|
const store = gameEntity.transformStore;
|
|
337
331
|
const ctx = {
|
|
338
332
|
dt: delta,
|
|
@@ -376,7 +370,7 @@ var Jumper2DBehaviorSystem = class {
|
|
|
376
370
|
this.attachedRuntimeSlots.clear();
|
|
377
371
|
}
|
|
378
372
|
};
|
|
379
|
-
var
|
|
373
|
+
var Jumper2DBehavior = defineBehavior({
|
|
380
374
|
name: "jumper-2d",
|
|
381
375
|
defaultOptions,
|
|
382
376
|
systemFactory: (ctx) => new Jumper2DBehaviorSystem(ctx.world, ctx.scene, ctx.wasmStage ?? null, ctx.getBehaviorLinks),
|
|
@@ -397,10 +391,10 @@ export {
|
|
|
397
391
|
createJumpInput2D,
|
|
398
392
|
createJumpState2D,
|
|
399
393
|
Jumper2DTickEvent,
|
|
400
|
-
|
|
394
|
+
Jumper2DSystem,
|
|
401
395
|
Jumper2DState,
|
|
402
396
|
Jumper2DEvent,
|
|
403
397
|
Jumper2DFSM,
|
|
404
|
-
|
|
398
|
+
Jumper2DBehavior
|
|
405
399
|
};
|
|
406
|
-
//# sourceMappingURL=chunk-
|
|
400
|
+
//# sourceMappingURL=chunk-GTLA6LOJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/behaviors/jumper-2d/components.ts","../src/lib/behaviors/jumper-2d/jumper-2d.system.ts","../src/lib/behaviors/jumper-2d/jumper-2d-fsm.ts","../src/lib/behaviors/jumper-2d/jumper-2d.descriptor.ts"],"sourcesContent":["export interface JumpConfig2D {\n\tjumpHeight: number;\n\tgravity: number;\n\tmaxFallSpeed?: number;\n\tmaxJumps: number;\n\tresetJumpsOnGround: boolean;\n\tcoyoteTimeMs: number;\n\tjumpBufferMs: number;\n\tminTimeBetweenJumpsMs?: number;\n\tvariableJump?: {\n\t\tenabled: boolean;\n\t\tcutGravityMultiplier: number;\n\t\tmaxHoldMs?: number;\n\t};\n}\n\nexport function createJumpConfig2D(\n\toptions: Partial<JumpConfig2D> = {},\n): JumpConfig2D {\n\treturn {\n\t\tjumpHeight: options.jumpHeight ?? 2.5,\n\t\tgravity: options.gravity ?? 20,\n\t\tmaxFallSpeed: options.maxFallSpeed,\n\t\tmaxJumps: options.maxJumps ?? 1,\n\t\tresetJumpsOnGround: options.resetJumpsOnGround ?? true,\n\t\tcoyoteTimeMs: options.coyoteTimeMs ?? 100,\n\t\tjumpBufferMs: options.jumpBufferMs ?? 80,\n\t\tminTimeBetweenJumpsMs: options.minTimeBetweenJumpsMs,\n\t\tvariableJump: options.variableJump,\n\t};\n}\n\nexport interface JumpInput2D {\n\tjumpPressed: boolean;\n\tjumpHeld: boolean;\n\tjumpReleased: boolean;\n\tfastFall?: boolean;\n}\n\nexport function createJumpInput2D(): JumpInput2D {\n\treturn {\n\t\tjumpPressed: false,\n\t\tjumpHeld: false,\n\t\tjumpReleased: false,\n\t\tfastFall: false,\n\t};\n}\n\nexport interface JumpContext2D {\n\tdt: number;\n\tvelocityY: number;\n\tisGrounded: boolean;\n\ttimeSinceGroundedMs: number;\n\tsetVerticalVelocity(y: number): void;\n}\n\nexport interface JumpState2D {\n\tjumpsUsed: number;\n\tlastJumpTimeMs: number;\n\tbufferedJumpMs: number;\n\tcoyoteMs: number;\n\tisJumping: boolean;\n\tjumpHoldMs: number;\n\tjumpCutApplied: boolean;\n}\n\nexport function createJumpState2D(): JumpState2D {\n\treturn {\n\t\tjumpsUsed: 0,\n\t\tlastJumpTimeMs: 0,\n\t\tbufferedJumpMs: 0,\n\t\tcoyoteMs: 0,\n\t\tisJumping: false,\n\t\tjumpHoldMs: 0,\n\t\tjumpCutApplied: false,\n\t};\n}\n","import type {\n\tJumpConfig2D,\n\tJumpContext2D,\n\tJumpInput2D,\n\tJumpState2D,\n} from './components';\n\nfunction jumpVelocityFromHeight(gravity: number, height: number): number {\n\treturn Math.sqrt(2 * gravity * height);\n}\n\nexport enum Jumper2DTickEvent {\n\tNone = 'none',\n\tJump = 'jump',\n\tFall = 'fall',\n\tLand = 'land',\n}\n\nexport interface Jumper2DTickResult {\n\tevent: Jumper2DTickEvent;\n}\n\nexport class Jumper2DSystem {\n\ttick(\n\t\tconfig: JumpConfig2D,\n\t\tinput: JumpInput2D,\n\t\tctx: JumpContext2D,\n\t\tstate: JumpState2D,\n\t): Jumper2DTickResult {\n\t\tconst dtMs = ctx.dt * 1000;\n\t\tconst now = performance.now();\n\t\tlet event = Jumper2DTickEvent.None;\n\t\tlet jumpedThisFrame = false;\n\n\t\tif (ctx.isGrounded) {\n\t\t\tstate.coyoteMs = config.coyoteTimeMs;\n\t\t\tif (config.resetJumpsOnGround) {\n\t\t\t\tstate.jumpsUsed = 0;\n\t\t\t}\n\t\t\tif (state.isJumping) {\n\t\t\t\tstate.isJumping = false;\n\t\t\t\tstate.jumpCutApplied = false;\n\t\t\t\tevent = Jumper2DTickEvent.Land;\n\t\t\t}\n\t\t} else {\n\t\t\tstate.coyoteMs = Math.max(0, state.coyoteMs - dtMs);\n\t\t}\n\n\t\tif (input.jumpPressed) {\n\t\t\tstate.bufferedJumpMs = config.jumpBufferMs;\n\t\t} else {\n\t\t\tstate.bufferedJumpMs = Math.max(0, state.bufferedJumpMs - dtMs);\n\t\t}\n\n\t\tif (state.isJumping && input.jumpHeld) {\n\t\t\tstate.jumpHoldMs += dtMs;\n\t\t}\n\n\t\tconst wantsJump = state.bufferedJumpMs > 0;\n\t\tconst isFirstJump =\n\t\t\tctx.isGrounded || (state.coyoteMs > 0 && state.jumpsUsed === 0);\n\t\tconst hasAirJumps = state.jumpsUsed > 0 && state.jumpsUsed < config.maxJumps;\n\t\tconst canJump = isFirstJump || hasAirJumps;\n\t\tconst cooldownOk =\n\t\t\tconfig.minTimeBetweenJumpsMs == null ||\n\t\t\tnow - state.lastJumpTimeMs >= config.minTimeBetweenJumpsMs;\n\n\t\tif (wantsJump && canJump && cooldownOk) {\n\t\t\tctx.setVerticalVelocity(jumpVelocityFromHeight(config.gravity, config.jumpHeight));\n\t\t\tjumpedThisFrame = true;\n\t\t\tstate.jumpsUsed++;\n\t\t\tstate.lastJumpTimeMs = now;\n\t\t\tstate.bufferedJumpMs = 0;\n\t\t\tstate.coyoteMs = 0;\n\t\t\tstate.isJumping = true;\n\t\t\tstate.jumpHoldMs = 0;\n\t\t\tstate.jumpCutApplied = false;\n\t\t\tevent = Jumper2DTickEvent.Jump;\n\t\t}\n\n\t\tif (!ctx.isGrounded || jumpedThisFrame) {\n\t\t\tlet gravityMul = 1;\n\t\t\tconst isFalling = ctx.velocityY < 0;\n\n\t\t\tif (isFalling) {\n\t\t\t\tgravityMul = input.fastFall ? 1.75 : 1;\n\t\t\t\tif (event !== Jumper2DTickEvent.Land) {\n\t\t\t\t\tevent = Jumper2DTickEvent.Fall;\n\t\t\t\t}\n\t\t\t} else if (state.isJumping) {\n\t\t\t\tconst variableJump = config.variableJump;\n\t\t\t\tif (variableJump?.enabled && !state.jumpCutApplied) {\n\t\t\t\t\tconst holdExpired =\n\t\t\t\t\t\tvariableJump.maxHoldMs != null &&\n\t\t\t\t\t\tstate.jumpHoldMs >= variableJump.maxHoldMs;\n\t\t\t\t\tif (!input.jumpHeld || holdExpired) {\n\t\t\t\t\t\tgravityMul = variableJump.cutGravityMultiplier;\n\t\t\t\t\t\tstate.jumpCutApplied = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlet newVelocityY = ctx.velocityY - config.gravity * gravityMul * ctx.dt;\n\t\t\tif (config.maxFallSpeed != null) {\n\t\t\t\tnewVelocityY = Math.max(-config.maxFallSpeed, newVelocityY);\n\t\t\t}\n\t\t\tctx.setVerticalVelocity(newVelocityY);\n\t\t} else {\n\t\t\tctx.setVerticalVelocity(0);\n\t\t}\n\n\t\treturn { event };\n\t}\n}\n","import { SyncStateMachine, t } from '../../core/utility/sync-state-machine';\nimport type { JumpState2D } from './components';\n\nexport enum Jumper2DState {\n\tGrounded = 'grounded',\n\tJumping = 'jumping',\n\tFalling = 'falling',\n}\n\nexport enum Jumper2DEvent {\n\tJump = 'jump',\n\tFall = 'fall',\n\tLand = 'land',\n}\n\nexport interface Jumper2DFSMContext {\n\tstate: JumpState2D;\n}\n\nexport class Jumper2DFSM {\n\tmachine: SyncStateMachine<Jumper2DState, Jumper2DEvent, never>;\n\n\tconstructor(private ctx: Jumper2DFSMContext) {\n\t\tthis.machine = new SyncStateMachine<Jumper2DState, Jumper2DEvent, never>(\n\t\t\tJumper2DState.Grounded,\n\t\t\t[\n\t\t\t\tt(Jumper2DState.Grounded, Jumper2DEvent.Jump, Jumper2DState.Jumping),\n\t\t\t\tt(Jumper2DState.Grounded, Jumper2DEvent.Fall, Jumper2DState.Falling),\n\t\t\t\tt(Jumper2DState.Grounded, Jumper2DEvent.Land, Jumper2DState.Grounded),\n\t\t\t\tt(Jumper2DState.Jumping, Jumper2DEvent.Fall, Jumper2DState.Falling),\n\t\t\t\tt(Jumper2DState.Jumping, Jumper2DEvent.Land, Jumper2DState.Grounded),\n\t\t\t\tt(Jumper2DState.Jumping, Jumper2DEvent.Jump, Jumper2DState.Jumping),\n\t\t\t\tt(Jumper2DState.Falling, Jumper2DEvent.Land, Jumper2DState.Grounded),\n\t\t\t\tt(Jumper2DState.Falling, Jumper2DEvent.Jump, Jumper2DState.Jumping),\n\t\t\t\tt(Jumper2DState.Falling, Jumper2DEvent.Fall, Jumper2DState.Falling),\n\t\t\t],\n\t\t);\n\t}\n\n\tgetState(): Jumper2DState {\n\t\treturn this.machine.getState();\n\t}\n\n\tdispatch(event: Jumper2DEvent): void {\n\t\tif (this.machine.can(event)) {\n\t\t\tthis.machine.syncDispatch(event);\n\t\t}\n\t}\n\n\tisJumping(): boolean {\n\t\treturn this.getState() === Jumper2DState.Jumping;\n\t}\n\n\tisGrounded(): boolean {\n\t\treturn this.getState() === Jumper2DState.Grounded;\n\t}\n\n\tgetJumpsUsed(): number {\n\t\treturn this.ctx.state.jumpsUsed;\n\t}\n\n\tapplyTickEvent(tickEvent: string, isGrounded: boolean): void {\n\t\tswitch (tickEvent) {\n\t\t\tcase 'jump':\n\t\t\t\tthis.dispatch(Jumper2DEvent.Jump);\n\t\t\t\tbreak;\n\t\t\tcase 'fall':\n\t\t\t\tthis.dispatch(Jumper2DEvent.Fall);\n\t\t\t\tbreak;\n\t\t\tcase 'land':\n\t\t\t\tthis.dispatch(Jumper2DEvent.Land);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tif (!isGrounded && this.getState() === Jumper2DState.Grounded) {\n\t\t\t\t\tthis.dispatch(Jumper2DEvent.Fall);\n\t\t\t\t}\n\t\t\t\tif (isGrounded && this.getState() !== Jumper2DState.Grounded) {\n\t\t\t\t\tthis.dispatch(Jumper2DEvent.Land);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t}\n}\n","import { defineBehavior } from '../behavior-descriptor';\nimport type { BehaviorEntityLink, BehaviorSystem } from '../behavior-system';\nimport { setVelocityIntent } from '../../capabilities/velocity-intents';\nimport {\n\tGroundProbe3D,\n\tgetGroundAnchorOffsetY,\n\tgetGroundSnapTargetY,\n\ttype GroundProbeSupportHit,\n} from '../shared/ground-probe-3d';\nimport {\n\tcreateJumpConfig2D,\n\tcreateJumpInput2D,\n\tcreateJumpState2D,\n\ttype JumpConfig2D,\n\ttype JumpContext2D,\n\ttype JumpInput2D,\n\ttype JumpState2D,\n} from './components';\nimport {\n\tJumper2DSystem,\n\tJumper2DTickEvent,\n} from './jumper-2d.system';\nimport { Jumper2DFSM, Jumper2DState } from './jumper-2d-fsm';\nimport type { WasmStageRuntime } from '../../runtime/wasm-stage-runtime';\n\nexport interface Jumper2DBehaviorOptions {\n\tjumpHeight?: number;\n\tgravity?: number;\n\tmaxFallSpeed?: number;\n\tmaxJumps?: number;\n\tresetJumpsOnGround?: boolean;\n\tcoyoteTimeMs?: number;\n\tjumpBufferMs?: number;\n\tminTimeBetweenJumpsMs?: number;\n\tvariableJump?: JumpConfig2D['variableJump'];\n\tgroundRayLength?: number;\n\tsnapToGroundDistance?: number;\n\tdebugGroundProbe?: boolean;\n}\n\nconst defaultOptions: Jumper2DBehaviorOptions = {\n\tjumpHeight: 2.5,\n\tgravity: 20,\n\tmaxJumps: 1,\n\tresetJumpsOnGround: true,\n\tcoyoteTimeMs: 100,\n\tjumpBufferMs: 80,\n\tgroundRayLength: 1,\n\tdebugGroundProbe: false,\n};\n\nconst JUMPER_2D_BEHAVIOR_KEY = Symbol.for('zylem:behavior:jumper-2d');\n\nexport function isJumper2DGrounded(params: {\n\tnearGround?: boolean;\n\tvelocityY: number;\n\tsupportToi?: number | null;\n\tsnapToGroundDistance?: number;\n}): boolean {\n\tif (params.velocityY > 0) {\n\t\treturn false;\n\t}\n\n\tif (params.snapToGroundDistance != null) {\n\t\treturn (\n\t\t\tparams.supportToi != null &&\n\t\t\tparams.supportToi <= params.snapToGroundDistance\n\t\t);\n\t}\n\n\treturn params.nearGround === true;\n}\n\nfunction shouldSnapJumper2DToGround(\n\tsupport: GroundProbeSupportHit | null,\n\toptions: Jumper2DBehaviorOptions,\n\tvelocityY: number,\n): support is GroundProbeSupportHit {\n\treturn (\n\t\tsupport != null &&\n\t\toptions.snapToGroundDistance != null &&\n\t\tvelocityY <= 0 &&\n\t\tsupport.toi <= options.snapToGroundDistance\n\t);\n}\n\nexport interface Jumper2DEntity {\n\tuuid: string;\n\tbody: import('@dimforge/rapier3d-compat').RigidBody | null;\n\ttransformStore: import('../../capabilities/transform-store').TransformState;\n\tjumper2d: JumpConfig2D;\n\t$jumper2d: JumpInput2D;\n\tjumper2dState: JumpState2D;\n}\n\nclass Jumper2DBehaviorSystem implements BehaviorSystem {\n\tprivate behavior = new Jumper2DSystem();\n\tprivate groundProbe: GroundProbe3D;\n\tprivate initializedEntities = new Set<string>();\n\tprivate timeSinceGroundedMs = new Map<string, number>();\n\tprivate wasJumpHeld = new Map<string, boolean>();\n\tprivate attachedRuntimeSlots = new Set<number>();\n\t/** Reused per-entity per-frame instead of spreading a fresh input object. */\n\tprivate effectiveInput: JumpInput2D = createJumpInput2D();\n\n\tconstructor(\n\t\tworld: import('../../collision/world').ZylemWorld | null,\n\t\tprivate scene: any,\n\t\tprivate wasmStage: WasmStageRuntime | null,\n\t\tprivate getBehaviorLinks?: (key: symbol) => Iterable<BehaviorEntityLink>,\n\t) {\n\t\tthis.groundProbe = new GroundProbe3D(world);\n\t}\n\n\tupdate(_ecs: unknown, delta: number): void {\n\t\tconst links = this.getBehaviorLinks?.(JUMPER_2D_BEHAVIOR_KEY);\n\t\tif (!links) return;\n\n\t\tfor (const link of links) {\n\t\t\tconst gameEntity = link.entity as any;\n\t\t\tconst jumperRef = link.ref as any;\n\n\t\t\tconst options = jumperRef.options as Jumper2DBehaviorOptions;\n\t\t\tconst handle = (gameEntity.runtimeHandle ?? -1) as number;\n\t\t\tif (this.wasmStage && handle >= 0) {\n\t\t\t\tthis.ensureWasmAttached(handle, options);\n\t\t\t\tconst input = gameEntity.$jumper2d as JumpInput2D | undefined;\n\t\t\t\tconst jumpPressed = !!(input?.jumpPressed || input?.jumpHeld);\n\t\t\t\tthis.wasmStage.setJumper2DInput(handle, jumpPressed);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (!gameEntity.body) continue;\n\n\t\t\tif (!gameEntity.jumper2d) {\n\t\t\t\tgameEntity.jumper2d = createJumpConfig2D(options);\n\t\t\t}\n\t\t\tif (!gameEntity.$jumper2d) {\n\t\t\t\tgameEntity.$jumper2d = createJumpInput2D();\n\t\t\t}\n\t\t\tif (!gameEntity.jumper2dState) {\n\t\t\t\tgameEntity.jumper2dState = createJumpState2D();\n\t\t\t}\n\t\t\tif (!this.initializedEntities.has(gameEntity.uuid)) {\n\t\t\t\tthis.initializedEntities.add(gameEntity.uuid);\n\t\t\t\tgameEntity.body.setGravityScale(0, true);\n\t\t\t}\n\t\t\tif (!jumperRef.fsm && gameEntity.jumper2dState) {\n\t\t\t\tjumperRef.fsm = new Jumper2DFSM({\n\t\t\t\t\tstate: gameEntity.jumper2dState,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst rayLength = options.groundRayLength ?? 1;\n\t\t\tconst bodyVelocity = gameEntity.body.linvel();\n\t\t\tconst probeOriginYOffset = 0.05 - getGroundAnchorOffsetY(gameEntity);\n\t\t\tconst support = this.groundProbe.probeSupport(gameEntity, {\n\t\t\t\trayLength,\n\t\t\t\tmode: 'any',\n\t\t\t\tdebug: options.debugGroundProbe ?? false,\n\t\t\t\tscene: this.scene,\n\t\t\t\toriginYOffset: probeOriginYOffset,\n\t\t\t});\n\t\t\tconst nearGround = support != null;\n\t\t\tconst isGrounded = isJumper2DGrounded({\n\t\t\t\tnearGround,\n\t\t\t\tvelocityY: bodyVelocity.y,\n\t\t\t\tsupportToi: support?.toi ?? null,\n\t\t\t\tsnapToGroundDistance: options.snapToGroundDistance,\n\t\t\t});\n\t\t\tif (shouldSnapJumper2DToGround(support, options, bodyVelocity.y)) {\n\t\t\t\tconst currentPosition = gameEntity.body.translation();\n\t\t\t\tgameEntity.body.setTranslation(\n\t\t\t\t\t{\n\t\t\t\t\t\tx: currentPosition.x,\n\t\t\t\t\t\ty: getGroundSnapTargetY(gameEntity, support),\n\t\t\t\t\t\tz: currentPosition.z,\n\t\t\t\t\t},\n\t\t\t\t\ttrue,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tlet timeSinceGroundedMs = this.timeSinceGroundedMs.get(gameEntity.uuid) ?? 0;\n\t\t\tif (isGrounded) {\n\t\t\t\ttimeSinceGroundedMs = 0;\n\t\t\t} else {\n\t\t\t\ttimeSinceGroundedMs += delta * 1000;\n\t\t\t}\n\t\t\tthis.timeSinceGroundedMs.set(gameEntity.uuid, timeSinceGroundedMs);\n\n\t\t\tconst config: JumpConfig2D = gameEntity.jumper2d;\n\t\t\tconst input: JumpInput2D = gameEntity.$jumper2d;\n\t\t\tconst state: JumpState2D = gameEntity.jumper2dState;\n\t\t\tconst previousHeld = this.wasJumpHeld.get(gameEntity.uuid) ?? false;\n\t\t\tconst held = input.jumpHeld === true;\n\t\t\tconst effectiveInput = this.effectiveInput;\n\t\t\teffectiveInput.jumpPressed = input.jumpPressed || (held && !previousHeld);\n\t\t\teffectiveInput.jumpHeld = held;\n\t\t\teffectiveInput.jumpReleased = input.jumpReleased || (!held && previousHeld);\n\t\t\teffectiveInput.fastFall = input.fastFall;\n\n\t\t\tconst store = gameEntity.transformStore;\n\t\t\tconst ctx: JumpContext2D = {\n\t\t\t\tdt: delta,\n\t\t\t\tvelocityY: bodyVelocity.y,\n\t\t\t\tisGrounded,\n\t\t\t\ttimeSinceGroundedMs,\n\t\t\t\tsetVerticalVelocity: (y: number) => {\n\t\t\t\t\tsetVelocityIntent(\n\t\t\t\t\t\tstore,\n\t\t\t\t\t\t'jumper-2d',\n\t\t\t\t\t\t{ y },\n\t\t\t\t\t\t{ mode: 'replace', priority: 20 },\n\t\t\t\t\t);\n\t\t\t\t\tctx.velocityY = y;\n\t\t\t\t},\n\t\t\t};\n\n\t\t\tconst result = this.behavior.tick(config, effectiveInput, ctx, state);\n\t\t\tthis.wasJumpHeld.set(gameEntity.uuid, held);\n\n\t\t\tif (jumperRef.fsm) {\n\t\t\t\t(jumperRef.fsm as Jumper2DFSM).applyTickEvent(result.event, isGrounded);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate ensureWasmAttached(handle: number, options: Jumper2DBehaviorOptions): void {\n\t\tif (!this.wasmStage || this.attachedRuntimeSlots.has(handle)) return;\n\t\tconst jumpForce = Math.sqrt(2 * (options.gravity ?? 20) * (options.jumpHeight ?? 2.5));\n\t\tthis.wasmStage.attachJumper2D(handle, {\n\t\t\tjumpForce,\n\t\t\tmaxJumps: options.maxJumps ?? 1,\n\t\t\tjumpBufferTime: (options.jumpBufferMs ?? 80) / 1000,\n\t\t\tcoyoteTime: (options.coyoteTimeMs ?? 100) / 1000,\n\t\t\tgravity: options.gravity ?? 20,\n\t\t});\n\t\tthis.attachedRuntimeSlots.add(handle);\n\t}\n\n\tdestroy(_ecs: unknown): void {\n\t\tthis.groundProbe.destroy();\n\t\tthis.initializedEntities.clear();\n\t\tthis.timeSinceGroundedMs.clear();\n\t\tthis.wasJumpHeld.clear();\n\t\tthis.attachedRuntimeSlots.clear();\n\t}\n}\n\nexport const Jumper2DBehavior = defineBehavior<\n\tJumper2DBehaviorOptions,\n\t{\n\t\tgetState: () => Jumper2DState;\n\t\tisJumping: () => boolean;\n\t\tgetJumpsUsed: () => number;\n\t\tgetJumpsRemaining: () => number;\n\t},\n\tJumper2DEntity\n>({\n\tname: 'jumper-2d',\n\tdefaultOptions,\n\tsystemFactory: (ctx) =>\n\t\tnew Jumper2DBehaviorSystem(ctx.world, ctx.scene, ctx.wasmStage ?? null, ctx.getBehaviorLinks),\n\tcreateHandle: (ref) => ({\n\t\tgetState: () =>\n\t\t\t(ref.fsm as Jumper2DFSM | undefined)?.getState() ??\n\t\t\tJumper2DState.Grounded,\n\t\tisJumping: () =>\n\t\t\t(ref.fsm as Jumper2DFSM | undefined)?.isJumping() ?? false,\n\t\tgetJumpsUsed: () =>\n\t\t\t(ref.fsm as Jumper2DFSM | undefined)?.getJumpsUsed() ?? 0,\n\t\tgetJumpsRemaining: () => {\n\t\t\tconst maxJumps = ref.options.maxJumps ?? 1;\n\t\t\tconst used =\n\t\t\t\t(ref.fsm as Jumper2DFSM | undefined)?.getJumpsUsed() ?? 0;\n\t\t\treturn maxJumps - used;\n\t\t},\n\t}),\n});\n"],"mappings":"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","names":["Jumper2DTickEvent","Jumper2DState","Jumper2DEvent","input"]}
|
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createFirstPersonInputComponent
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-QVCMJ4Q2.js";
|
|
4
4
|
import {
|
|
5
5
|
createJumpInput3D
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-4WH2ZN7P.js";
|
|
7
7
|
|
|
8
8
|
// src/lib/coordinators/first-person-shooter.coordinator.ts
|
|
9
9
|
var FirstPersonShooterCoordinator = class {
|
|
10
|
-
constructor(entity, fpsController
|
|
10
|
+
constructor(entity, fpsController) {
|
|
11
11
|
this.entity = entity;
|
|
12
12
|
this.fpsController = fpsController;
|
|
13
|
-
this.jumper = jumper;
|
|
14
13
|
}
|
|
15
14
|
entity;
|
|
16
15
|
fpsController;
|
|
17
|
-
jumper;
|
|
18
16
|
update(input) {
|
|
19
17
|
if (!this.entity.$fps) {
|
|
20
18
|
this.entity.$fps = createFirstPersonInputComponent();
|
|
@@ -38,7 +36,6 @@ var FirstPersonShooterCoordinator = class {
|
|
|
38
36
|
input.moveZ,
|
|
39
37
|
this.fpsController.getYaw()
|
|
40
38
|
);
|
|
41
|
-
void this.jumper.getState();
|
|
42
39
|
}
|
|
43
40
|
getMoveDirWorld(moveX, moveZ, yaw) {
|
|
44
41
|
const sinYaw = Math.sin(yaw);
|
|
@@ -64,4 +61,4 @@ var FirstPersonShooterCoordinator = class {
|
|
|
64
61
|
export {
|
|
65
62
|
FirstPersonShooterCoordinator
|
|
66
63
|
};
|
|
67
|
-
//# sourceMappingURL=chunk-
|
|
64
|
+
//# sourceMappingURL=chunk-H4TJ7X4O.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/coordinators/first-person-shooter.coordinator.ts"],"sourcesContent":["import {\n\tcreateFirstPersonInputComponent,\n\ttype FirstPersonInputComponent,\n} from '../behaviors/first-person/components';\nimport {\n\tcreateJumpInput3D,\n\ttype JumpInput3D,\n} from '../behaviors/jumper-3d/components';\n\nexport interface FirstPersonShooterInput {\n\tmoveX: number;\n\tmoveZ: number;\n\tlookX: number;\n\tlookY: number;\n\tsprint: boolean;\n\tjumpPressed: boolean;\n\tjumpHeld: boolean;\n\tjumpReleased: boolean;\n\tfastFall?: boolean;\n}\n\ninterface FirstPersonShooterControllerHandle {\n\tgetYaw(): number;\n}\n\nexport type FirstPersonShooterEntity = {\n\t$fps?: FirstPersonInputComponent;\n\t$jumper?: JumpInput3D;\n};\n\n/**\n * Coordinator that fans a single input snapshot out to the first-person\n * behavior (`$fps`) and the Jumper3D behavior (`$jumper`), converting local\n * move axes into the camera-relative world direction the jumper uses for\n * air control.\n */\nexport class FirstPersonShooterCoordinator {\n\tconstructor(\n\t\tprivate entity: FirstPersonShooterEntity,\n\t\tprivate fpsController: FirstPersonShooterControllerHandle,\n\t) {}\n\n\tpublic update(input: FirstPersonShooterInput): void {\n\t\tif (!this.entity.$fps) {\n\t\t\tthis.entity.$fps = createFirstPersonInputComponent();\n\t\t}\n\t\tif (!this.entity.$jumper) {\n\t\t\tthis.entity.$jumper = createJumpInput3D();\n\t\t}\n\n\t\tconst fpsInput = this.entity.$fps;\n\t\tfpsInput.moveX = input.moveX;\n\t\tfpsInput.moveZ = input.moveZ;\n\t\tfpsInput.lookX = input.lookX;\n\t\tfpsInput.lookY = input.lookY;\n\t\tfpsInput.sprint = input.sprint;\n\n\t\tconst jumperInput = this.entity.$jumper;\n\t\tjumperInput.jumpPressed = input.jumpPressed;\n\t\tjumperInput.jumpHeld = input.jumpHeld;\n\t\tjumperInput.jumpReleased = input.jumpReleased;\n\t\tjumperInput.fastFall = input.fastFall ?? false;\n\t\tjumperInput.moveDirWorld = this.getMoveDirWorld(\n\t\t\tinput.moveX,\n\t\t\tinput.moveZ,\n\t\t\tthis.fpsController.getYaw(),\n\t\t);\n\t}\n\n\tprivate getMoveDirWorld(\n\t\tmoveX: number,\n\t\tmoveZ: number,\n\t\tyaw: number,\n\t): { x: number; y: number; z: number } | undefined {\n\t\tconst sinYaw = Math.sin(yaw);\n\t\tconst cosYaw = Math.cos(yaw);\n\t\tconst rightX = cosYaw;\n\t\tconst rightZ = -sinYaw;\n\t\tconst forwardX = -sinYaw;\n\t\tconst forwardZ = -cosYaw;\n\t\tconst worldX = rightX * moveX + forwardX * -moveZ;\n\t\tconst worldZ = rightZ * moveX + forwardZ * -moveZ;\n\t\tconst length = Math.hypot(worldX, worldZ);\n\t\tif (length <= 1e-6) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\treturn {\n\t\t\tx: worldX / length,\n\t\t\ty: 0,\n\t\t\tz: worldZ / length,\n\t\t};\n\t}\n}\n"],"mappings":";;;;;;;;AAoCO,IAAM,gCAAN,MAAoC;AAAA,EAC1C,YACS,QACA,eACP;AAFO;AACA;AAAA,EACN;AAAA,EAFM;AAAA,EACA;AAAA,EAGF,OAAO,OAAsC;AACnD,QAAI,CAAC,KAAK,OAAO,MAAM;AACtB,WAAK,OAAO,OAAO,gCAAgC;AAAA,IACpD;AACA,QAAI,CAAC,KAAK,OAAO,SAAS;AACzB,WAAK,OAAO,UAAU,kBAAkB;AAAA,IACzC;AAEA,UAAM,WAAW,KAAK,OAAO;AAC7B,aAAS,QAAQ,MAAM;AACvB,aAAS,QAAQ,MAAM;AACvB,aAAS,QAAQ,MAAM;AACvB,aAAS,QAAQ,MAAM;AACvB,aAAS,SAAS,MAAM;AAExB,UAAM,cAAc,KAAK,OAAO;AAChC,gBAAY,cAAc,MAAM;AAChC,gBAAY,WAAW,MAAM;AAC7B,gBAAY,eAAe,MAAM;AACjC,gBAAY,WAAW,MAAM,YAAY;AACzC,gBAAY,eAAe,KAAK;AAAA,MAC/B,MAAM;AAAA,MACN,MAAM;AAAA,MACN,KAAK,cAAc,OAAO;AAAA,IAC3B;AAAA,EACD;AAAA,EAEQ,gBACP,OACA,OACA,KACkD;AAClD,UAAM,SAAS,KAAK,IAAI,GAAG;AAC3B,UAAM,SAAS,KAAK,IAAI,GAAG;AAC3B,UAAM,SAAS;AACf,UAAM,SAAS,CAAC;AAChB,UAAM,WAAW,CAAC;AAClB,UAAM,WAAW,CAAC;AAClB,UAAM,SAAS,SAAS,QAAQ,WAAW,CAAC;AAC5C,UAAM,SAAS,SAAS,QAAQ,WAAW,CAAC;AAC5C,UAAM,SAAS,KAAK,MAAM,QAAQ,MAAM;AACxC,QAAI,UAAU,MAAM;AACnB,aAAO;AAAA,IACR;AAEA,WAAO;AAAA,MACN,GAAG,SAAS;AAAA,MACZ,GAAG;AAAA,MACH,GAAG,SAAS;AAAA,IACb;AAAA,EACD;AACD;","names":[]}
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from "./chunk-OHCPV2NT.js";
|
|
8
8
|
import {
|
|
9
9
|
StageBoundaryDim
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-4V7TIRO2.js";
|
|
11
11
|
import {
|
|
12
12
|
SyncStateMachine,
|
|
13
13
|
t
|
|
@@ -135,12 +135,10 @@ function createWorldBoundary3DHandle(ref) {
|
|
|
135
135
|
};
|
|
136
136
|
}
|
|
137
137
|
var WorldBoundary3DSystem = class {
|
|
138
|
-
constructor(
|
|
139
|
-
this.world = world;
|
|
138
|
+
constructor(wasmStage, getBehaviorLinks) {
|
|
140
139
|
this.wasmStage = wasmStage;
|
|
141
140
|
this.getBehaviorLinks = getBehaviorLinks;
|
|
142
141
|
}
|
|
143
|
-
world;
|
|
144
142
|
wasmStage;
|
|
145
143
|
getBehaviorLinks;
|
|
146
144
|
attachedRuntimeSlots = /* @__PURE__ */ new Set();
|
|
@@ -197,7 +195,7 @@ var WorldBoundary3DSystem = class {
|
|
|
197
195
|
var WorldBoundary3DBehavior = defineBehavior({
|
|
198
196
|
name: "world-boundary-3d",
|
|
199
197
|
defaultOptions,
|
|
200
|
-
systemFactory: (ctx) => new WorldBoundary3DSystem(ctx.
|
|
198
|
+
systemFactory: (ctx) => new WorldBoundary3DSystem(ctx.wasmStage ?? null, ctx.getBehaviorLinks),
|
|
201
199
|
createHandle: createWorldBoundary3DHandle
|
|
202
200
|
});
|
|
203
201
|
|
|
@@ -209,4 +207,4 @@ export {
|
|
|
209
207
|
WorldBoundary3DFSM,
|
|
210
208
|
WorldBoundary3DBehavior
|
|
211
209
|
};
|
|
212
|
-
//# sourceMappingURL=chunk-
|
|
210
|
+
//# sourceMappingURL=chunk-HNESASBB.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/behaviors/world-boundary-3d/world-boundary-3d-fsm.ts","../src/lib/behaviors/world-boundary-3d/world-boundary-3d.descriptor.ts"],"sourcesContent":["import { SyncStateMachine, t } from '../../core/utility/sync-state-machine';\nimport {\n\tclampPointToBounds3D,\n\tcomputeBounds3DHits,\n\tconstrainMovementToBounds3D,\n\thasAnyBounds3DHit,\n\tinsetBounds3D,\n\ttype Bounds3DPaddingInput,\n\ttype Bounds3DPoint,\n} from '../shared/bounds-3d';\n\nexport type WorldBoundary3DHit =\n\t| 'top'\n\t| 'bottom'\n\t| 'left'\n\t| 'right'\n\t| 'back'\n\t| 'front';\n\nexport type WorldBoundary3DHits = Record<WorldBoundary3DHit, boolean>;\n\nexport interface WorldBoundary3DPosition extends Bounds3DPoint {}\n\nexport interface WorldBoundary3DBounds {\n\ttop: number;\n\tbottom: number;\n\tleft: number;\n\tright: number;\n\tback: number;\n\tfront: number;\n}\n\nexport enum WorldBoundary3DState {\n\tInside = 'inside',\n\tTouching = 'touching',\n}\n\nexport enum WorldBoundary3DEvent {\n\tEnterInside = 'enter-inside',\n\tTouchBoundary = 'touch-boundary',\n}\n\nexport function computeWorldBoundary3DHits(\n\tposition: WorldBoundary3DPosition,\n\tbounds: WorldBoundary3DBounds,\n\tpadding?: Bounds3DPaddingInput,\n): WorldBoundary3DHits {\n\treturn computeBounds3DHits(position, insetBounds3D({\n\t\tminX: bounds.left,\n\t\tmaxX: bounds.right,\n\t\tminY: bounds.bottom,\n\t\tmaxY: bounds.top,\n\t\tminZ: bounds.back,\n\t\tmaxZ: bounds.front,\n\t}, padding));\n}\n\nexport function hasAnyWorldBoundary3DHit(hits: WorldBoundary3DHits): boolean {\n\treturn hasAnyBounds3DHit(hits);\n}\n\nexport class WorldBoundary3DFSM {\n\tpublic readonly machine: SyncStateMachine<WorldBoundary3DState, WorldBoundary3DEvent, never>;\n\n\tprivate lastHits: WorldBoundary3DHits = {\n\t\ttop: false,\n\t\tbottom: false,\n\t\tleft: false,\n\t\tright: false,\n\t\tback: false,\n\t\tfront: false,\n\t};\n\tprivate lastPosition: WorldBoundary3DPosition | null = null;\n\tprivate lastClampedPosition: WorldBoundary3DPosition | null = null;\n\tprivate lastUpdatedAtMs: number | null = null;\n\n\tconstructor() {\n\t\tthis.machine = new SyncStateMachine<WorldBoundary3DState, WorldBoundary3DEvent, never>(\n\t\t\tWorldBoundary3DState.Inside,\n\t\t\t[\n\t\t\t\tt(WorldBoundary3DState.Inside, WorldBoundary3DEvent.TouchBoundary, WorldBoundary3DState.Touching),\n\t\t\t\tt(WorldBoundary3DState.Touching, WorldBoundary3DEvent.EnterInside, WorldBoundary3DState.Inside),\n\t\t\t\tt(WorldBoundary3DState.Inside, WorldBoundary3DEvent.EnterInside, WorldBoundary3DState.Inside),\n\t\t\t\tt(WorldBoundary3DState.Touching, WorldBoundary3DEvent.TouchBoundary, WorldBoundary3DState.Touching),\n\t\t\t],\n\t\t);\n\t}\n\n\tgetState(): WorldBoundary3DState {\n\t\treturn this.machine.getState();\n\t}\n\n\tgetLastHits(): WorldBoundary3DHits {\n\t\treturn this.lastHits;\n\t}\n\n\tgetMovement(\n\t\tmoveX: number,\n\t\tmoveY: number,\n\t\tmoveZ: number,\n\t): { moveX: number; moveY: number; moveZ: number } {\n\t\treturn constrainMovementToBounds3D(this.lastHits, moveX, moveY, moveZ);\n\t}\n\n\tgetLastPosition(): WorldBoundary3DPosition | null {\n\t\treturn this.lastPosition;\n\t}\n\n\tgetLastClampedPosition(): WorldBoundary3DPosition | null {\n\t\treturn this.lastClampedPosition;\n\t}\n\n\tgetLastUpdatedAtMs(): number | null {\n\t\treturn this.lastUpdatedAtMs;\n\t}\n\n\tupdate(\n\t\tposition: WorldBoundary3DPosition,\n\t\tbounds: WorldBoundary3DBounds,\n\t\tpadding?: Bounds3DPaddingInput,\n\t): WorldBoundary3DHits {\n\t\tconst effectiveBounds = insetBounds3D({\n\t\t\tminX: bounds.left,\n\t\t\tmaxX: bounds.right,\n\t\t\tminY: bounds.bottom,\n\t\t\tmaxY: bounds.top,\n\t\t\tminZ: bounds.back,\n\t\t\tmaxZ: bounds.front,\n\t\t}, padding);\n\n\t\tconst hits = computeBounds3DHits(position, effectiveBounds);\n\t\tthis.lastHits = hits;\n\t\tthis.lastPosition = { x: position.x, y: position.y, z: position.z };\n\t\tthis.lastClampedPosition = clampPointToBounds3D(position, effectiveBounds);\n\t\tthis.lastUpdatedAtMs = Date.now();\n\n\t\tif (hasAnyWorldBoundary3DHit(hits)) {\n\t\t\tthis.dispatch(WorldBoundary3DEvent.TouchBoundary);\n\t\t} else {\n\t\t\tthis.dispatch(WorldBoundary3DEvent.EnterInside);\n\t\t}\n\n\t\treturn hits;\n\t}\n\n\tprivate dispatch(event: WorldBoundary3DEvent): void {\n\t\tif (this.machine.can(event)) {\n\t\t\tthis.machine.syncDispatch(event);\n\t\t}\n\t}\n}\n","import { defineBehavior, type BehaviorRef } from '../behavior-descriptor';\nimport type { BehaviorEntityLink, BehaviorSystem } from '../behavior-system';\nimport type { Bounds3DPaddingInput } from '../shared/bounds-3d';\nimport {\n\tWorldBoundary3DFSM,\n\ttype WorldBoundary3DBounds,\n\ttype WorldBoundary3DHits,\n\ttype WorldBoundary3DPosition,\n} from './world-boundary-3d-fsm';\nimport {\n\tStageBoundaryDim,\n\ttype WasmStageRuntime,\n} from '../../runtime/wasm-stage-runtime';\n\nexport interface WorldBoundary3DOptions {\n\tboundaries: WorldBoundary3DBounds;\n\tpadding?: Bounds3DPaddingInput;\n}\n\nexport interface WorldBoundary3DHandle {\n\tgetLastHits(): WorldBoundary3DHits | null;\n\tgetMovement(\n\t\tmoveX: number,\n\t\tmoveY: number,\n\t\tmoveZ: number,\n\t): { moveX: number; moveY: number; moveZ: number };\n\tgetLastClampedPosition(): WorldBoundary3DPosition | null;\n}\n\nconst defaultOptions: WorldBoundary3DOptions = {\n\tboundaries: { top: 0, bottom: 0, left: 0, right: 0, back: 0, front: 0 },\n\tpadding: 0,\n};\n\nconst WORLD_BOUNDARY_3D_BEHAVIOR_KEY = Symbol.for(\n\t'zylem:behavior:world-boundary-3d',\n);\n\nfunction createWorldBoundary3DHandle(\n\tref: BehaviorRef<WorldBoundary3DOptions>,\n): WorldBoundary3DHandle {\n\treturn {\n\t\tgetLastHits: () => {\n\t\t\tconst fsm = ref.fsm as WorldBoundary3DFSM | undefined;\n\t\t\treturn fsm?.getLastHits() ?? null;\n\t\t},\n\t\tgetMovement: (moveX: number, moveY: number, moveZ: number) => {\n\t\t\tconst fsm = ref.fsm as WorldBoundary3DFSM | undefined;\n\t\t\treturn fsm?.getMovement(moveX, moveY, moveZ) ?? { moveX, moveY, moveZ };\n\t\t},\n\t\tgetLastClampedPosition: () => {\n\t\t\tconst fsm = ref.fsm as WorldBoundary3DFSM | undefined;\n\t\t\treturn fsm?.getLastClampedPosition() ?? null;\n\t\t},\n\t};\n}\n\nclass WorldBoundary3DSystem implements BehaviorSystem {\n\tprivate attachedRuntimeSlots = new Set<number>();\n\n\tconstructor(\n\t\tprivate world: any,\n\t\tprivate wasmStage: WasmStageRuntime | null,\n\t\tprivate getBehaviorLinks?: (key: symbol) => Iterable<BehaviorEntityLink>,\n\t) {}\n\n\tupdate(_ecs: unknown, _delta: number): void {\n\t\tconst links = this.getBehaviorLinks?.(WORLD_BOUNDARY_3D_BEHAVIOR_KEY);\n\t\tif (!links) return;\n\n\t\tfor (const link of links) {\n\t\t\tconst gameEntity = link.entity as any;\n\t\t\tconst boundaryRef = link.ref as any;\n\t\t\tconst options = boundaryRef.options as WorldBoundary3DOptions;\n\t\t\tif (!boundaryRef.fsm) {\n\t\t\t\tboundaryRef.fsm = new WorldBoundary3DFSM();\n\t\t\t}\n\n\t\t\tconst handle = (gameEntity.runtimeHandle ?? -1) as number;\n\t\t\tif (this.wasmStage && handle >= 0) {\n\t\t\t\tthis.ensureWasmAttached(handle, options);\n\t\t\t\tconst query = this.wasmStage.queryWorldBoundary(handle);\n\t\t\t\tif (query) {\n\t\t\t\t\tboundaryRef.fsm.update(\n\t\t\t\t\t\t{ x: query.clamped[0], y: query.clamped[1], z: query.clamped[2] },\n\t\t\t\t\t\toptions.boundaries,\n\t\t\t\t\t\toptions.padding,\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!gameEntity.body) continue;\n\t\t\tconst pos = gameEntity.body.translation();\n\t\t\tboundaryRef.fsm.update(\n\t\t\t\t{ x: pos.x, y: pos.y, z: pos.z },\n\t\t\t\toptions.boundaries,\n\t\t\t\toptions.padding,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate ensureWasmAttached(handle: number, options: WorldBoundary3DOptions): void {\n\t\tif (!this.wasmStage || this.attachedRuntimeSlots.has(handle)) return;\n\t\tconst padding = typeof options.padding === 'number' ? options.padding : 0;\n\t\tthis.wasmStage.attachWorldBoundary(handle, StageBoundaryDim.Three, {\n\t\t\ttop: options.boundaries.top,\n\t\t\tbottom: options.boundaries.bottom,\n\t\t\tleft: options.boundaries.left,\n\t\t\tright: options.boundaries.right,\n\t\t\tfront: options.boundaries.front,\n\t\t\tback: options.boundaries.back,\n\t\t\tpadding,\n\t\t});\n\t\tthis.attachedRuntimeSlots.add(handle);\n\t}\n\n\tdestroy(_ecs: unknown): void {\n\t\tthis.attachedRuntimeSlots.clear();\n\t}\n}\n\nexport const WorldBoundary3DBehavior = defineBehavior({\n\tname: 'world-boundary-3d',\n\tdefaultOptions,\n\tsystemFactory: (ctx) =>\n\t\tnew WorldBoundary3DSystem(ctx.world, ctx.wasmStage ?? null, ctx.getBehaviorLinks),\n\tcreateHandle: createWorldBoundary3DHandle,\n});\n"],"mappings":"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","names":["WorldBoundary3DState","WorldBoundary3DEvent"]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/behaviors/world-boundary-3d/world-boundary-3d-fsm.ts","../src/lib/behaviors/world-boundary-3d/world-boundary-3d.descriptor.ts"],"sourcesContent":["import { SyncStateMachine, t } from '../../core/utility/sync-state-machine';\nimport {\n\tclampPointToBounds3D,\n\tcomputeBounds3DHits,\n\tconstrainMovementToBounds3D,\n\thasAnyBounds3DHit,\n\tinsetBounds3D,\n\ttype Bounds3DPaddingInput,\n\ttype Bounds3DPoint,\n} from '../shared/bounds-3d';\n\nexport type WorldBoundary3DHit =\n\t| 'top'\n\t| 'bottom'\n\t| 'left'\n\t| 'right'\n\t| 'back'\n\t| 'front';\n\nexport type WorldBoundary3DHits = Record<WorldBoundary3DHit, boolean>;\n\nexport interface WorldBoundary3DPosition extends Bounds3DPoint {}\n\nexport interface WorldBoundary3DBounds {\n\ttop: number;\n\tbottom: number;\n\tleft: number;\n\tright: number;\n\tback: number;\n\tfront: number;\n}\n\nexport enum WorldBoundary3DState {\n\tInside = 'inside',\n\tTouching = 'touching',\n}\n\nexport enum WorldBoundary3DEvent {\n\tEnterInside = 'enter-inside',\n\tTouchBoundary = 'touch-boundary',\n}\n\nexport function computeWorldBoundary3DHits(\n\tposition: WorldBoundary3DPosition,\n\tbounds: WorldBoundary3DBounds,\n\tpadding?: Bounds3DPaddingInput,\n): WorldBoundary3DHits {\n\treturn computeBounds3DHits(position, insetBounds3D({\n\t\tminX: bounds.left,\n\t\tmaxX: bounds.right,\n\t\tminY: bounds.bottom,\n\t\tmaxY: bounds.top,\n\t\tminZ: bounds.back,\n\t\tmaxZ: bounds.front,\n\t}, padding));\n}\n\nexport function hasAnyWorldBoundary3DHit(hits: WorldBoundary3DHits): boolean {\n\treturn hasAnyBounds3DHit(hits);\n}\n\nexport class WorldBoundary3DFSM {\n\tpublic readonly machine: SyncStateMachine<WorldBoundary3DState, WorldBoundary3DEvent, never>;\n\n\tprivate lastHits: WorldBoundary3DHits = {\n\t\ttop: false,\n\t\tbottom: false,\n\t\tleft: false,\n\t\tright: false,\n\t\tback: false,\n\t\tfront: false,\n\t};\n\tprivate lastPosition: WorldBoundary3DPosition | null = null;\n\tprivate lastClampedPosition: WorldBoundary3DPosition | null = null;\n\tprivate lastUpdatedAtMs: number | null = null;\n\n\tconstructor() {\n\t\tthis.machine = new SyncStateMachine<WorldBoundary3DState, WorldBoundary3DEvent, never>(\n\t\t\tWorldBoundary3DState.Inside,\n\t\t\t[\n\t\t\t\tt(WorldBoundary3DState.Inside, WorldBoundary3DEvent.TouchBoundary, WorldBoundary3DState.Touching),\n\t\t\t\tt(WorldBoundary3DState.Touching, WorldBoundary3DEvent.EnterInside, WorldBoundary3DState.Inside),\n\t\t\t\tt(WorldBoundary3DState.Inside, WorldBoundary3DEvent.EnterInside, WorldBoundary3DState.Inside),\n\t\t\t\tt(WorldBoundary3DState.Touching, WorldBoundary3DEvent.TouchBoundary, WorldBoundary3DState.Touching),\n\t\t\t],\n\t\t);\n\t}\n\n\tgetState(): WorldBoundary3DState {\n\t\treturn this.machine.getState();\n\t}\n\n\tgetLastHits(): WorldBoundary3DHits {\n\t\treturn this.lastHits;\n\t}\n\n\tgetMovement(\n\t\tmoveX: number,\n\t\tmoveY: number,\n\t\tmoveZ: number,\n\t): { moveX: number; moveY: number; moveZ: number } {\n\t\treturn constrainMovementToBounds3D(this.lastHits, moveX, moveY, moveZ);\n\t}\n\n\tgetLastPosition(): WorldBoundary3DPosition | null {\n\t\treturn this.lastPosition;\n\t}\n\n\tgetLastClampedPosition(): WorldBoundary3DPosition | null {\n\t\treturn this.lastClampedPosition;\n\t}\n\n\tgetLastUpdatedAtMs(): number | null {\n\t\treturn this.lastUpdatedAtMs;\n\t}\n\n\tupdate(\n\t\tposition: WorldBoundary3DPosition,\n\t\tbounds: WorldBoundary3DBounds,\n\t\tpadding?: Bounds3DPaddingInput,\n\t): WorldBoundary3DHits {\n\t\tconst effectiveBounds = insetBounds3D({\n\t\t\tminX: bounds.left,\n\t\t\tmaxX: bounds.right,\n\t\t\tminY: bounds.bottom,\n\t\t\tmaxY: bounds.top,\n\t\t\tminZ: bounds.back,\n\t\t\tmaxZ: bounds.front,\n\t\t}, padding);\n\n\t\tconst hits = computeBounds3DHits(position, effectiveBounds);\n\t\tthis.lastHits = hits;\n\t\tthis.lastPosition = { x: position.x, y: position.y, z: position.z };\n\t\tthis.lastClampedPosition = clampPointToBounds3D(position, effectiveBounds);\n\t\tthis.lastUpdatedAtMs = Date.now();\n\n\t\tif (hasAnyWorldBoundary3DHit(hits)) {\n\t\t\tthis.dispatch(WorldBoundary3DEvent.TouchBoundary);\n\t\t} else {\n\t\t\tthis.dispatch(WorldBoundary3DEvent.EnterInside);\n\t\t}\n\n\t\treturn hits;\n\t}\n\n\tprivate dispatch(event: WorldBoundary3DEvent): void {\n\t\tif (this.machine.can(event)) {\n\t\t\tthis.machine.syncDispatch(event);\n\t\t}\n\t}\n}\n","import { defineBehavior, type BehaviorRef } from '../behavior-descriptor';\nimport type { BehaviorEntityLink, BehaviorSystem } from '../behavior-system';\nimport type { Bounds3DPaddingInput } from '../shared/bounds-3d';\nimport {\n\tWorldBoundary3DFSM,\n\ttype WorldBoundary3DBounds,\n\ttype WorldBoundary3DHits,\n\ttype WorldBoundary3DPosition,\n} from './world-boundary-3d-fsm';\nimport {\n\tStageBoundaryDim,\n\ttype WasmStageRuntime,\n} from '../../runtime/wasm-stage-runtime';\n\nexport interface WorldBoundary3DOptions {\n\tboundaries: WorldBoundary3DBounds;\n\tpadding?: Bounds3DPaddingInput;\n}\n\nexport interface WorldBoundary3DHandle {\n\tgetLastHits(): WorldBoundary3DHits | null;\n\tgetMovement(\n\t\tmoveX: number,\n\t\tmoveY: number,\n\t\tmoveZ: number,\n\t): { moveX: number; moveY: number; moveZ: number };\n\tgetLastClampedPosition(): WorldBoundary3DPosition | null;\n}\n\nconst defaultOptions: WorldBoundary3DOptions = {\n\tboundaries: { top: 0, bottom: 0, left: 0, right: 0, back: 0, front: 0 },\n\tpadding: 0,\n};\n\nconst WORLD_BOUNDARY_3D_BEHAVIOR_KEY = Symbol.for(\n\t'zylem:behavior:world-boundary-3d',\n);\n\nfunction createWorldBoundary3DHandle(\n\tref: BehaviorRef<WorldBoundary3DOptions>,\n): WorldBoundary3DHandle {\n\treturn {\n\t\tgetLastHits: () => {\n\t\t\tconst fsm = ref.fsm as WorldBoundary3DFSM | undefined;\n\t\t\treturn fsm?.getLastHits() ?? null;\n\t\t},\n\t\tgetMovement: (moveX: number, moveY: number, moveZ: number) => {\n\t\t\tconst fsm = ref.fsm as WorldBoundary3DFSM | undefined;\n\t\t\treturn fsm?.getMovement(moveX, moveY, moveZ) ?? { moveX, moveY, moveZ };\n\t\t},\n\t\tgetLastClampedPosition: () => {\n\t\t\tconst fsm = ref.fsm as WorldBoundary3DFSM | undefined;\n\t\t\treturn fsm?.getLastClampedPosition() ?? null;\n\t\t},\n\t};\n}\n\nclass WorldBoundary3DSystem implements BehaviorSystem {\n\tprivate attachedRuntimeSlots = new Set<number>();\n\n\tconstructor(\n\t\tprivate wasmStage: WasmStageRuntime | null,\n\t\tprivate getBehaviorLinks?: (key: symbol) => Iterable<BehaviorEntityLink>,\n\t) {}\n\n\tupdate(_ecs: unknown, _delta: number): void {\n\t\tconst links = this.getBehaviorLinks?.(WORLD_BOUNDARY_3D_BEHAVIOR_KEY);\n\t\tif (!links) return;\n\n\t\tfor (const link of links) {\n\t\t\tconst gameEntity = link.entity as any;\n\t\t\tconst boundaryRef = link.ref as any;\n\t\t\tconst options = boundaryRef.options as WorldBoundary3DOptions;\n\t\t\tif (!boundaryRef.fsm) {\n\t\t\t\tboundaryRef.fsm = new WorldBoundary3DFSM();\n\t\t\t}\n\n\t\t\tconst handle = (gameEntity.runtimeHandle ?? -1) as number;\n\t\t\tif (this.wasmStage && handle >= 0) {\n\t\t\t\tthis.ensureWasmAttached(handle, options);\n\t\t\t\tconst query = this.wasmStage.queryWorldBoundary(handle);\n\t\t\t\tif (query) {\n\t\t\t\t\tboundaryRef.fsm.update(\n\t\t\t\t\t\t{ x: query.clamped[0], y: query.clamped[1], z: query.clamped[2] },\n\t\t\t\t\t\toptions.boundaries,\n\t\t\t\t\t\toptions.padding,\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!gameEntity.body) continue;\n\t\t\tconst pos = gameEntity.body.translation();\n\t\t\tboundaryRef.fsm.update(\n\t\t\t\t{ x: pos.x, y: pos.y, z: pos.z },\n\t\t\t\toptions.boundaries,\n\t\t\t\toptions.padding,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate ensureWasmAttached(handle: number, options: WorldBoundary3DOptions): void {\n\t\tif (!this.wasmStage || this.attachedRuntimeSlots.has(handle)) return;\n\t\tconst padding = typeof options.padding === 'number' ? options.padding : 0;\n\t\tthis.wasmStage.attachWorldBoundary(handle, StageBoundaryDim.Three, {\n\t\t\ttop: options.boundaries.top,\n\t\t\tbottom: options.boundaries.bottom,\n\t\t\tleft: options.boundaries.left,\n\t\t\tright: options.boundaries.right,\n\t\t\tfront: options.boundaries.front,\n\t\t\tback: options.boundaries.back,\n\t\t\tpadding,\n\t\t});\n\t\tthis.attachedRuntimeSlots.add(handle);\n\t}\n\n\tdestroy(_ecs: unknown): void {\n\t\tthis.attachedRuntimeSlots.clear();\n\t}\n}\n\nexport const WorldBoundary3DBehavior = defineBehavior({\n\tname: 'world-boundary-3d',\n\tdefaultOptions,\n\tsystemFactory: (ctx) =>\n\t\tnew WorldBoundary3DSystem(ctx.wasmStage ?? null, ctx.getBehaviorLinks),\n\tcreateHandle: createWorldBoundary3DHandle,\n});\n"],"mappings":"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","names":["WorldBoundary3DState","WorldBoundary3DEvent"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/behaviors/thruster/components.ts"],"sourcesContent":["/**\n * Thruster-specific ECS Components\n * \n * These components are specific to the thruster movement system.\n */\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ThrusterMovementComponent (capability / hardware spec)\n// Defines the thrust capabilities of an entity\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ThrusterMovementComponent {\n\t/** Linear thrust force in Newtons (or scaled units) */\n\tlinearThrust: number;\n\t/** Angular thrust torque scalar */\n\tangularThrust: number;\n\t/** Optional linear damping override */\n\tlinearDamping?: number;\n\t/** Optional angular damping override */\n\tangularDamping?: number;\n}\n\nexport function createThrusterMovementComponent(\n\tlinearThrust: number,\n\tangularThrust: number,\n\toptions?: { linearDamping?: number; angularDamping?: number }\n): ThrusterMovementComponent {\n\treturn {\n\t\tlinearThrust,\n\t\tangularThrust,\n\t\tlinearDamping: options?.linearDamping,\n\t\tangularDamping: options?.angularDamping,\n\t};\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ThrusterInputComponent (intent)\n// Written by: Player controller, AI controller, FSM adapter\n// Read by:
|
|
1
|
+
{"version":3,"sources":["../src/lib/behaviors/thruster/components.ts"],"sourcesContent":["/**\n * Thruster-specific ECS Components\n * \n * These components are specific to the thruster movement system.\n */\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ThrusterMovementComponent (capability / hardware spec)\n// Defines the thrust capabilities of an entity\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ThrusterMovementComponent {\n\t/** Linear thrust force in Newtons (or scaled units) */\n\tlinearThrust: number;\n\t/** Angular thrust torque scalar */\n\tangularThrust: number;\n\t/** Optional linear damping override */\n\tlinearDamping?: number;\n\t/** Optional angular damping override */\n\tangularDamping?: number;\n}\n\nexport function createThrusterMovementComponent(\n\tlinearThrust: number,\n\tangularThrust: number,\n\toptions?: { linearDamping?: number; angularDamping?: number }\n): ThrusterMovementComponent {\n\treturn {\n\t\tlinearThrust,\n\t\tangularThrust,\n\t\tlinearDamping: options?.linearDamping,\n\t\tangularDamping: options?.angularDamping,\n\t};\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ThrusterInputComponent (intent)\n// Written by: Player controller, AI controller, FSM adapter\n// Read by: ThrusterSystem\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ThrusterInputComponent {\n\t/** Forward thrust intent: 0..1 */\n\tthrust: number;\n\t/** Rotation intent: -1..1 */\n\trotate: number;\n\t/** World-space X thrust intent: -1..1 */\n\tthrustX: number;\n\t/** World-space Y thrust intent: -1..1 */\n\tthrustY: number;\n}\n\nexport function createThrusterInputComponent(): ThrusterInputComponent {\n\treturn {\n\t\tthrust: 0,\n\t\trotate: 0,\n\t\tthrustX: 0,\n\t\tthrustY: 0,\n\t};\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ThrusterStateComponent (optional but useful)\n// Useful for: damage, EMP, overheating, UI feedback\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ThrusterStateComponent {\n\t/** Whether the thruster is enabled */\n\tenabled: boolean;\n\t/** Current thrust after FSM/gating */\n\tcurrentThrust: number;\n}\n\nexport function createThrusterStateComponent(): ThrusterStateComponent {\n\treturn {\n\t\tenabled: true,\n\t\tcurrentThrust: 0,\n\t};\n}\n"],"mappings":";AAsBO,SAAS,gCACf,cACA,eACA,SAC4B;AAC5B,SAAO;AAAA,IACN;AAAA,IACA;AAAA,IACA,eAAe,SAAS;AAAA,IACxB,gBAAgB,SAAS;AAAA,EAC1B;AACD;AAmBO,SAAS,+BAAuD;AACtE,SAAO;AAAA,IACN,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,SAAS;AAAA,EACV;AACD;AAcO,SAAS,+BAAuD;AACtE,SAAO;AAAA,IACN,SAAS;AAAA,IACT,eAAe;AAAA,EAChB;AACD;","names":[]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/lib/behaviors/shared/ground-probe-3d.ts
|
|
2
|
-
import { Cuboid } from "@dimforge/rapier3d-compat";
|
|
2
|
+
import { Ball, Capsule, Cuboid } from "@dimforge/rapier3d-compat";
|
|
3
3
|
import {
|
|
4
4
|
BoxGeometry,
|
|
5
5
|
Mesh,
|
|
@@ -86,6 +86,42 @@ function extractShapeData(shapeType, internal) {
|
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
// src/lib/behaviors/shared/ground-probe-3d.ts
|
|
89
|
+
function inspectLiveColliderShape(entity) {
|
|
90
|
+
const shape = entity.collider?.shape;
|
|
91
|
+
if (!shape) return null;
|
|
92
|
+
if (shape instanceof Capsule) {
|
|
93
|
+
return {
|
|
94
|
+
halfBelow: shape.halfHeight + shape.radius,
|
|
95
|
+
halfX: shape.radius,
|
|
96
|
+
halfZ: shape.radius
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
if (shape instanceof Cuboid) {
|
|
100
|
+
return {
|
|
101
|
+
halfBelow: shape.halfExtents.y,
|
|
102
|
+
halfX: shape.halfExtents.x,
|
|
103
|
+
halfZ: shape.halfExtents.z
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
if (shape instanceof Ball) {
|
|
107
|
+
return {
|
|
108
|
+
halfBelow: shape.radius,
|
|
109
|
+
halfX: shape.radius,
|
|
110
|
+
halfZ: shape.radius
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
function getLiveColliderCenterOffsetY(entity) {
|
|
116
|
+
const collider = entity.collider;
|
|
117
|
+
const body = entity.body;
|
|
118
|
+
if (!collider || !body) return 0;
|
|
119
|
+
try {
|
|
120
|
+
return collider.translation().y - body.translation().y;
|
|
121
|
+
} catch {
|
|
122
|
+
return 0;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
89
125
|
var GROUND_SNAP_EPSILON = 1e-3;
|
|
90
126
|
var CENTER_BOX_HALF = 0.05;
|
|
91
127
|
var DEFAULT_BOX_THICKNESS = 0.05;
|
|
@@ -130,7 +166,6 @@ var GroundProbe3D = class {
|
|
|
130
166
|
z: shapePos.z
|
|
131
167
|
},
|
|
132
168
|
origin: { ...shapePos },
|
|
133
|
-
rayIndex: 0,
|
|
134
169
|
colliderUuid: hit.collider?._parent?.userData?.uuid
|
|
135
170
|
};
|
|
136
171
|
}
|
|
@@ -195,6 +230,14 @@ function deriveDefaultBox(entity, mode) {
|
|
|
195
230
|
if (mode === "center") {
|
|
196
231
|
return { x: CENTER_BOX_HALF, y: CENTER_BOX_HALF, z: CENTER_BOX_HALF };
|
|
197
232
|
}
|
|
233
|
+
const live = inspectLiveColliderShape(entity);
|
|
234
|
+
if (live) {
|
|
235
|
+
return {
|
|
236
|
+
x: Math.max(live.halfX * 0.7, CENTER_BOX_HALF),
|
|
237
|
+
y: DEFAULT_BOX_THICKNESS,
|
|
238
|
+
z: Math.max(live.halfZ * 0.7, CENTER_BOX_HALF)
|
|
239
|
+
};
|
|
240
|
+
}
|
|
198
241
|
const runtimeColliderDesc = entity?.colliderDesc;
|
|
199
242
|
if (runtimeColliderDesc) {
|
|
200
243
|
const inspected = inspectColliderDesc(runtimeColliderDesc);
|
|
@@ -226,6 +269,11 @@ function deriveDefaultBox(entity, mode) {
|
|
|
226
269
|
return { x: 0.35, y: DEFAULT_BOX_THICKNESS, z: 0.35 };
|
|
227
270
|
}
|
|
228
271
|
function getGroundAnchorOffsetY(entity) {
|
|
272
|
+
const live = inspectLiveColliderShape(entity);
|
|
273
|
+
if (live) {
|
|
274
|
+
const centerY2 = getLiveColliderCenterOffsetY(entity);
|
|
275
|
+
return live.halfBelow - centerY2;
|
|
276
|
+
}
|
|
229
277
|
const runtimeColliderDesc = entity?.colliderDesc;
|
|
230
278
|
if (runtimeColliderDesc) {
|
|
231
279
|
const inspected = inspectColliderDesc(runtimeColliderDesc);
|
|
@@ -258,4 +306,4 @@ export {
|
|
|
258
306
|
getGroundAnchorOffsetY,
|
|
259
307
|
getGroundSnapTargetY
|
|
260
308
|
};
|
|
261
|
-
//# sourceMappingURL=chunk-
|
|
309
|
+
//# sourceMappingURL=chunk-JXMF4NUI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/behaviors/shared/ground-probe-3d.ts","../src/lib/physics/collider-desc-inspect.ts"],"sourcesContent":["import { Ball, Capsule, Cuboid, type Collider, type RigidBody } from '@dimforge/rapier3d-compat';\nimport {\n\tBoxGeometry,\n\tMesh,\n\tMeshBasicMaterial,\n} from 'three';\nimport type { ZylemWorld } from '../../collision/world';\nimport { inspectColliderDesc } from '../../physics/collider-desc-inspect';\n\n/**\n * Probe sizing hint.\n *\n * - `center`: prefer a tiny box that behaves close to a single downward ray\n * (used by `Jumper3DBehavior` for thin/single-point ground tests).\n * - `any`: prefer a box sized from the entity's collider footprint\n * (used by `Platformer3D` so the character \"feels\" the ground under any\n * foot, not just dead-center).\n *\n * The actual box dimensions are still configurable via `options.box`.\n */\nexport type GroundProbeMode = 'center' | 'any';\n\nexport interface GroundProbeEntity {\n\tuuid: string;\n\tbody: RigidBody | null;\n\t/** Live Rapier collider; when present, probe geometry is derived from its shape. */\n\tcollider?: Collider | null;\n}\n\n/**\n * Geometry extracted from a live Rapier collider shape: the half-extent of\n * the shape below its center (`halfBelow`) and the horizontal half-extents\n * of its footprint. First-priority source for probe sizing/anchoring — works\n * for any entity with a live collider, engine metadata or not.\n */\ninterface LiveShapeInfo {\n\thalfBelow: number;\n\thalfX: number;\n\thalfZ: number;\n}\n\nfunction inspectLiveColliderShape(entity: GroundProbeEntity): LiveShapeInfo | null {\n\tconst shape = entity.collider?.shape;\n\tif (!shape) return null;\n\n\tif (shape instanceof Capsule) {\n\t\treturn {\n\t\t\thalfBelow: shape.halfHeight + shape.radius,\n\t\t\thalfX: shape.radius,\n\t\t\thalfZ: shape.radius,\n\t\t};\n\t}\n\tif (shape instanceof Cuboid) {\n\t\treturn {\n\t\t\thalfBelow: shape.halfExtents.y,\n\t\t\thalfX: shape.halfExtents.x,\n\t\t\thalfZ: shape.halfExtents.z,\n\t\t};\n\t}\n\tif (shape instanceof Ball) {\n\t\treturn {\n\t\t\thalfBelow: shape.radius,\n\t\t\thalfX: shape.radius,\n\t\t\thalfZ: shape.radius,\n\t\t};\n\t}\n\treturn null;\n}\n\n/**\n * Vertical offset of the collider center relative to the body origin\n * (0 when either translation is unavailable).\n */\nfunction getLiveColliderCenterOffsetY(entity: GroundProbeEntity): number {\n\tconst collider = entity.collider;\n\tconst body = entity.body;\n\tif (!collider || !body) return 0;\n\ttry {\n\t\treturn collider.translation().y - body.translation().y;\n\t} catch {\n\t\treturn 0;\n\t}\n}\n\nexport interface GroundProbeOptions {\n\t/** Maximum cast distance in world units. */\n\trayLength: number;\n\t/** Selects the default box size when `box` is not explicitly provided. */\n\tmode?: GroundProbeMode;\n\t/** Render a wireframe of the cast box for debugging. Requires `scene`. */\n\tdebug?: boolean;\n\t/** Three.js scene used to host the debug wireframe mesh. */\n\tscene?: any;\n\t/**\n\t * Vertical offset added to the body translation when computing the cast\n\t * origin. Allows callers to shift the probe to e.g. just above the\n\t * capsule's bottom face.\n\t */\n\toriginYOffset?: number;\n\t/**\n\t * Half-extents of the cast cuboid. When omitted the probe derives a\n\t * sensible default from the entity's collider:\n\t * - capsule: `(r * 0.7, 0.05, r * 0.7)` — a thin slab smaller than the\n\t * capsule footprint, so side walls don't register as ground.\n\t * - cuboid: shrink the bottom face by 30% so the probe sits well inside\n\t * the collider footprint.\n\t * - `mode: 'center'`: forces a tiny `(0.05, 0.05, 0.05)` box.\n\t */\n\tbox?: { x: number; y: number; z: number };\n}\n\nexport interface GroundProbeSupportHit {\n\ttoi: number;\n\tpoint: {\n\t\tx: number;\n\t\ty: number;\n\t\tz: number;\n\t};\n\torigin: {\n\t\tx: number;\n\t\ty: number;\n\t\tz: number;\n\t};\n\tcolliderUuid?: string;\n}\n\nexport const GROUND_SNAP_EPSILON = 0.001;\n\n/**\n * Tiny box used when the caller selects `mode: 'center'` — close enough to a\n * point cast that it preserves the old single-ray behavior of `Jumper3DBehavior`.\n */\nconst CENTER_BOX_HALF = 0.05;\n\n/**\n * Default vertical thickness of the cast cuboid. The box is intentionally thin\n * vertically so the impact distance closely matches a downward ray's TOI;\n * existing snap-to-ground math (which assumes `point.y ≈ origin.y - toi`)\n * keeps working with negligible drift.\n */\nconst DEFAULT_BOX_THICKNESS = 0.05;\n\n/**\n * GroundProbe3D — single-cast cuboid probe used by 3D platformer and jumper\n * behaviors to detect the ground beneath an entity.\n *\n * Replaces the previous 5-ray probe: a single shape-cast handles edge cases\n * (corner of a platform, narrow gap) more robustly and produces a single,\n * intuitive debug visualization (a wireframe box).\n */\nexport class GroundProbe3D {\n\tprivate debugMeshes = new Map<string, Mesh>();\n\n\tconstructor(private world: ZylemWorld | null) {}\n\n\tprobeSupport(\n\t\tentity: GroundProbeEntity,\n\t\toptions: GroundProbeOptions,\n\t): GroundProbeSupportHit | null {\n\t\tif (!this.world?.world || !entity.body) return null;\n\n\t\tconst translation = entity.body.translation();\n\t\tconst originYOffset = options.originYOffset ?? 0;\n\t\tconst box = options.box ?? deriveDefaultBox(entity, options.mode ?? 'any');\n\n\t\tconst shapePos = {\n\t\t\tx: translation.x,\n\t\t\ty: translation.y + originYOffset,\n\t\t\tz: translation.z,\n\t\t};\n\t\tconst shapeRot = { x: 0, y: 0, z: 0, w: 1 };\n\t\tconst shapeVel = { x: 0, y: -1, z: 0 };\n\n\t\tconst shape = new Cuboid(box.x, box.y, box.z);\n\t\tconst hit = this.world.world.castShape(\n\t\t\tshapePos,\n\t\t\tshapeRot,\n\t\t\tshapeVel,\n\t\t\tshape,\n\t\t\toptions.rayLength,\n\t\t\ttrue,\n\t\t\tundefined,\n\t\t\tundefined,\n\t\t\tundefined,\n\t\t\tentity.body,\n\t\t);\n\n\t\tlet support: GroundProbeSupportHit | null = null;\n\t\tif (hit) {\n\t\t\tsupport = {\n\t\t\t\ttoi: hit.toi,\n\t\t\t\tpoint: {\n\t\t\t\t\tx: shapePos.x,\n\t\t\t\t\ty: shapePos.y - hit.toi - box.y,\n\t\t\t\t\tz: shapePos.z,\n\t\t\t\t},\n\t\t\t\torigin: { ...shapePos },\n\t\t\t\tcolliderUuid: (hit.collider as any)?._parent?.userData?.uuid,\n\t\t\t};\n\t\t}\n\n\t\tif (options.debug && options.scene) {\n\t\t\tthis.updateDebugMesh(\n\t\t\t\tentity.uuid,\n\t\t\t\tshapePos,\n\t\t\t\tbox,\n\t\t\t\toptions.rayLength,\n\t\t\t\tBoolean(support),\n\t\t\t\toptions.scene,\n\t\t\t);\n\t\t} else {\n\t\t\tthis.disposeDebugMesh(entity.uuid);\n\t\t}\n\n\t\treturn support;\n\t}\n\n\tdetect(entity: GroundProbeEntity, options: GroundProbeOptions): boolean {\n\t\treturn this.probeSupport(entity, options) != null;\n\t}\n\n\tdestroyEntity(uuid: string): void {\n\t\tthis.disposeDebugMesh(uuid);\n\t}\n\n\tdestroy(): void {\n\t\tfor (const uuid of this.debugMeshes.keys()) {\n\t\t\tthis.disposeDebugMesh(uuid);\n\t\t}\n\t\tthis.debugMeshes.clear();\n\t}\n\n\tprivate updateDebugMesh(\n\t\tuuid: string,\n\t\tshapePos: { x: number; y: number; z: number },\n\t\tbox: { x: number; y: number; z: number },\n\t\trayLength: number,\n\t\thasGround: boolean,\n\t\tscene: any,\n\t): void {\n\t\tlet mesh = this.debugMeshes.get(uuid);\n\t\tif (!mesh) {\n\t\t\tconst geometry = new BoxGeometry(2 * box.x, 2 * box.y, 2 * box.z);\n\t\t\tconst material = new MeshBasicMaterial({\n\t\t\t\twireframe: true,\n\t\t\t\tcolor: 0xff0000,\n\t\t\t});\n\t\t\tmesh = new Mesh(geometry, material);\n\t\t\tscene.add(mesh);\n\t\t\tthis.debugMeshes.set(uuid, mesh);\n\t\t}\n\n\t\t// Position the box at the cast midpoint so users can see the swept volume.\n\t\tmesh.position.set(\n\t\t\tshapePos.x,\n\t\t\tshapePos.y - rayLength * 0.5,\n\t\t\tshapePos.z,\n\t\t);\n\t\tmesh.scale.set(1, Math.max(rayLength / Math.max(2 * box.y, 1e-6), 1), 1);\n\t\t(mesh.material as MeshBasicMaterial).color.setHex(\n\t\t\thasGround ? 0x00ff00 : 0xff0000,\n\t\t);\n\t}\n\n\tprivate disposeDebugMesh(uuid: string): void {\n\t\tconst mesh = this.debugMeshes.get(uuid);\n\t\tif (!mesh) return;\n\n\t\tmesh.removeFromParent();\n\t\tmesh.geometry.dispose();\n\t\t(mesh.material as MeshBasicMaterial).dispose();\n\n\t\tthis.debugMeshes.delete(uuid);\n\t}\n}\n\nfunction deriveDefaultBox(\n\tentity: GroundProbeEntity,\n\tmode: GroundProbeMode,\n): { x: number; y: number; z: number } {\n\tif (mode === 'center') {\n\t\treturn { x: CENTER_BOX_HALF, y: CENTER_BOX_HALF, z: CENTER_BOX_HALF };\n\t}\n\n\t// First priority: derive from the live Rapier collider shape. Works for\n\t// any consumer (e.g. the web harness) without engine collider metadata.\n\tconst live = inspectLiveColliderShape(entity);\n\tif (live) {\n\t\treturn {\n\t\t\tx: Math.max(live.halfX * 0.7, CENTER_BOX_HALF),\n\t\t\ty: DEFAULT_BOX_THICKNESS,\n\t\t\tz: Math.max(live.halfZ * 0.7, CENTER_BOX_HALF),\n\t\t};\n\t}\n\n\tconst runtimeColliderDesc = (entity as any)?.colliderDesc;\n\tif (runtimeColliderDesc) {\n\t\tconst inspected = inspectColliderDesc(runtimeColliderDesc);\n\t\tif (inspected.shape === 'capsule' && inspected.dimensions.length >= 2) {\n\t\t\tconst radius = inspected.dimensions[1] ?? 0.5;\n\t\t\tconst half = Math.max(radius * 0.7, CENTER_BOX_HALF);\n\t\t\treturn { x: half, y: DEFAULT_BOX_THICKNESS, z: half };\n\t\t}\n\t\tif (inspected.shape === 'cuboid' && inspected.dimensions.length >= 3) {\n\t\t\tconst halfX = (inspected.dimensions[0] ?? 0.5) * 0.7;\n\t\t\tconst halfZ = (inspected.dimensions[2] ?? 0.5) * 0.7;\n\t\t\treturn {\n\t\t\t\tx: Math.max(halfX, CENTER_BOX_HALF),\n\t\t\t\ty: DEFAULT_BOX_THICKNESS,\n\t\t\t\tz: Math.max(halfZ, CENTER_BOX_HALF),\n\t\t\t};\n\t\t}\n\t}\n\n\tconst collisionSize =\n\t\t(entity as any)?.options?.collision?.size ??\n\t\t(entity as any)?.options?.collisionSize ??\n\t\t(entity as any)?.options?.size;\n\tif (collisionSize) {\n\t\tconst halfX = ((collisionSize.x ?? 1) / 2) * 0.7;\n\t\tconst halfZ = ((collisionSize.z ?? collisionSize.x ?? 1) / 2) * 0.7;\n\t\treturn {\n\t\t\tx: Math.max(halfX, CENTER_BOX_HALF),\n\t\t\ty: DEFAULT_BOX_THICKNESS,\n\t\t\tz: Math.max(halfZ, CENTER_BOX_HALF),\n\t\t};\n\t}\n\n\treturn { x: 0.35, y: DEFAULT_BOX_THICKNESS, z: 0.35 };\n}\n\nexport function getGroundAnchorOffsetY(entity: any): number {\n\t// First priority: live Rapier collider shape (see inspectLiveColliderShape).\n\tconst live = inspectLiveColliderShape(entity);\n\tif (live) {\n\t\tconst centerY = getLiveColliderCenterOffsetY(entity);\n\t\treturn live.halfBelow - centerY;\n\t}\n\n\tconst runtimeColliderDesc = entity?.colliderDesc;\n\tif (runtimeColliderDesc) {\n\t\tconst inspected = inspectColliderDesc(runtimeColliderDesc);\n\t\tconst centerY = inspected.translation?.[1] ?? 0;\n\t\tif (inspected.shape === 'capsule' && inspected.dimensions.length >= 2) {\n\t\t\tconst halfCylinder = inspected.dimensions[0] ?? 0;\n\t\t\tconst radius = inspected.dimensions[1] ?? 0;\n\t\t\treturn halfCylinder + radius - centerY;\n\t\t}\n\t\tif (inspected.shape === 'cuboid' && inspected.dimensions.length >= 2) {\n\t\t\tconst halfHeight = inspected.dimensions[1] ?? 0;\n\t\t\treturn halfHeight - centerY;\n\t\t}\n\t}\n\n\tconst collisionSize =\n\t\tentity?.options?.collision?.size ??\n\t\tentity?.options?.collisionSize ??\n\t\tentity?.options?.size;\n\tconst height = collisionSize?.y ?? 0;\n\tif (height <= 0) {\n\t\treturn 0;\n\t}\n\n\tconst collisionPosition =\n\t\tentity?.options?.collision?.position ??\n\t\tentity?.options?.collisionPosition;\n\tconst centerY = collisionPosition?.y ?? height / 2;\n\treturn (height / 2) - centerY;\n}\n\nexport function getGroundSnapTargetY(\n\tentity: any,\n\tsupport: GroundProbeSupportHit,\n\tepsilon: number = GROUND_SNAP_EPSILON,\n): number {\n\treturn support.point.y + getGroundAnchorOffsetY(entity) + epsilon;\n}\n","import type { ColliderDesc } from '@dimforge/rapier3d-compat';\n\n/**\n * Subset of Rapier shape kinds that game-lib introspects for sizing decisions\n * (e.g. ground probes derive their cast box from the entity collider). Mirrors\n * the public Rapier shape vocabulary, kept as string literals so callers can\n * branch without depending on Rapier's internal shape enum.\n */\nexport type ColliderShapeKind =\n\t| 'ball'\n\t| 'cuboid'\n\t| 'capsule'\n\t| 'cone'\n\t| 'cylinder'\n\t| 'heightfield'\n\t| 'convexHull'\n\t| 'trimesh';\n\n/**\n * Plain, runtime-agnostic snapshot of a {@link ColliderDesc}'s shape and\n * placement. Only contains the fields game-lib needs today; extend as new\n * call sites appear.\n *\n * - `shape` — discriminator for the underlying Rapier shape.\n * - `dimensions` — shape-specific numbers (see {@link inspectColliderDesc}).\n * - `translation` — local offset relative to the parent body, when set.\n * - `vertices` / `indices` — raw geometry payload for `convexHull` and\n * `trimesh` shapes; tagged via `__zylemShapeData` at construction time.\n */\nexport interface ColliderDescInspection {\n\tshape: ColliderShapeKind;\n\tdimensions: number[];\n\ttranslation?: [number, number, number];\n\tvertices?: number[];\n\tindices?: number[];\n}\n\n/**\n * Read the shape kind, dimensions, and local translation off a Rapier\n * {@link ColliderDesc} without going through the WASM-backed accessors that\n * cannot cross worker boundaries.\n *\n * Layout per shape (matches Rapier's primitive constructors):\n * - `ball` → `[radius]`\n * - `cuboid` → `[halfX, halfY, halfZ]`\n * - `capsule` → `[halfHeight, radius]`\n * - `cone` → `[halfHeight, radius]`\n * - `cylinder` → `[halfHeight, radius]`\n * - `heightfield` → flattened heights array\n * - `convexHull` → flattened vertex array (set via the `__zylemShapeData` tag)\n * - `trimesh` → flattened vertex array (indices ignored here)\n */\nexport function inspectColliderDesc(desc: ColliderDesc): ColliderDescInspection {\n\tconst internal = desc as any;\n\tconst customShapeData = internal.__zylemShapeData as\n\t\t| { shape: 'convexHull'; vertices: number[] }\n\t\t| { shape: 'trimesh'; vertices: number[]; indices: number[] }\n\t\t| undefined;\n\n\tif (customShapeData?.shape === 'convexHull') {\n\t\tconst result = finalize('convexHull', [], internal);\n\t\tresult.vertices = [...customShapeData.vertices];\n\t\treturn result;\n\t}\n\tif (customShapeData?.shape === 'trimesh') {\n\t\tconst result = finalize('trimesh', [], internal);\n\t\tresult.vertices = [...customShapeData.vertices];\n\t\tresult.indices = [...customShapeData.indices];\n\t\treturn result;\n\t}\n\n\tconst shapeType = internal.shape?.type ?? internal.shapeType ?? 0;\n\tconst { shape, dimensions } = extractShapeData(shapeType, internal);\n\treturn finalize(shape, dimensions, internal);\n}\n\nfunction finalize(\n\tshape: ColliderShapeKind,\n\tdimensions: number[],\n\tinternal: any,\n): ColliderDescInspection {\n\tconst result: ColliderDescInspection = { shape, dimensions };\n\tconst t = internal.translation;\n\tif (t && (t.x !== 0 || t.y !== 0 || t.z !== 0)) {\n\t\tresult.translation = [t.x, t.y, t.z];\n\t}\n\treturn result;\n}\n\n/**\n * Rapier shape type enum values (from rapier internals):\n * 0 = Ball, 1 = Cuboid, 2 = Capsule, 6 = Cone, 7 = Cylinder, 11 = HeightField\n */\nfunction extractShapeData(\n\tshapeType: number,\n\tinternal: any,\n): { shape: ColliderShapeKind; dimensions: number[] } {\n\tswitch (shapeType) {\n\t\tcase 0:\n\t\t\treturn {\n\t\t\t\tshape: 'ball',\n\t\t\t\tdimensions: [internal.shape?.radius ?? internal.halfExtents?.x ?? 1],\n\t\t\t};\n\t\tcase 1:\n\t\t\treturn {\n\t\t\t\tshape: 'cuboid',\n\t\t\t\tdimensions: [\n\t\t\t\t\tinternal.shape?.halfExtents?.x ?? internal.halfExtents?.x ?? 0.5,\n\t\t\t\t\tinternal.shape?.halfExtents?.y ?? internal.halfExtents?.y ?? 0.5,\n\t\t\t\t\tinternal.shape?.halfExtents?.z ?? internal.halfExtents?.z ?? 0.5,\n\t\t\t\t],\n\t\t\t};\n\t\tcase 2:\n\t\t\treturn {\n\t\t\t\tshape: 'capsule',\n\t\t\t\tdimensions: [\n\t\t\t\t\tinternal.shape?.halfHeight ?? 0.5,\n\t\t\t\t\tinternal.shape?.radius ?? 0.5,\n\t\t\t\t],\n\t\t\t};\n\t\tcase 6:\n\t\t\treturn {\n\t\t\t\tshape: 'cone',\n\t\t\t\tdimensions: [\n\t\t\t\t\tinternal.shape?.halfHeight ?? 1,\n\t\t\t\t\tinternal.shape?.radius ?? 1,\n\t\t\t\t],\n\t\t\t};\n\t\tcase 7:\n\t\t\treturn {\n\t\t\t\tshape: 'cylinder',\n\t\t\t\tdimensions: [\n\t\t\t\t\tinternal.shape?.halfHeight ?? 1,\n\t\t\t\t\tinternal.shape?.radius ?? 1,\n\t\t\t\t],\n\t\t\t};\n\t\tcase 11: {\n\t\t\tconst heights = internal.shape?.heights;\n\t\t\treturn {\n\t\t\t\tshape: 'heightfield',\n\t\t\t\tdimensions: heights ? Array.from(heights) : [],\n\t\t\t};\n\t\t}\n\t\tdefault:\n\t\t\treturn { shape: 'cuboid', dimensions: [0.5, 0.5, 0.5] };\n\t}\n}\n"],"mappings":"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","names":["centerY"]}
|