@zylem/game-lib 0.6.0 → 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +9 -16
  2. package/dist/actions.d.ts +30 -21
  3. package/dist/actions.js +628 -145
  4. package/dist/actions.js.map +1 -1
  5. package/dist/behavior/platformer-3d.d.ts +296 -0
  6. package/dist/behavior/platformer-3d.js +518 -0
  7. package/dist/behavior/platformer-3d.js.map +1 -0
  8. package/dist/behavior/ricochet-2d.d.ts +274 -0
  9. package/dist/behavior/ricochet-2d.js +394 -0
  10. package/dist/behavior/ricochet-2d.js.map +1 -0
  11. package/dist/behavior/screen-wrap.d.ts +86 -0
  12. package/dist/behavior/screen-wrap.js +195 -0
  13. package/dist/behavior/screen-wrap.js.map +1 -0
  14. package/dist/behavior/thruster.d.ts +10 -0
  15. package/dist/behavior/thruster.js +234 -0
  16. package/dist/behavior/thruster.js.map +1 -0
  17. package/dist/behavior/world-boundary-2d.d.ts +141 -0
  18. package/dist/behavior/world-boundary-2d.js +181 -0
  19. package/dist/behavior/world-boundary-2d.js.map +1 -0
  20. package/dist/behavior-descriptor-BWNWmIjv.d.ts +142 -0
  21. package/dist/{blueprints-BOCc3Wve.d.ts → blueprints-BWGz8fII.d.ts} +2 -2
  22. package/dist/camera-B5e4c78l.d.ts +468 -0
  23. package/dist/camera.d.ts +3 -2
  24. package/dist/camera.js +962 -166
  25. package/dist/camera.js.map +1 -1
  26. package/dist/composition-DrzFrbqI.d.ts +218 -0
  27. package/dist/{core-CZhozNRH.d.ts → core-DAkskq6Y.d.ts} +97 -65
  28. package/dist/core.d.ts +12 -6
  29. package/dist/core.js +4449 -1052
  30. package/dist/core.js.map +1 -1
  31. package/dist/{entities-BAxfJOkk.d.ts → entities-DC9ce_vx.d.ts} +154 -45
  32. package/dist/entities.d.ts +5 -2
  33. package/dist/entities.js +2505 -722
  34. package/dist/entities.js.map +1 -1
  35. package/dist/entity-BpbZqg19.d.ts +1100 -0
  36. package/dist/entity-types-DAu8sGJH.d.ts +26 -0
  37. package/dist/global-change-Dc8uCKi2.d.ts +25 -0
  38. package/dist/main.d.ts +472 -29
  39. package/dist/main.js +11877 -6124
  40. package/dist/main.js.map +1 -1
  41. package/dist/{stage-types-CD21XoIU.d.ts → stage-types-BFsm3qsZ.d.ts} +255 -26
  42. package/dist/stage.d.ts +11 -6
  43. package/dist/stage.js +3462 -491
  44. package/dist/stage.js.map +1 -1
  45. package/dist/thruster-DhRaJnoL.d.ts +172 -0
  46. package/dist/world-Be5m1XC1.d.ts +31 -0
  47. package/package.json +21 -4
  48. package/dist/behaviors.d.ts +0 -106
  49. package/dist/behaviors.js +0 -398
  50. package/dist/behaviors.js.map +0 -1
  51. package/dist/camera-CpbDr4-V.d.ts +0 -116
  52. package/dist/entity-COvRtFNG.d.ts +0 -395
  53. package/dist/moveable-B_vyA6cw.d.ts +0 -67
  54. package/dist/transformable-CUhvyuYO.d.ts +0 -67
@@ -0,0 +1,518 @@
1
+ // src/lib/behaviors/platformer-3d/components.ts
2
+ function createPlatformer3DMovementComponent(options = {}) {
3
+ return {
4
+ walkSpeed: options.walkSpeed ?? 12,
5
+ runSpeed: options.runSpeed ?? 24,
6
+ jumpForce: options.jumpForce ?? 12,
7
+ maxJumps: options.maxJumps ?? 1,
8
+ gravity: options.gravity ?? 9.82,
9
+ groundRayLength: options.groundRayLength ?? 1,
10
+ coyoteTime: options.coyoteTime ?? 0.1,
11
+ jumpBufferTime: options.jumpBufferTime ?? 0.1,
12
+ jumpCutMultiplier: options.jumpCutMultiplier ?? 0.5,
13
+ multiJumpWindowTime: options.multiJumpWindowTime ?? 0.15
14
+ // 150ms default
15
+ };
16
+ }
17
+ function createPlatformer3DInputComponent() {
18
+ return {
19
+ moveX: 0,
20
+ moveZ: 0,
21
+ jump: false,
22
+ run: false
23
+ };
24
+ }
25
+ function createPlatformer3DStateComponent() {
26
+ return {
27
+ grounded: false,
28
+ jumping: false,
29
+ falling: false,
30
+ jumpCount: 0,
31
+ jumpStartHeight: 0,
32
+ currentSpeed: 0,
33
+ lastGroundedY: 0,
34
+ jumpPressedLastFrame: false,
35
+ collisionGrounded: false,
36
+ groundedCollisionTime: 0,
37
+ timeSinceGrounded: 0,
38
+ jumpBuffered: false,
39
+ jumpBufferTimer: 0,
40
+ jumpHeld: false,
41
+ jumpCutApplied: false,
42
+ jumpReleasedSinceLastJump: true,
43
+ timeSinceJump: 0
44
+ };
45
+ }
46
+
47
+ // src/lib/behaviors/platformer-3d/platformer-3d-fsm.ts
48
+ import { StateMachine, t } from "typescript-fsm";
49
+ var Platformer3DState = /* @__PURE__ */ ((Platformer3DState2) => {
50
+ Platformer3DState2["Idle"] = "idle";
51
+ Platformer3DState2["Walking"] = "walking";
52
+ Platformer3DState2["Running"] = "running";
53
+ Platformer3DState2["Jumping"] = "jumping";
54
+ Platformer3DState2["Falling"] = "falling";
55
+ Platformer3DState2["Landing"] = "landing";
56
+ return Platformer3DState2;
57
+ })(Platformer3DState || {});
58
+ var Platformer3DEvent = /* @__PURE__ */ ((Platformer3DEvent2) => {
59
+ Platformer3DEvent2["Walk"] = "walk";
60
+ Platformer3DEvent2["Run"] = "run";
61
+ Platformer3DEvent2["Jump"] = "jump";
62
+ Platformer3DEvent2["Fall"] = "fall";
63
+ Platformer3DEvent2["Land"] = "land";
64
+ Platformer3DEvent2["Stop"] = "stop";
65
+ return Platformer3DEvent2;
66
+ })(Platformer3DEvent || {});
67
+ var Platformer3DFSM = class {
68
+ constructor(ctx) {
69
+ this.ctx = ctx;
70
+ this.machine = new StateMachine(
71
+ "idle" /* Idle */,
72
+ [
73
+ // Idle transitions
74
+ t("idle" /* Idle */, "walk" /* Walk */, "walking" /* Walking */),
75
+ t("idle" /* Idle */, "run" /* Run */, "running" /* Running */),
76
+ t("idle" /* Idle */, "jump" /* Jump */, "jumping" /* Jumping */),
77
+ t("idle" /* Idle */, "fall" /* Fall */, "falling" /* Falling */),
78
+ // Walking transitions
79
+ t("walking" /* Walking */, "run" /* Run */, "running" /* Running */),
80
+ t("walking" /* Walking */, "jump" /* Jump */, "jumping" /* Jumping */),
81
+ t("walking" /* Walking */, "stop" /* Stop */, "idle" /* Idle */),
82
+ t("walking" /* Walking */, "fall" /* Fall */, "falling" /* Falling */),
83
+ // Running transitions
84
+ t("running" /* Running */, "walk" /* Walk */, "walking" /* Walking */),
85
+ t("running" /* Running */, "jump" /* Jump */, "jumping" /* Jumping */),
86
+ t("running" /* Running */, "stop" /* Stop */, "idle" /* Idle */),
87
+ t("running" /* Running */, "fall" /* Fall */, "falling" /* Falling */),
88
+ // Jumping transitions
89
+ t("jumping" /* Jumping */, "fall" /* Fall */, "falling" /* Falling */),
90
+ t("jumping" /* Jumping */, "land" /* Land */, "landing" /* Landing */),
91
+ t("jumping" /* Jumping */, "jump" /* Jump */, "jumping" /* Jumping */),
92
+ // Multi-jump
93
+ // Falling transitions
94
+ t("falling" /* Falling */, "land" /* Land */, "landing" /* Landing */),
95
+ // Landing transitions
96
+ t("landing" /* Landing */, "walk" /* Walk */, "walking" /* Walking */),
97
+ t("landing" /* Landing */, "run" /* Run */, "running" /* Running */),
98
+ t("landing" /* Landing */, "stop" /* Stop */, "idle" /* Idle */),
99
+ // Self-transitions (no-ops)
100
+ t("idle" /* Idle */, "stop" /* Stop */, "idle" /* Idle */)
101
+ ]
102
+ );
103
+ }
104
+ machine;
105
+ /**
106
+ * Get the current state
107
+ */
108
+ getState() {
109
+ return this.machine.getState();
110
+ }
111
+ /**
112
+ * Dispatch an event to the FSM
113
+ */
114
+ dispatch(event) {
115
+ if (this.machine.can(event)) {
116
+ this.machine.dispatch(event);
117
+ }
118
+ }
119
+ /**
120
+ * Check if grounded
121
+ */
122
+ isGrounded() {
123
+ const state = this.getState();
124
+ return state === "idle" /* Idle */ || state === "walking" /* Walking */ || state === "running" /* Running */ || state === "landing" /* Landing */;
125
+ }
126
+ /**
127
+ * Get current jump count from context
128
+ */
129
+ getJumpCount() {
130
+ return this.ctx.state.jumpCount;
131
+ }
132
+ /**
133
+ * Handle collision event to update ground state
134
+ */
135
+ handleCollision(ctx) {
136
+ if (ctx.contact.normal.y > 0.5) {
137
+ this.ctx.state.collisionGrounded = true;
138
+ this.ctx.state.groundedCollisionTime = performance.now();
139
+ }
140
+ }
141
+ /**
142
+ * Update FSM based on current state
143
+ */
144
+ update(input, state) {
145
+ this.ctx.input = input;
146
+ this.ctx.state = state;
147
+ const currentState = this.getState();
148
+ const hasInput = Math.abs(input.moveX) > 0.1 || Math.abs(input.moveZ) > 0.1;
149
+ const isRunning = input.run;
150
+ if (currentState === "falling" /* Falling */ && state.grounded) {
151
+ this.dispatch("land" /* Land */);
152
+ }
153
+ if (state.grounded) {
154
+ if (hasInput) {
155
+ if (isRunning) {
156
+ this.dispatch("run" /* Run */);
157
+ } else {
158
+ this.dispatch("walk" /* Walk */);
159
+ }
160
+ } else {
161
+ this.dispatch("stop" /* Stop */);
162
+ }
163
+ } else {
164
+ if (state.falling) {
165
+ this.dispatch("fall" /* Fall */);
166
+ }
167
+ }
168
+ if (input.jump && !state.jumpPressedLastFrame) {
169
+ this.dispatch("jump" /* Jump */);
170
+ }
171
+ }
172
+ };
173
+
174
+ // src/lib/behaviors/platformer-3d/platformer-3d.behavior.ts
175
+ import { Vector3, BufferGeometry, LineBasicMaterial, Line } from "three";
176
+ import { Ray } from "@dimforge/rapier3d-compat";
177
+ var Platformer3DBehavior = class {
178
+ world;
179
+ scene;
180
+ rays = /* @__PURE__ */ new Map();
181
+ debugLines = /* @__PURE__ */ new Map();
182
+ // Store Line objects for debug visualization
183
+ constructor(world, scene) {
184
+ this.world = world;
185
+ this.scene = scene;
186
+ }
187
+ /**
188
+ * Detect if entity is on the ground using raycasting
189
+ */
190
+ /**
191
+ * Detect if entity is on the ground using raycasting (multi-sample: center + 4 corners)
192
+ */
193
+ detectGround(entity) {
194
+ if (!this.world?.world || !entity.body) return false;
195
+ const translation = entity.body.translation();
196
+ const rayLength = entity.platformer.groundRayLength;
197
+ const radius = 0.4;
198
+ const offsets = [
199
+ { x: 0, z: 0 },
200
+ { x: radius, z: radius },
201
+ { x: -radius, z: radius },
202
+ { x: radius, z: -radius },
203
+ { x: -radius, z: -radius }
204
+ ];
205
+ let entityRays = this.rays.get(entity.uuid);
206
+ if (!entityRays) {
207
+ entityRays = offsets.map(() => new Ray({ x: 0, y: 0, z: 0 }, { x: 0, y: -1, z: 0 }));
208
+ this.rays.set(entity.uuid, entityRays);
209
+ }
210
+ let grounded = false;
211
+ offsets.forEach((offset, i) => {
212
+ if (grounded) return;
213
+ const ray = entityRays[i];
214
+ ray.origin = {
215
+ x: translation.x + offset.x,
216
+ y: translation.y,
217
+ z: translation.z + offset.z
218
+ };
219
+ ray.dir = { x: 0, y: -1, z: 0 };
220
+ const hit = this.world.world.castRay(
221
+ ray,
222
+ rayLength,
223
+ true,
224
+ void 0,
225
+ void 0,
226
+ void 0,
227
+ void 0,
228
+ (collider) => {
229
+ const ref = collider._parent?.userData?.uuid;
230
+ if (ref === entity.uuid) return false;
231
+ grounded = true;
232
+ return true;
233
+ }
234
+ );
235
+ });
236
+ if (this.scene) {
237
+ this.updateDebugLines(entity, entityRays, grounded, rayLength);
238
+ }
239
+ return grounded;
240
+ }
241
+ updateDebugLines(entity, rays, hasGround, length) {
242
+ let lines = this.debugLines.get(entity.uuid);
243
+ if (!lines) {
244
+ lines = rays.map(() => {
245
+ const geometry = new BufferGeometry().setFromPoints([new Vector3(), new Vector3()]);
246
+ const material = new LineBasicMaterial({ color: 16711680 });
247
+ const line = new Line(geometry, material);
248
+ this.scene.add(line);
249
+ return line;
250
+ });
251
+ this.debugLines.set(entity.uuid, lines);
252
+ }
253
+ rays.forEach((ray, i) => {
254
+ const line = lines[i];
255
+ const start = new Vector3(ray.origin.x, ray.origin.y, ray.origin.z);
256
+ const end = new Vector3(
257
+ ray.origin.x + ray.dir.x * length,
258
+ ray.origin.y + ray.dir.y * length,
259
+ ray.origin.z + ray.dir.z * length
260
+ );
261
+ line.geometry.setFromPoints([start, end]);
262
+ line.material.color.setHex(hasGround ? 65280 : 16711680);
263
+ });
264
+ }
265
+ /**
266
+ * Apply horizontal movement based on input
267
+ */
268
+ applyMovement(entity, delta) {
269
+ const input = entity.$platformer;
270
+ const movement = entity.platformer;
271
+ const state = entity.platformerState;
272
+ const speed = input.run ? movement.runSpeed : movement.walkSpeed;
273
+ state.currentSpeed = speed;
274
+ const moveX = input.moveX * speed;
275
+ const moveZ = input.moveZ * speed;
276
+ const currentVel = entity.body.linvel();
277
+ entity.transformStore.velocity.x = moveX;
278
+ entity.transformStore.velocity.y = currentVel.y;
279
+ entity.transformStore.velocity.z = moveZ;
280
+ entity.transformStore.dirty.velocity = true;
281
+ }
282
+ /**
283
+ * Handle jump logic with multi-jump support
284
+ */
285
+ /**
286
+ * Handle jump logic with multi-jump, coyote time, and buffering
287
+ */
288
+ handleJump(entity, delta) {
289
+ const input = entity.$platformer;
290
+ const movement = entity.platformer;
291
+ const state = entity.platformerState;
292
+ if (state.jumping || state.falling) {
293
+ state.timeSinceJump += delta;
294
+ }
295
+ if (!input.jump && state.jumpHeld) {
296
+ state.jumpReleasedSinceLastJump = true;
297
+ }
298
+ if (input.jump && !state.jumpPressedLastFrame) {
299
+ state.jumpBuffered = true;
300
+ state.jumpBufferTimer = movement.jumpBufferTime;
301
+ }
302
+ state.jumpPressedLastFrame = input.jump;
303
+ state.jumpHeld = input.jump;
304
+ if (state.jumpBuffered) {
305
+ state.jumpBufferTimer -= delta;
306
+ if (state.jumpBufferTimer <= 0) {
307
+ state.jumpBuffered = false;
308
+ }
309
+ }
310
+ const minTimeBeforeCut = 0.1;
311
+ const canApplyCut = state.timeSinceJump >= minTimeBeforeCut;
312
+ if (!input.jump && state.jumping && !state.jumpCutApplied && canApplyCut) {
313
+ const velocity = entity.body.linvel();
314
+ entity.transformStore.velocity.y = velocity.y * movement.jumpCutMultiplier;
315
+ entity.transformStore.dirty.velocity = true;
316
+ state.jumpCutApplied = true;
317
+ }
318
+ if (!state.jumpBuffered) return;
319
+ const inCoyoteWindow = !state.grounded && state.timeSinceGrounded <= movement.coyoteTime;
320
+ const isFirstJump = state.grounded || inCoyoteWindow && state.jumpCount === 0;
321
+ const hasJumpsRemaining = state.jumpCount < movement.maxJumps;
322
+ const buttonReleased = state.jumpReleasedSinceLastJump;
323
+ const inMultiJumpWindow = state.timeSinceJump >= movement.multiJumpWindowTime;
324
+ const canMultiJump = !state.grounded && hasJumpsRemaining && buttonReleased && inMultiJumpWindow;
325
+ console.log("[JUMP DEBUG] Attempting jump:", {
326
+ grounded: state.grounded,
327
+ jumpCount: state.jumpCount,
328
+ maxJumps: movement.maxJumps,
329
+ isFirstJump,
330
+ canMultiJump,
331
+ "--- Multi-jump conditions ---": "",
332
+ "!grounded": !state.grounded,
333
+ hasJumpsRemaining,
334
+ buttonReleased,
335
+ inMultiJumpWindow,
336
+ timeSinceJump: state.timeSinceJump.toFixed(3),
337
+ multiJumpWindowTime: movement.multiJumpWindowTime
338
+ });
339
+ if (isFirstJump || canMultiJump) {
340
+ console.log("[JUMP DEBUG] \u2705 EXECUTING JUMP #" + (state.jumpCount + 1));
341
+ state.jumpBuffered = false;
342
+ state.jumpCount++;
343
+ state.jumpReleasedSinceLastJump = false;
344
+ state.timeSinceJump = 0;
345
+ state.jumpStartHeight = entity.body.translation().y;
346
+ state.jumping = true;
347
+ state.falling = false;
348
+ state.jumpCutApplied = false;
349
+ entity.transformStore.velocity.y = movement.jumpForce;
350
+ entity.transformStore.dirty.velocity = true;
351
+ } else {
352
+ console.log("[JUMP DEBUG] \u274C JUMP BLOCKED - conditions not met");
353
+ }
354
+ }
355
+ /**
356
+ * Apply gravity when not grounded
357
+ */
358
+ applyGravity(entity, delta) {
359
+ const movement = entity.platformer;
360
+ const state = entity.platformerState;
361
+ if (state.grounded) return;
362
+ if (state.jumping && state.timeSinceJump < 0.01) {
363
+ return;
364
+ }
365
+ const currentVel = entity.body.linvel();
366
+ const newYVelocity = currentVel.y - movement.gravity * delta;
367
+ entity.transformStore.velocity.y = newYVelocity;
368
+ entity.transformStore.dirty.velocity = true;
369
+ }
370
+ /**
371
+ * Update entity state based on physics
372
+ */
373
+ updateState(entity, delta) {
374
+ const state = entity.platformerState;
375
+ const wasGrounded = state.grounded;
376
+ const velocity = entity.body.linvel();
377
+ let isGrounded = false;
378
+ const isAirborne = state.jumping || state.falling;
379
+ if (isAirborne) {
380
+ const nearGround = this.detectGround(entity);
381
+ const canLand = state.falling && !state.jumping;
382
+ const hasLanded = Math.abs(velocity.y) < 0.5;
383
+ isGrounded = nearGround && canLand && hasLanded;
384
+ } else {
385
+ const nearGround = this.detectGround(entity);
386
+ const notFallingFast = velocity.y > -2;
387
+ isGrounded = nearGround && notFallingFast;
388
+ }
389
+ state.grounded = isGrounded;
390
+ if (state.grounded) {
391
+ state.timeSinceGrounded = 0;
392
+ state.lastGroundedY = entity.body.translation().y;
393
+ } else {
394
+ state.timeSinceGrounded += delta;
395
+ }
396
+ if (!wasGrounded && state.grounded) {
397
+ state.jumpCount = 0;
398
+ state.jumping = false;
399
+ state.falling = false;
400
+ state.jumpCutApplied = false;
401
+ }
402
+ if (velocity.y < -0.1 && !state.grounded) {
403
+ if (state.jumping && velocity.y < 0) {
404
+ state.jumping = false;
405
+ state.falling = true;
406
+ } else if (!state.jumping) {
407
+ state.falling = true;
408
+ }
409
+ }
410
+ }
411
+ /**
412
+ * Update all platformer entities
413
+ */
414
+ update(delta) {
415
+ if (!this.world?.collisionMap) return;
416
+ for (const [, entity] of this.world.collisionMap) {
417
+ const platformerEntity = entity;
418
+ if (!platformerEntity.platformer || !platformerEntity.$platformer || !platformerEntity.platformerState) {
419
+ continue;
420
+ }
421
+ this.updateState(platformerEntity, delta);
422
+ this.applyMovement(platformerEntity, delta);
423
+ this.handleJump(platformerEntity, delta);
424
+ this.applyGravity(platformerEntity, delta);
425
+ }
426
+ }
427
+ /**
428
+ * Cleanup
429
+ */
430
+ destroy() {
431
+ this.rays.clear();
432
+ }
433
+ };
434
+
435
+ // src/lib/behaviors/behavior-descriptor.ts
436
+ function defineBehavior(config) {
437
+ return {
438
+ key: /* @__PURE__ */ Symbol.for(`zylem:behavior:${config.name}`),
439
+ defaultOptions: config.defaultOptions,
440
+ systemFactory: config.systemFactory,
441
+ createHandle: config.createHandle
442
+ };
443
+ }
444
+
445
+ // src/lib/behaviors/platformer-3d/platformer-3d.descriptor.ts
446
+ var defaultOptions = {
447
+ walkSpeed: 12,
448
+ runSpeed: 24,
449
+ jumpForce: 12,
450
+ maxJumps: 1,
451
+ gravity: 9.82,
452
+ groundRayLength: 1
453
+ };
454
+ var Platformer3DBehaviorSystem = class {
455
+ constructor(world, scene) {
456
+ this.world = world;
457
+ this.scene = scene;
458
+ this.movementBehavior = new Platformer3DBehavior(world, scene);
459
+ }
460
+ movementBehavior;
461
+ update(ecs, delta) {
462
+ if (!this.world?.collisionMap) return;
463
+ for (const [, entity] of this.world.collisionMap) {
464
+ const gameEntity = entity;
465
+ if (typeof gameEntity.getBehaviorRefs !== "function") continue;
466
+ const refs = gameEntity.getBehaviorRefs();
467
+ const platformerRef = refs.find(
468
+ (r) => r.descriptor.key === /* @__PURE__ */ Symbol.for("zylem:behavior:platformer-3d")
469
+ );
470
+ if (!platformerRef || !gameEntity.body) continue;
471
+ const options = platformerRef.options;
472
+ if (!gameEntity.platformer) {
473
+ gameEntity.platformer = createPlatformer3DMovementComponent(options);
474
+ }
475
+ if (!gameEntity.$platformer) {
476
+ gameEntity.$platformer = createPlatformer3DInputComponent();
477
+ }
478
+ if (!gameEntity.platformerState) {
479
+ gameEntity.platformerState = createPlatformer3DStateComponent();
480
+ }
481
+ if (!platformerRef.fsm && gameEntity.$platformer && gameEntity.platformerState) {
482
+ platformerRef.fsm = new Platformer3DFSM({
483
+ input: gameEntity.$platformer,
484
+ state: gameEntity.platformerState
485
+ });
486
+ }
487
+ if (platformerRef.fsm && gameEntity.$platformer && gameEntity.platformerState) {
488
+ platformerRef.fsm.update(gameEntity.$platformer, gameEntity.platformerState);
489
+ }
490
+ }
491
+ this.movementBehavior.update(delta);
492
+ }
493
+ destroy(_ecs) {
494
+ this.movementBehavior.destroy();
495
+ }
496
+ };
497
+ var Platformer3DBehavior2 = defineBehavior({
498
+ name: "platformer-3d",
499
+ defaultOptions,
500
+ systemFactory: (ctx) => new Platformer3DBehaviorSystem(ctx.world, ctx.scene),
501
+ createHandle: (ref) => ({
502
+ getState: () => ref.fsm?.getState() ?? "idle" /* Idle */,
503
+ isGrounded: () => ref.fsm?.isGrounded() ?? false,
504
+ getJumpCount: () => ref.fsm?.getJumpCount() ?? 0,
505
+ onPlatformCollision: (ctx) => ref.fsm?.handleCollision(ctx)
506
+ })
507
+ });
508
+ export {
509
+ Platformer3DBehavior2 as Platformer3DBehavior,
510
+ Platformer3DEvent,
511
+ Platformer3DFSM,
512
+ Platformer3DBehavior as Platformer3DMovementBehavior,
513
+ Platformer3DState,
514
+ createPlatformer3DInputComponent,
515
+ createPlatformer3DMovementComponent,
516
+ createPlatformer3DStateComponent
517
+ };
518
+ //# sourceMappingURL=platformer-3d.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/behaviors/platformer-3d/components.ts","../../src/lib/behaviors/platformer-3d/platformer-3d-fsm.ts","../../src/lib/behaviors/platformer-3d/platformer-3d.behavior.ts","../../src/lib/behaviors/behavior-descriptor.ts","../../src/lib/behaviors/platformer-3d/platformer-3d.descriptor.ts"],"sourcesContent":["/**\n * Platformer 3D ECS Components\n * \n * Components for 3D platformer movement system including walking, running,\n * jumping with multi-jump support, and falling/landing states.\n */\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Platformer3DMovementComponent (capability / configuration)\n// Defines the movement capabilities and physics parameters\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface Platformer3DMovementComponent {\n\t/** Base walking speed */\n\twalkSpeed: number;\n\t/** Sprint/run speed */\n\trunSpeed: number;\n\t/** Initial jump force/velocity */\n\tjumpForce: number;\n\t/** Maximum number of jumps (1 = single jump, 2 = double jump, etc.) */\n\tmaxJumps: number;\n\t/** Gravity force (optional override) */\n\tgravity: number;\n\t/** Ray length for ground detection */\n\tgroundRayLength: number;\n\t/** Coyote time in seconds (grace period after leaving ground) */\n\tcoyoteTime: number;\n\t/** Jump buffer time in seconds (queue jump input before landing) */\n\tjumpBufferTime: number;\n\t/** Velocity multiplier when releasing jump button early (0-1) */\n\tjumpCutMultiplier: number;\n\t/** Time in seconds before multi-jump becomes available (0 = after button release) */\n\tmultiJumpWindowTime: number;\n}\n\nexport function createPlatformer3DMovementComponent(\n\toptions: Partial<Platformer3DMovementComponent> = {}\n): Platformer3DMovementComponent {\n\treturn {\n\t\twalkSpeed: options.walkSpeed ?? 12,\n\t\trunSpeed: options.runSpeed ?? 24,\n\t\tjumpForce: options.jumpForce ?? 12,\n\t\tmaxJumps: options.maxJumps ?? 1,\n\t\tgravity: options.gravity ?? 9.82,\n\t\tgroundRayLength: options.groundRayLength ?? 1.0,\n\t\tcoyoteTime: options.coyoteTime ?? 0.1,\n\t\tjumpBufferTime: options.jumpBufferTime ?? 0.1,\n\t\tjumpCutMultiplier: options.jumpCutMultiplier ?? 0.5,\n\t\tmultiJumpWindowTime: options.multiJumpWindowTime ?? 0.15, // 150ms default\n\t};\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Platformer3DInputComponent (intent)\n// Written by: Player controller, AI controller\n// Read by: Platformer3DBehavior\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface Platformer3DInputComponent {\n\t/** Horizontal movement input (-1 to 1) */\n\tmoveX: number;\n\t/** Forward/backward movement input (-1 to 1) */\n\tmoveZ: number;\n\t/** Jump button pressed */\n\tjump: boolean;\n\t/** Run/sprint button held */\n\trun: boolean;\n}\n\nexport function createPlatformer3DInputComponent(): Platformer3DInputComponent {\n\treturn {\n\t\tmoveX: 0,\n\t\tmoveZ: 0,\n\t\tjump: false,\n\t\trun: false,\n\t};\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Platformer3DStateComponent (runtime state)\n// Tracks the current state of the platformer entity\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface Platformer3DStateComponent {\n\t/** Is the entity on the ground */\n\tgrounded: boolean;\n\t/** Is currently in a jump arc */\n\tjumping: boolean;\n\t/** Is falling (not jumping) */\n\tfalling: boolean;\n\t/** Current jump count (for multi-jump) */\n\tjumpCount: number;\n\t/** Y position when jump started */\n\tjumpStartHeight: number;\n\t/** Current movement speed being applied */\n\tcurrentSpeed: number;\n\t/** Last known ground Y position */\n\tlastGroundedY: number;\n\t/** Was jump button pressed last frame (for edge detection) */\n\tjumpPressedLastFrame: boolean;\n\t\n\t// -- New fields --\n\t/** Is currently colliding with ground (from physics collision callback) */\n\tcollisionGrounded: boolean;\n\t/** Timestamp of last ground collision */\n\tgroundedCollisionTime: number;\n\t/** Time in seconds since last grounded */\n\ttimeSinceGrounded: number;\n\t/** Is a jump allowed due to buffer? */\n\tjumpBuffered: boolean;\n\t/** Time remaining in jump buffer */\n\tjumpBufferTimer: number;\n\t/** Is jump button currently held */\n\tjumpHeld: boolean;\n\t/** Has the jump cut been applied for this jump? */\n\tjumpCutApplied: boolean;\n\t/** Has jump button been released since last jump (required for multi-jump) */\n\tjumpReleasedSinceLastJump: boolean;\n\t/** Time since current jump started (for multi-jump window) */\n\ttimeSinceJump: number;\n}\n\nexport function createPlatformer3DStateComponent(): Platformer3DStateComponent {\n\treturn {\n\t\tgrounded: false,\n\t\tjumping: false,\n\t\tfalling: false,\n\t\tjumpCount: 0,\n\t\tjumpStartHeight: 0,\n\t\tcurrentSpeed: 0,\n\t\tlastGroundedY: 0,\n\t\tjumpPressedLastFrame: false,\n\t\tcollisionGrounded: false,\n\t\tgroundedCollisionTime: 0,\n\t\ttimeSinceGrounded: 0,\n\t\tjumpBuffered: false,\n\t\tjumpBufferTimer: 0,\n\t\tjumpHeld: false,\n\t\tjumpCutApplied: false,\n\t\tjumpReleasedSinceLastJump: true,\n\t\ttimeSinceJump: 0,\n\t};\n}\n","/**\n * Platformer 3D Finite State Machine\n * \n * Manages state transitions for 3D platformer movement:\n * - Idle: Standing still on ground\n * - Walking: Moving on ground (slow)\n * - Running: Moving on ground (fast)\n * - Jumping: Ascending from jump\n * - Falling: Descending (gravity)\n * - Landing: Just touched ground\n */\n\nimport { StateMachine, t } from 'typescript-fsm';\nimport type { Platformer3DInputComponent, Platformer3DStateComponent } from './components';\n\n/**\n * Simplified Collision Context interface\n */\nexport interface PlatformerCollisionContext {\n\tcontact: {\n\t\tnormal: { x: number; y: number; z?: number };\n\t};\n}\n\n/**\n * Platformer states\n */\nexport enum Platformer3DState {\n\tIdle = 'idle',\n\tWalking = 'walking',\n\tRunning = 'running',\n\tJumping = 'jumping',\n\tFalling = 'falling',\n\tLanding = 'landing',\n}\n\n/**\n * Events that trigger state transitions\n */\nexport enum Platformer3DEvent {\n\tWalk = 'walk',\n\tRun = 'run',\n\tJump = 'jump',\n\tFall = 'fall',\n\tLand = 'land',\n\tStop = 'stop',\n}\n\n/**\n * Context for the FSM\n */\nexport interface Platformer3DContext {\n\tinput: Platformer3DInputComponent;\n\tstate: Platformer3DStateComponent;\n}\n\n/**\n * Platformer 3D FSM\n */\nexport class Platformer3DFSM {\n\tmachine: StateMachine<Platformer3DState, Platformer3DEvent, never>;\n\n\tconstructor(private ctx: Platformer3DContext) {\n\t\tthis.machine = new StateMachine<Platformer3DState, Platformer3DEvent, never>(\n\t\t\tPlatformer3DState.Idle,\n\t\t\t[\n\t\t\t\t// Idle transitions\n\t\t\t\tt(Platformer3DState.Idle, Platformer3DEvent.Walk, Platformer3DState.Walking),\n\t\t\t\tt(Platformer3DState.Idle, Platformer3DEvent.Run, Platformer3DState.Running),\n\t\t\t\tt(Platformer3DState.Idle, Platformer3DEvent.Jump, Platformer3DState.Jumping),\n\t\t\t\tt(Platformer3DState.Idle, Platformer3DEvent.Fall, Platformer3DState.Falling),\n\t\t\t\t\n\t\t\t\t// Walking transitions\n\t\t\t\tt(Platformer3DState.Walking, Platformer3DEvent.Run, Platformer3DState.Running),\n\t\t\t\tt(Platformer3DState.Walking, Platformer3DEvent.Jump, Platformer3DState.Jumping),\n\t\t\t\tt(Platformer3DState.Walking, Platformer3DEvent.Stop, Platformer3DState.Idle),\n\t\t\t\tt(Platformer3DState.Walking, Platformer3DEvent.Fall, Platformer3DState.Falling),\n\t\t\t\t\n\t\t\t\t// Running transitions\n\t\t\t\tt(Platformer3DState.Running, Platformer3DEvent.Walk, Platformer3DState.Walking),\n\t\t\t\tt(Platformer3DState.Running, Platformer3DEvent.Jump, Platformer3DState.Jumping),\n\t\t\t\tt(Platformer3DState.Running, Platformer3DEvent.Stop, Platformer3DState.Idle),\n\t\t\t\tt(Platformer3DState.Running, Platformer3DEvent.Fall, Platformer3DState.Falling),\n\t\t\t\t\n\t\t\t\t// Jumping transitions\n\t\t\t\tt(Platformer3DState.Jumping, Platformer3DEvent.Fall, Platformer3DState.Falling),\n\t\t\t\tt(Platformer3DState.Jumping, Platformer3DEvent.Land, Platformer3DState.Landing),\n\t\t\t\tt(Platformer3DState.Jumping, Platformer3DEvent.Jump, Platformer3DState.Jumping), // Multi-jump\n\t\t\t\t\n\t\t\t\t// Falling transitions\n\t\t\t\tt(Platformer3DState.Falling, Platformer3DEvent.Land, Platformer3DState.Landing),\n\t\t\t\t\n\t\t\t\t// Landing transitions\n\t\t\t\tt(Platformer3DState.Landing, Platformer3DEvent.Walk, Platformer3DState.Walking),\n\t\t\t\tt(Platformer3DState.Landing, Platformer3DEvent.Run, Platformer3DState.Running),\n\t\t\t\tt(Platformer3DState.Landing, Platformer3DEvent.Stop, Platformer3DState.Idle),\n\t\t\t\t\n\t\t\t\t// Self-transitions (no-ops)\n\t\t\t\tt(Platformer3DState.Idle, Platformer3DEvent.Stop, Platformer3DState.Idle),\n\t\t\t]\n\t\t);\n\t}\n\n\t/**\n\t * Get the current state\n\t */\n\tgetState(): Platformer3DState {\n\t\treturn this.machine.getState();\n\t}\n\n\t/**\n\t * Dispatch an event to the FSM\n\t */\n\tdispatch(event: Platformer3DEvent): void {\n\t\tif (this.machine.can(event)) {\n\t\t\tthis.machine.dispatch(event);\n\t\t}\n\t}\n\n\t/**\n\t * Check if grounded\n\t */\n\tisGrounded(): boolean {\n\t\tconst state = this.getState();\n\t\treturn state === Platformer3DState.Idle || \n\t\t state === Platformer3DState.Walking || \n\t\t state === Platformer3DState.Running || \n\t\t state === Platformer3DState.Landing;\n\t}\n\n\t/**\n\t * Get current jump count from context\n\t */\n\tgetJumpCount(): number {\n\t\treturn this.ctx.state.jumpCount;\n\t}\n\n\t/**\n\t * Handle collision event to update ground state\n\t */\n\thandleCollision(ctx: PlatformerCollisionContext): void {\n\t\t// Check if collision is ground (normal pointing up)\n\t\t// Y normal > 0.5 means roughly within 60 degrees of up\n\t\tif (ctx.contact.normal.y > 0.5) {\n\t\t\tthis.ctx.state.collisionGrounded = true;\n\t\t\tthis.ctx.state.groundedCollisionTime = performance.now();\n\t\t}\n\t}\n\n\t/**\n\t * Update FSM based on current state\n\t */\n\tupdate(input: Platformer3DInputComponent, state: Platformer3DStateComponent): void {\n\t\t// Update context\n\t\tthis.ctx.input = input;\n\t\tthis.ctx.state = state;\n\n\t\t// Get current FSM state\n\t\tconst currentState = this.getState();\n\n\t\t// Determine movement state\n\t\tconst hasInput = Math.abs(input.moveX) > 0.1 || Math.abs(input.moveZ) > 0.1;\n\t\tconst isRunning = input.run;\n\n\t\t// Handle landing: if we're in falling state and now grounded, land\n\t\tif (currentState === Platformer3DState.Falling && state.grounded) {\n\t\t\tthis.dispatch(Platformer3DEvent.Land);\n\t\t\t// Don't return - allow immediate transition to movement state\n\t\t}\n\n\t\t// Send appropriate events based on state\n\t\tif (state.grounded) {\n\t\t\tif (hasInput) {\n\t\t\t\tif (isRunning) {\n\t\t\t\t\tthis.dispatch(Platformer3DEvent.Run);\n\t\t\t\t} else {\n\t\t\t\t\tthis.dispatch(Platformer3DEvent.Walk);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthis.dispatch(Platformer3DEvent.Stop);\n\t\t\t}\n\t\t} else {\n\t\t\t// Not grounded - check if we should be falling\n\t\t\t// Transition to falling if: We're in jumping state and now falling, \n\t\t\t// OR we walked off a ledge (idle/walking/running and now falling)\n\t\t\tif (state.falling) {\n\t\t\t\tthis.dispatch(Platformer3DEvent.Fall);\n\t\t\t}\n\t\t}\n\n\t\t// Handle jump input (edge detection handled in behavior)\n\t\tif (input.jump && !state.jumpPressedLastFrame) {\n\t\t\tthis.dispatch(Platformer3DEvent.Jump);\n\t\t}\n\t}\n}\n","/**\n * Platformer 3D Behavior System\n * \n * Handles 3D platformer physics including:\n * - Ground detection via raycasting\n * - Horizontal movement (walk/run)\n * - Jumping with multi-jump support\n * - Gravity application\n * - State synchronization with FSM\n */\n\nimport { Vector3, BufferGeometry, LineBasicMaterial, Line } from 'three';\nimport { Ray } from '@dimforge/rapier3d-compat';\nimport type {\n\tPlatformer3DMovementComponent,\n\tPlatformer3DInputComponent,\n\tPlatformer3DStateComponent,\n} from './components';\n\n/**\n * Entity with platformer components\n */\nexport interface Platformer3DEntity {\n\tuuid: string;\n\tbody: any; // RigidBody\n\tplatformer: Platformer3DMovementComponent;\n\t$platformer: Platformer3DInputComponent;\n\tplatformerState: Platformer3DStateComponent;\n\ttransformStore?: any;\n}\n\n/**\n * Platformer 3D Movement Behavior\n * \n * Core physics system for 3D platformer movement\n */\nexport class Platformer3DBehavior {\n\tprivate world: any;\n\tprivate scene: any;\n\tprivate rays: Map<string, Ray[]> = new Map();\n\tprivate debugLines: Map<string, any[]> = new Map(); // Store Line objects for debug visualization\n\n\tconstructor(world: any, scene: any) {\n\t\tthis.world = world;\n\t\tthis.scene = scene;\n\t}\n\n\t/**\n\t * Detect if entity is on the ground using raycasting\n\t */\n\t/**\n\t * Detect if entity is on the ground using raycasting (multi-sample: center + 4 corners)\n\t */\n\tprivate detectGround(entity: Platformer3DEntity): boolean {\n\t\tif (!this.world?.world || !entity.body) return false;\n\n\t\tconst translation = entity.body.translation();\n\t\tconst rayLength = entity.platformer.groundRayLength;\n\t\tconst radius = 0.4; // Sample radius (approx 80% of typical character collider radius)\n\n\t\t// Define ray offsets (center + 4 corners)\n\t\tconst offsets = [\n\t\t\t{ x: 0, z: 0 },\n\t\t\t{ x: radius, z: radius },\n\t\t\t{ x: -radius, z: radius },\n\t\t\t{ x: radius, z: -radius },\n\t\t\t{ x: -radius, z: -radius },\n\t\t];\n\n\t\t// Get or create rays for this entity\n\t\tlet entityRays = this.rays.get(entity.uuid);\n\t\tif (!entityRays) {\n\t\t\tentityRays = offsets.map(() => new Ray({ x: 0, y: 0, z: 0 }, { x: 0, y: -1, z: 0 }));\n\t\t\tthis.rays.set(entity.uuid, entityRays);\n\t\t}\n\n\t\tlet grounded = false;\n\t\t\n\t\t// Cast all rays\n\t\toffsets.forEach((offset, i) => {\n\t\t\tif (grounded) return; // Optimization: stop if already grounded (comment out for full debug viz)\n\n\t\t\tconst ray = entityRays![i];\n\t\t\tray.origin = { \n\t\t\t\tx: translation.x + offset.x, \n\t\t\t\ty: translation.y, \n\t\t\t\tz: translation.z + offset.z \n\t\t\t};\n\t\t\tray.dir = { x: 0, y: -1, z: 0 };\n\n\t\t\tconst hit = this.world.world.castRay(\n\t\t\t\tray,\n\t\t\t\trayLength,\n\t\t\t\ttrue,\n\t\t\t\tundefined,\n\t\t\t\tundefined,\n\t\t\t\tundefined,\n\t\t\t\tundefined,\n\t\t\t\t(collider: any) => {\n\t\t\t\t\tconst ref = collider._parent?.userData?.uuid;\n\t\t\t\t\tif (ref === entity.uuid) return false;\n\t\t\t\t\tgrounded = true;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t);\n\t\t});\n\n\t\t// Visualization logic omitted for brevity/performance (restore if needed)\n\t\tif (this.scene) {\n\t\t\tthis.updateDebugLines(entity, entityRays, grounded, rayLength);\n\t\t}\n\n\t\treturn grounded;\n\t}\n\n\tprivate updateDebugLines(entity: Platformer3DEntity, rays: Ray[], hasGround: boolean, length: number) {\n\t\tlet lines = this.debugLines.get(entity.uuid);\n\t\tif (!lines) {\n\t\t\tlines = rays.map(() => {\n\t\t\t\tconst geometry = new BufferGeometry().setFromPoints([new Vector3(), new Vector3()]);\n\t\t\t\tconst material = new LineBasicMaterial({ color: 0xff0000 });\n\t\t\t\tconst line = new Line(geometry, material);\n\t\t\t\tthis.scene.add(line);\n\t\t\t\treturn line;\n\t\t\t});\n\t\t\tthis.debugLines.set(entity.uuid, lines);\n\t\t}\n\n\t\trays.forEach((ray, i) => {\n\t\t\tconst line = lines![i];\n\t\t\tconst start = new Vector3(ray.origin.x, ray.origin.y, ray.origin.z);\n\t\t\tconst end = new Vector3(\n\t\t\t\tray.origin.x + ray.dir.x * length,\n\t\t\t\tray.origin.y + ray.dir.y * length,\n\t\t\t\tray.origin.z + ray.dir.z * length\n\t\t\t);\n\t\t\tline.geometry.setFromPoints([start, end]);\n\t\t\tline.material.color.setHex(hasGround ? 0x00ff00 : 0xff0000);\n\t\t});\n\t}\n\n\t/**\n\t * Apply horizontal movement based on input\n\t */\n\tprivate applyMovement(entity: Platformer3DEntity, delta: number): void {\n\t\tconst input = entity.$platformer;\n\t\tconst movement = entity.platformer;\n\t\tconst state = entity.platformerState;\n\n\t\t// Determine current speed\n\t\tconst speed = input.run ? movement.runSpeed : movement.walkSpeed;\n\t\tstate.currentSpeed = speed;\n\n\t\t// Calculate horizontal movement\n\t\tconst moveX = input.moveX * speed;\n\t\tconst moveZ = input.moveZ * speed;\n\n\t\t// Get current Y velocity from physics body (preserve vertical momentum)\n\t\tconst currentVel = entity.body.linvel();\n\n\t\t// Set complete velocity - horizontal from input, vertical preserved\n\t\tentity.transformStore.velocity.x = moveX;\n\t\tentity.transformStore.velocity.y = currentVel.y;\n\t\tentity.transformStore.velocity.z = moveZ;\n\t\tentity.transformStore.dirty.velocity = true;\n\t}\n\n\t/**\n\t * Handle jump logic with multi-jump support\n\t */\n\t/**\n\t * Handle jump logic with multi-jump, coyote time, and buffering\n\t */\n\tprivate handleJump(entity: Platformer3DEntity, delta: number): void {\n\t\tconst input = entity.$platformer;\n\t\tconst movement = entity.platformer;\n\t\tconst state = entity.platformerState;\n\n\t\t// Track time since last jump (for multi-jump window)\n\t\tif (state.jumping || state.falling) {\n\t\t\tstate.timeSinceJump += delta;\n\t\t}\n\n\t\t// Track jump button release (required for multi-jump)\n\t\tif (!input.jump && state.jumpHeld) {\n\t\t\tstate.jumpReleasedSinceLastJump = true;\n\t\t}\n\n\t\t// 1. Jump Buffering - queue on new press\n\t\tif (input.jump && !state.jumpPressedLastFrame) {\n\t\t\tstate.jumpBuffered = true;\n\t\t\tstate.jumpBufferTimer = movement.jumpBufferTime;\n\t\t}\n\t\t\n\t\tstate.jumpPressedLastFrame = input.jump;\n\t\tstate.jumpHeld = input.jump;\n\n\t\t// Decrement buffer timer\n\t\tif (state.jumpBuffered) {\n\t\t\tstate.jumpBufferTimer -= delta;\n\t\t\tif (state.jumpBufferTimer <= 0) {\n\t\t\t\tstate.jumpBuffered = false;\n\t\t\t}\n\t\t}\n\n\t\t// 2. Variable Jump Height (Jump Cut)\n\t\t// Only apply jump cut if we've been jumping for a bit (prevents cutting multi-jump immediately)\n\t\tconst minTimeBeforeCut = 0.1; // 100ms minimum before cut can apply\n\t\tconst canApplyCut = state.timeSinceJump >= minTimeBeforeCut;\n\t\tif (!input.jump && state.jumping && !state.jumpCutApplied && canApplyCut) {\n\t\t\tconst velocity = entity.body.linvel();\n\t\t\tentity.transformStore.velocity.y = velocity.y * movement.jumpCutMultiplier;\n\t\t\tentity.transformStore.dirty.velocity = true;\n\t\t\tstate.jumpCutApplied = true;\n\t\t}\n\n\t\t// Execute Jump (if buffered input exists)\n\t\tif (!state.jumpBuffered) return;\n\n\t\t// 3. Jump eligibility check\n\t\tconst inCoyoteWindow = !state.grounded && state.timeSinceGrounded <= movement.coyoteTime;\n\t\tconst isFirstJump = state.grounded || (inCoyoteWindow && state.jumpCount === 0);\n\t\t\n\t\t// Multi-jump requirements:\n\t\t// 1. Not grounded\n\t\t// 2. Have jumps remaining\n\t\t// 3. Button was released since last jump (no holding for double jump)\n\t\t// 4. Within the jump window (after multiJumpWindowTime has passed)\n\t\tconst hasJumpsRemaining = state.jumpCount < movement.maxJumps;\n\t\tconst buttonReleased = state.jumpReleasedSinceLastJump;\n\t\tconst inMultiJumpWindow = state.timeSinceJump >= movement.multiJumpWindowTime;\n\t\tconst canMultiJump = !state.grounded && hasJumpsRemaining && buttonReleased && inMultiJumpWindow;\n\t\t\n\t\t// DEBUG: Log jump attempt\n\t\tconsole.log('[JUMP DEBUG] Attempting jump:', {\n\t\t\tgrounded: state.grounded,\n\t\t\tjumpCount: state.jumpCount,\n\t\t\tmaxJumps: movement.maxJumps,\n\t\t\tisFirstJump,\n\t\t\tcanMultiJump,\n\t\t\t'--- Multi-jump conditions ---': '',\n\t\t\t'!grounded': !state.grounded,\n\t\t\thasJumpsRemaining,\n\t\t\tbuttonReleased,\n\t\t\tinMultiJumpWindow,\n\t\t\ttimeSinceJump: state.timeSinceJump.toFixed(3),\n\t\t\tmultiJumpWindowTime: movement.multiJumpWindowTime,\n\t\t});\n\t\t\n\t\tif (isFirstJump || canMultiJump) {\n\t\t\tconsole.log('[JUMP DEBUG] ✅ EXECUTING JUMP #' + (state.jumpCount + 1));\n\t\t\t\n\t\t\t// Consume buffered input\n\t\t\tstate.jumpBuffered = false;\n\t\t\t\n\t\t\t// Increment jump count and reset tracking\n\t\t\tstate.jumpCount++;\n\t\t\tstate.jumpReleasedSinceLastJump = false; // Must release again for next jump\n\t\t\tstate.timeSinceJump = 0; // Reset window timer\n\n\t\t\t// Record jump start height\n\t\t\tstate.jumpStartHeight = entity.body.translation().y;\n\n\t\t\t// Apply jump force\n\t\t\tstate.jumping = true;\n\t\t\tstate.falling = false;\n\t\t\tstate.jumpCutApplied = false;\n\n\t\t\t// Apply jump force via transform store\n\t\t\tentity.transformStore.velocity.y = movement.jumpForce;\n\t\t\tentity.transformStore.dirty.velocity = true;\n\t\t} else {\n\t\t\tconsole.log('[JUMP DEBUG] ❌ JUMP BLOCKED - conditions not met');\n\t\t}\n\t}\n\n\t/**\n\t * Apply gravity when not grounded\n\t */\n\tprivate applyGravity(entity: Platformer3DEntity, delta: number): void {\n\t\tconst movement = entity.platformer;\n\t\tconst state = entity.platformerState;\n\n\t\tif (state.grounded) return;\n\t\t\n\t\t// Skip gravity on the same frame as a jump (prevents overwriting jump velocity)\n\t\t// timeSinceJump is reset to 0 when we jump, so if it's very small, we just jumped\n\t\tif (state.jumping && state.timeSinceJump < 0.01) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Get current velocity from physics body and add gravity\n\t\tconst currentVel = entity.body.linvel();\n\t\tconst newYVelocity = currentVel.y - movement.gravity * delta;\n\n\t\t// Update the Y velocity in transform store (applyMovement already set x/z and old y)\n\t\tentity.transformStore.velocity.y = newYVelocity;\n\t\tentity.transformStore.dirty.velocity = true;\n\t}\n\n\t/**\n\t * Update entity state based on physics\n\t */\n\tprivate updateState(entity: Platformer3DEntity, delta: number): void {\n\t\tconst state = entity.platformerState;\n\n\t\t// 1. Reset grounded state before detection\n\t\tconst wasGrounded = state.grounded;\n\t\t\n\t\t// Read ACTUAL velocity from physics body\n\t\tconst velocity = entity.body.linvel();\n\t\t\n\t\tlet isGrounded = false;\n\t\t\n\t\t// Separate ground detection for airborne vs walking\n\t\tconst isAirborne = state.jumping || state.falling;\n\t\t\n\t\tif (isAirborne) {\n\t\t\t// Airborne: Must be FALLING (not jumping) with very low velocity to land\n\t\t\t// This prevents false grounding at jump apex or during descent\n\t\t\tconst nearGround = this.detectGround(entity);\n\t\t\tconst canLand = state.falling && !state.jumping; // Only land when falling, not jumping\n\t\t\tconst hasLanded = Math.abs(velocity.y) < 0.5; // Very strict threshold for landing\n\t\t\tisGrounded = nearGround && canLand && hasLanded;\n\t\t} else {\n\t\t\t// On ground (walking/idle): Normal raycast detection with lenient threshold\n\t\t\tconst nearGround = this.detectGround(entity);\n\t\t\tconst notFallingFast = velocity.y > -2.0; // Lenient - don't ground while falling fast\n\t\t\tisGrounded = nearGround && notFallingFast;\n\t\t}\n\n\t\tstate.grounded = isGrounded;\n\n\t\t// 2. Update Coyote Timer\n\t\tif (state.grounded) {\n\t\t\tstate.timeSinceGrounded = 0;\n\t\t\tstate.lastGroundedY = entity.body.translation().y;\n\t\t} else {\n\t\t\tstate.timeSinceGrounded += delta;\n\t\t}\n\n\t\t// 3. Landing Logic\n\t\tif (!wasGrounded && state.grounded) {\n\t\t\tstate.jumpCount = 0;\n\t\t\tstate.jumping = false;\n\t\t\tstate.falling = false;\n\t\t\tstate.jumpCutApplied = false;\n\t\t}\n\n\t\t// 4. Falling Logic (negative Y velocity and not grounded)\n\t\tif (velocity.y < -0.1 && !state.grounded) {\n\t\t\tif (state.jumping && velocity.y < 0) {\n\t\t\t\tstate.jumping = false;\n\t\t\t\tstate.falling = true;\n\t\t\t} else if (!state.jumping) {\n\t\t\t\tstate.falling = true;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Update all platformer entities\n\t */\n\tupdate(delta: number): void {\n\t\tif (!this.world?.collisionMap) return;\n\n\t\tfor (const [, entity] of this.world.collisionMap) {\n\t\t\tconst platformerEntity = entity as any;\n\n\t\t\t// Check if entity has platformer components\n\t\t\tif (!platformerEntity.platformer || !platformerEntity.$platformer || !platformerEntity.platformerState) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Update state first\n\t\t\tthis.updateState(platformerEntity, delta);\n\n\t\t\t// Apply movement\n\t\t\tthis.applyMovement(platformerEntity, delta);\n\n\t\t\t// Handle jumping\n\t\t\tthis.handleJump(platformerEntity, delta);\n\n\t\t\t// Apply gravity\n\t\t\tthis.applyGravity(platformerEntity, delta);\n\t\t}\n\t}\n\n\t/**\n\t * Cleanup\n\t */\n\tdestroy(): void {\n\t\tthis.rays.clear();\n\t}\n}\n","/**\n * BehaviorDescriptor\n *\n * Type-safe behavior descriptors that provide options inference.\n * Used with entity.use() to declaratively attach behaviors to entities.\n *\n * Each behavior can define its own handle type via `createHandle`,\n * providing behavior-specific methods with full type safety.\n */\n\nimport type { BehaviorSystemFactory } from './behavior-system';\n\n/**\n * Base handle returned by entity.use() for lazy access to behavior runtime.\n * FSM is null until entity is spawned and components are initialized.\n */\nexport interface BaseBehaviorHandle<\n O extends Record<string, any> = Record<string, any>,\n> {\n /** Get the FSM instance (null until entity is spawned) */\n getFSM(): any | null;\n /** Get the current options */\n getOptions(): O;\n /** Access the underlying behavior ref */\n readonly ref: BehaviorRef<O>;\n}\n\n/**\n * Reference to a behavior stored on an entity\n */\nexport interface BehaviorRef<\n O extends Record<string, any> = Record<string, any>,\n> {\n /** The behavior descriptor */\n descriptor: BehaviorDescriptor<O, any>;\n /** Merged options (defaults + overrides) */\n options: O;\n /** Optional FSM instance - set lazily when entity is spawned */\n fsm?: any;\n}\n\n/**\n * A typed behavior descriptor that associates a symbol key with:\n * - Default options (providing type inference)\n * - A system factory to create the behavior system\n * - An optional handle factory for behavior-specific methods\n */\nexport interface BehaviorDescriptor<\n O extends Record<string, any> = Record<string, any>,\n H extends Record<string, any> = Record<string, never>,\n I = unknown,\n> {\n /** Unique symbol identifying this behavior */\n readonly key: symbol;\n /** Default options (used for type inference) */\n readonly defaultOptions: O;\n /** Factory to create the behavior system */\n readonly systemFactory: BehaviorSystemFactory;\n /**\n * Optional factory to create behavior-specific handle methods.\n * These methods are merged into the handle returned by entity.use().\n */\n readonly createHandle?: (ref: BehaviorRef<O>) => H;\n}\n\n/**\n * The full handle type returned by entity.use().\n * Combines base handle with behavior-specific methods.\n */\nexport type BehaviorHandle<\n O extends Record<string, any> = Record<string, any>,\n H extends Record<string, any> = Record<string, never>,\n> = BaseBehaviorHandle<O> & H;\n\n/**\n * Configuration for defining a new behavior\n */\nexport interface DefineBehaviorConfig<\n O extends Record<string, any>,\n H extends Record<string, any> = Record<string, never>,\n I = unknown,\n> {\n /** Human-readable name for debugging */\n name: string;\n /** Default options - these define the type */\n defaultOptions: O;\n /** Factory function to create the system */\n systemFactory: BehaviorSystemFactory;\n /**\n * Optional factory to create behavior-specific handle methods.\n * The returned object is merged into the handle returned by entity.use().\n *\n * @example\n * ```typescript\n * createHandle: (ref) => ({\n * getLastHits: () => ref.fsm?.getLastHits() ?? null,\n * getMovement: (moveX, moveY) => ref.fsm?.getMovement(moveX, moveY) ?? { moveX, moveY },\n * }),\n * ```\n */\n createHandle?: (ref: BehaviorRef<O>) => H;\n}\n\n/**\n * Define a typed behavior descriptor.\n *\n * @example\n * ```typescript\n * export const WorldBoundary2DBehavior = defineBehavior({\n * name: 'world-boundary-2d',\n * defaultOptions: { boundaries: { top: 0, bottom: 0, left: 0, right: 0 } },\n * systemFactory: (ctx) => new WorldBoundary2DSystem(ctx.world),\n * createHandle: (ref) => ({\n * getLastHits: () => ref.fsm?.getLastHits() ?? null,\n * getMovement: (moveX: number, moveY: number) =>\n * ref.fsm?.getMovement(moveX, moveY) ?? { moveX, moveY },\n * }),\n * });\n *\n * // Usage - handle has getLastHits and getMovement with full types\n * const boundary = ship.use(WorldBoundary2DBehavior, { ... });\n * const hits = boundary.getLastHits(); // Fully typed!\n * ```\n */\nexport function defineBehavior<\n O extends Record<string, any>,\n H extends Record<string, any> = Record<string, never>,\n I = unknown,\n>(\n config: DefineBehaviorConfig<O, H, I>,\n): BehaviorDescriptor<O, H, I> {\n return {\n key: Symbol.for(`zylem:behavior:${config.name}`),\n defaultOptions: config.defaultOptions,\n systemFactory: config.systemFactory,\n createHandle: config.createHandle,\n };\n}\n","/**\n * Platformer 3D Behavior Descriptor\n * \n * Type-safe descriptor for the platformer 3D behavior system.\n * Provides entity.use() API for 3D platformer movement.\n */\n\nimport type { IWorld } from 'bitecs';\nimport { defineBehavior } from '../behavior-descriptor';\nimport type { BehaviorSystem } from '../behavior-system';\nimport { Platformer3DBehavior as Platformer3DMovementBehavior, Platformer3DEntity } from './platformer-3d.behavior';\nimport { Platformer3DFSM, Platformer3DState, PlatformerCollisionContext } from './platformer-3d-fsm';\nimport {\n\tcreatePlatformer3DMovementComponent,\n\tcreatePlatformer3DInputComponent,\n\tcreatePlatformer3DStateComponent,\n} from './components';\n\n/**\n * Platformer behavior options (typed for entity.use() autocomplete)\n */\nexport interface Platformer3DBehaviorOptions {\n\t/** Base walking speed (default: 12) */\n\twalkSpeed?: number;\n\t/** Sprint/run speed (default: 24) */\n\trunSpeed?: number;\n\t/** Initial jump force (default: 12) */\n\tjumpForce?: number;\n\t/** Maximum number of jumps (default: 1) */\n\tmaxJumps?: number;\n\t/** Gravity force (default: 9.82) */\n\tgravity?: number;\n\t/** Ray length for ground detection (default: 1.0) */\n\tgroundRayLength?: number;\n}\n\nconst defaultOptions: Platformer3DBehaviorOptions = {\n\twalkSpeed: 12,\n\trunSpeed: 24,\n\tjumpForce: 12,\n\tmaxJumps: 1,\n\tgravity: 9.82,\n\tgroundRayLength: 1.0,\n};\n\n/**\n * Adapter that wraps Platformer3DBehavior as a BehaviorSystem\n */\nclass Platformer3DBehaviorSystem implements BehaviorSystem {\n\tprivate movementBehavior: Platformer3DMovementBehavior;\n\n\tconstructor(private world: any, private scene: any) {\n\t\tthis.movementBehavior = new Platformer3DMovementBehavior(world, scene);\n\t}\n\n\tupdate(ecs: IWorld, delta: number): void {\n\t\tif (!this.world?.collisionMap) return;\n\n\t\t// Initialize ECS components on entities with platformer behavior refs\n\t\tfor (const [, entity] of this.world.collisionMap) {\n\t\t\tconst gameEntity = entity as any;\n\n\t\t\tif (typeof gameEntity.getBehaviorRefs !== 'function') continue;\n\n\t\t\tconst refs = gameEntity.getBehaviorRefs();\n\t\t\tconst platformerRef = refs.find((r: any) =>\n\t\t\t\tr.descriptor.key === Symbol.for('zylem:behavior:platformer-3d')\n\t\t\t);\n\n\t\t\tif (!platformerRef || !gameEntity.body) continue;\n\n\t\t\tconst options = platformerRef.options as Platformer3DBehaviorOptions;\n\n\t\t\t// Ensure entity has platformer components (initialized once)\n\t\t\tif (!gameEntity.platformer) {\n\t\t\t\tgameEntity.platformer = createPlatformer3DMovementComponent(options);\n\t\t\t}\n\n\t\t\tif (!gameEntity.$platformer) {\n\t\t\t\tgameEntity.$platformer = createPlatformer3DInputComponent();\n\t\t\t}\n\n\t\t\tif (!gameEntity.platformerState) {\n\t\t\t\tgameEntity.platformerState = createPlatformer3DStateComponent();\n\t\t\t}\n\n\t\t\t// Create FSM lazily and attach to the BehaviorRef\n\t\t\tif (!platformerRef.fsm && gameEntity.$platformer && gameEntity.platformerState) {\n\t\t\t\tplatformerRef.fsm = new Platformer3DFSM({\n\t\t\t\t\tinput: gameEntity.$platformer,\n\t\t\t\t\tstate: gameEntity.platformerState,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Update FSM to sync state with physics\n\t\t\tif (platformerRef.fsm && gameEntity.$platformer && gameEntity.platformerState) {\n\t\t\t\tplatformerRef.fsm.update(gameEntity.$platformer, gameEntity.platformerState);\n\t\t\t}\n\t\t}\n\n\t\t// Delegate to the movement behavior\n\t\tthis.movementBehavior.update(delta);\n\t}\n\n\tdestroy(_ecs: IWorld): void {\n\t\tthis.movementBehavior.destroy();\n\t}\n}\n\n/**\n * Platformer3DBehavior - typed descriptor for 3D platformer movement\n * \n * Provides complete 3D platformer physics including:\n * - Walking and running\n * - Jumping with multi-jump support\n * - Gravity and falling\n * - Ground detection\n * \n * @example\n * ```typescript\n * import { Platformer3DBehavior } from \"@zylem/game-lib\";\n * \n * const player = await playgroundActor('player');\n * const platformer = player.use(Platformer3DBehavior, {\n * walkSpeed: 12,\n * runSpeed: 24,\n * jumpForce: 12,\n * maxJumps: 2, // Double jump!\n * });\n * \n * // In update loop\n * player.onUpdate(({ inputs }) => {\n * player.$platformer.moveX = inputs.p1.axes.Horizontal.value;\n * player.$platformer.moveZ = inputs.p1.axes.Vertical.value;\n * player.$platformer.jump = inputs.p1.buttons.A.held > 0;\n * player.$platformer.run = inputs.p1.shoulders.LTrigger.held > 0;\n * \n * const state = platformer.getState();\n * const grounded = platformer.isGrounded();\n * });\n * ```\n */\nexport const Platformer3DBehavior = defineBehavior<\n\tPlatformer3DBehaviorOptions,\n\t{\n\t\tgetState: () => Platformer3DState;\n\t\tisGrounded: () => boolean;\n\t\tgetJumpCount: () => number;\n\t\tonPlatformCollision: (ctx: PlatformerCollisionContext) => void;\n\t},\n\tPlatformer3DEntity\n>({\n\tname: 'platformer-3d',\n\tdefaultOptions,\n\tsystemFactory: (ctx) => new Platformer3DBehaviorSystem(ctx.world, ctx.scene),\n\tcreateHandle: (ref) => ({\n\t\tgetState: () => ref.fsm?.getState() ?? Platformer3DState.Idle,\n\t\tisGrounded: () => ref.fsm?.isGrounded() ?? false,\n\t\tgetJumpCount: () => ref.fsm?.getJumpCount() ?? 0,\n\t\tonPlatformCollision: (ctx) => ref.fsm?.handleCollision(ctx),\n\t}),\n});\n"],"mappings":"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","names":["Platformer3DState","Platformer3DEvent","Platformer3DBehavior"]}