incanto 0.31.0 → 0.33.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.
Files changed (54) hide show
  1. package/bin/incanto-play.mjs +7 -2
  2. package/dist/2d.d.ts +59 -2
  3. package/dist/2d.js +3 -3
  4. package/dist/3d.d.ts +28 -3
  5. package/dist/3d.js +4 -4
  6. package/dist/{behavior-D_jMpFh8.d.ts → behavior-CKwTCjfR.d.ts} +140 -1
  7. package/dist/{create-game-z5XaB1p5.js → create-game-D2QU5x7S.js} +16 -8
  8. package/dist/{create-game-DqOjMBUS.js → create-game-sFuTLqjD.js} +22 -10
  9. package/dist/debug.d.ts +1 -1
  10. package/dist/{duplicate-KRPtUtzl.js → duplicate-BgnG1Lqz.js} +1 -1
  11. package/dist/editor.js +35 -0
  12. package/dist/{environment-presets--DigHNg4.js → environment-presets-CQtEGogB.js} +2 -2
  13. package/dist/{gameplay-BftxM_It.js → gameplay-BpQCbABv.js} +90 -2
  14. package/dist/gameplay.d.ts +1 -1
  15. package/dist/gameplay.js +1 -1
  16. package/dist/index.d.ts +135 -23
  17. package/dist/index.js +6 -59
  18. package/dist/{loader-BlaRQGaA.js → loader-Buk8Bu1h.js} +53 -0
  19. package/dist/{loader-BYBrqTxP.d.ts → loader-COn5fS0o.d.ts} +1 -1
  20. package/dist/net.d.ts +1 -1
  21. package/dist/net.js +3 -3
  22. package/dist/{pathfinding-BwhqPD3i.d.ts → pathfinding-DUw9mir9.d.ts} +1 -1
  23. package/dist/{physics-2d-D9wquBvK.js → physics-2d-DjXR5DMu.js} +12 -2
  24. package/dist/{physics-3d-CnPygVGo.js → physics-3d-DF8npb1O.js} +55 -9
  25. package/dist/react.d.ts +1 -1
  26. package/dist/react.js +1 -1
  27. package/dist/{register-Dzkd6-os.js → register-CscIzJEO.js} +551 -13
  28. package/dist/{register-CUY284Is.js → register-CtI-itec.js} +2 -2
  29. package/dist/{register-tkR_8tWg.js → register-FIJtNbub.js} +145 -8
  30. package/dist/test-BRxLd2jH.js +642 -0
  31. package/dist/test.d.ts +55 -3
  32. package/dist/test.js +2 -392
  33. package/dist/{touch-031PxtCR.js → touch-BoNg_MnF.js} +2 -1
  34. package/dist/vite.js +1 -1
  35. package/editor/assets/{agent8-o27_Y1xN.js → agent8-BdDP3xKW.js} +1 -1
  36. package/editor/assets/{debug-DfcWX3uW.js → debug-DbjTyTlC.js} +1 -1
  37. package/editor/assets/{index-C9fb5QcT.js → index-BWCudoz1.js} +90 -90
  38. package/editor/index.html +1 -1
  39. package/package.json +1 -1
  40. package/schemas/scene.schema.json +520 -0
  41. package/skills/README.md +14 -2
  42. package/skills/incanto-audio.md +43 -0
  43. package/skills/incanto-building-2d-games.md +52 -0
  44. package/skills/incanto-building-3d-games.md +16 -0
  45. package/skills/incanto-hud.md +41 -0
  46. package/skills/incanto-node-reference.md +89 -0
  47. package/skills/incanto-physics-and-input.md +50 -0
  48. package/skills/incanto-verifying-your-game.md +86 -30
  49. package/templates-app/beacon-isle-3d/docs/project-3d-rules.md +40 -19
  50. package/templates-app/beacon-isle-3d/package.json +1 -1
  51. package/templates-app/tps-3d/docs/project-3d-rules.md +41 -19
  52. package/templates-app/tps-3d/package.json +1 -1
  53. package/templates-app/village-quest-3d/docs/project-3d-rules.md +40 -19
  54. package/templates-app/village-quest-3d/package.json +1 -1
@@ -1,9 +1,9 @@
1
- import { f as Node } from "./loader-BlaRQGaA.js";
2
- import { t as registerCoreNodes } from "./register-Dzkd6-os.js";
1
+ import { f as Node } from "./loader-Buk8Bu1h.js";
2
+ import { t as registerCoreNodes } from "./register-CscIzJEO.js";
3
3
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
4
4
  import { t as Rng } from "./rng-DP-SR7eg.js";
5
5
  import { i as getNodeSchema, l as registerNode } from "./registry-CJdGpT2V.js";
6
- import { r as effectiveOrder } from "./duplicate-KRPtUtzl.js";
6
+ import { r as effectiveOrder } from "./duplicate-BgnG1Lqz.js";
7
7
  import { i as applyParticlePreset, r as PARTICLE_PRESET_NAMES, t as ParticleSim } from "./particle-sim-Bw7hB93B.js";
8
8
  import { AdditiveBlending, BufferAttribute, BufferGeometry, CanvasTexture, Color, DynamicDrawUsage, Group, InstancedMesh, MathUtils, Matrix4, Mesh, MeshBasicMaterial, NearestFilter, NormalBlending, Object3D, PlaneGeometry } from "three";
9
9
  //#region src/2d/physics/collider-validate.ts
@@ -214,6 +214,28 @@ var CharacterBody2D = class extends PhysicsBody2D {
214
214
  if (!this._physics) throw new IncantoError("TREE_VIOLATION", this.collider.shape === void 0 ? `CharacterBody2D '${this.name}' has no collider — set a collider prop (physics skips collider-less bodies).` : `CharacterBody2D '${this.name}' has no physics world — await enablePhysics2D(engine) first.`);
215
215
  this._physics.moveAndSlide(this);
216
216
  }
217
+ /** @internal Set by the physics step from the KCC's resolved collisions. */
218
+ _onWall = 0;
219
+ /** @internal */
220
+ _onCeiling = false;
221
+ /**
222
+ * Touching a wall, and which side (-1 left, +1 right, 0 neither).
223
+ *
224
+ * Wall jump and wall slide are unbuildable without it — not awkward to build,
225
+ * impossible: `isOnFloor()` was the only surface a character could report, so
226
+ * even a hand-written behavior had nothing to ask.
227
+ */
228
+ isOnWall() {
229
+ return this._onWall !== 0;
230
+ }
231
+ /** -1 the wall is to the left, +1 to the right, 0 no wall. */
232
+ wallSide() {
233
+ return this._onWall;
234
+ }
235
+ /** Head hit something — cut the jump short, the way every platformer does. */
236
+ isOnCeiling() {
237
+ return this._onCeiling;
238
+ }
217
239
  isOnFloor() {
218
240
  return this._grounded;
219
241
  }
@@ -521,6 +543,18 @@ function clampCentered(v, lo, hi) {
521
543
  * - `platformer`: x movement + gravity (from the scene `physics.gravity[1]`,
522
544
  * default 980) + jump (`jumpHeight` px → impulse √(2·g·h))
523
545
  * - `topDown`: full-axis movement, no gravity
546
+ *
547
+ * ## Why the extra platformer props exist
548
+ *
549
+ * A jump that only works while `isOnFloor()` is true feels BROKEN, and players
550
+ * do not report it as "the coyote time is missing" — they report the game as
551
+ * unresponsive. The whole schema used to be five props, so a JSON-only game got
552
+ * a character that felt wrong and could not drive `AnimatedSprite2D.play('run')`
553
+ * either. `examples/platformer-2d` hand-rolls all of this in a 541-line
554
+ * behavior; these are those constants, as props.
555
+ *
556
+ * Every one defaults to OFF (0 / 1 / false), so an existing scene behaves
557
+ * exactly as before until it asks for something.
524
558
  */
525
559
  var CharacterController2D = class extends Node {
526
560
  static typeName = "CharacterController2D";
@@ -532,14 +566,48 @@ var CharacterController2D = class extends Node {
532
566
  maxSpeed: { default: 220 },
533
567
  jumpHeight: { default: 64 },
534
568
  moveAction: { default: "move" },
535
- jumpAction: { default: "jump" }
569
+ jumpAction: { default: "jump" },
570
+ /** Jump this long AFTER walking off a ledge. The single biggest one. */
571
+ coyoteSeconds: { default: 0 },
572
+ /** Press jump this long BEFORE landing and still get it. */
573
+ jumpBufferSeconds: { default: 0 },
574
+ /** Releasing jump early cuts the rise to this fraction (1 = fixed height). */
575
+ jumpCutMultiplier: { default: 1 },
576
+ /** 2 = double jump. Extra jumps work in mid-air. */
577
+ maxJumps: { default: 1 },
578
+ /** 0 = no dash. Speed while dashing, in px/s. */
579
+ dashSpeed: { default: 0 },
580
+ dashSeconds: { default: .15 },
581
+ dashAction: { default: "dash" },
582
+ /** 0 = no wall slide. Terminal fall speed while against a wall (px/s). */
583
+ wallSlideSpeed: { default: 0 },
584
+ /** 0 = no wall jump. `[away, up]` impulse in px/s. */
585
+ wallJumpImpulse: { default: [0, 0] }
536
586
  };
587
+ static signals = ["movementStateChanged"];
537
588
  mode = "platformer";
538
589
  maxSpeed = 220;
539
590
  /** Pixels (platformer mode). */
540
591
  jumpHeight = 64;
541
592
  moveAction = "move";
542
593
  jumpAction = "jump";
594
+ coyoteSeconds = 0;
595
+ jumpBufferSeconds = 0;
596
+ jumpCutMultiplier = 1;
597
+ maxJumps = 1;
598
+ dashSpeed = 0;
599
+ dashSeconds = .15;
600
+ dashAction = "dash";
601
+ wallSlideSpeed = 0;
602
+ wallJumpImpulse = [0, 0];
603
+ /** What the character is doing — drive `AnimatedSprite2D.play(state)` off it. */
604
+ state = "idle";
605
+ coyoteLeft = 0;
606
+ bufferLeft = 0;
607
+ jumpsUsed = 0;
608
+ dashLeft = 0;
609
+ dashDir = 1;
610
+ rising = false;
543
611
  onReady() {
544
612
  if (!(this.parent instanceof CharacterBody2D)) throw new IncantoError("TREE_VIOLATION", `CharacterController2D '${this.name}' must be a child of a CharacterBody2D (parent is '${this.parent?.name ?? "none"}').`);
545
613
  if (this.mode !== "platformer" && this.mode !== "topDown") throw new IncantoError("PROP_TYPE_MISMATCH", `CharacterController2D.mode must be 'platformer' or 'topDown', got '${this.mode}'.`);
@@ -557,12 +625,81 @@ var CharacterController2D = class extends Node {
557
625
  return;
558
626
  }
559
627
  const gravityY = (engine.scene.physics?.gravity)?.[1] ?? 980;
628
+ const grounded = body.isOnFloor();
629
+ const jumpImpulse = -Math.sqrt(2 * gravityY * this.jumpHeight);
630
+ if (this.dashLeft > 0) {
631
+ this.dashLeft -= dt;
632
+ body.velocity[0] = this.dashDir * this.dashSpeed;
633
+ body.velocity[1] = 0;
634
+ body.moveAndSlide();
635
+ this.setState("dash");
636
+ return;
637
+ }
638
+ if (this.dashSpeed > 0 && input.justPressed(this.dashAction)) {
639
+ this.dashLeft = this.dashSeconds;
640
+ this.dashDir = dir.x !== 0 ? Math.sign(dir.x) : this.dashDir;
641
+ body.velocity[0] = this.dashDir * this.dashSpeed;
642
+ body.velocity[1] = 0;
643
+ body.moveAndSlide();
644
+ this.setState("dash");
645
+ return;
646
+ }
560
647
  body.velocity[0] = dir.x * this.maxSpeed;
561
- if (body.isOnFloor()) {
562
- const jumped = input.justPressed(this.jumpAction);
563
- body.velocity[1] = jumped ? -Math.sqrt(2 * gravityY * this.jumpHeight) : 20;
564
- } else body.velocity[1] = (body.velocity[1] ?? 0) + gravityY * dt;
648
+ if (grounded) {
649
+ this.coyoteLeft = this.coyoteSeconds;
650
+ this.jumpsUsed = 0;
651
+ } else {
652
+ this.coyoteLeft = Math.max(0, this.coyoteLeft - dt);
653
+ if (this.coyoteLeft <= 0 && this.jumpsUsed === 0) this.jumpsUsed = 1;
654
+ }
655
+ if (input.justPressed(this.jumpAction)) this.bufferLeft = Math.max(this.jumpBufferSeconds, 0);
656
+ else this.bufferLeft = Math.max(0, this.bufferLeft - dt);
657
+ const wantsJump = this.bufferLeft > 0 || input.justPressed(this.jumpAction);
658
+ const onWall = this.wallSlideSpeed > 0 || this.wallJumpImpulse.some((v) => v !== 0) ? body.isOnWall() : false;
659
+ if (wantsJump && onWall && !grounded && this.wallJumpImpulse.some((v) => v !== 0)) {
660
+ const away = -body.wallSide();
661
+ body.velocity[0] = away * (this.wallJumpImpulse[0] ?? 0);
662
+ body.velocity[1] = -(this.wallJumpImpulse[1] ?? 0);
663
+ this.bufferLeft = 0;
664
+ this.rising = true;
665
+ this.jumpsUsed = 1;
666
+ } else if (wantsJump && (grounded || this.coyoteLeft > 0 || this.jumpsUsed < this.maxJumps)) {
667
+ body.velocity[1] = jumpImpulse;
668
+ this.bufferLeft = 0;
669
+ this.coyoteLeft = 0;
670
+ this.rising = true;
671
+ this.jumpsUsed = grounded ? 1 : this.jumpsUsed + 1;
672
+ } else if (grounded) {
673
+ body.velocity[1] = 20;
674
+ this.rising = false;
675
+ } else {
676
+ body.velocity[1] = (body.velocity[1] ?? 0) + gravityY * dt;
677
+ if (this.rising && this.jumpCutMultiplier < 1 && !input.isPressed(this.jumpAction)) {
678
+ body.velocity[1] = (body.velocity[1] ?? 0) * this.jumpCutMultiplier;
679
+ this.rising = false;
680
+ }
681
+ if ((body.velocity[1] ?? 0) >= 0) this.rising = false;
682
+ if (this.wallSlideSpeed > 0 && onWall && (body.velocity[1] ?? 0) > this.wallSlideSpeed) body.velocity[1] = this.wallSlideSpeed;
683
+ }
684
+ if (body.isOnCeiling() && (body.velocity[1] ?? 0) < 0) {
685
+ body.velocity[1] = 0;
686
+ this.rising = false;
687
+ }
565
688
  body.moveAndSlide();
689
+ const vy = body.velocity[1] ?? 0;
690
+ if (this.wallSlideSpeed > 0 && !body.isOnFloor() && body.isOnWall() && vy > 0) this.setState("wallSlide");
691
+ else if (body.isOnFloor()) this.setState(Math.abs(dir.x) > .01 ? "run" : "idle");
692
+ else this.setState(vy < 0 ? "jump" : "fall");
693
+ }
694
+ /**
695
+ * Emits `movementStateChanged(state)` on a change — the 3D sibling has had
696
+ * this since it shipped, and it is what drives `AnimatedSprite2D.play(...)`
697
+ * from scene JSON instead of a behavior polling velocities.
698
+ */
699
+ setState(next) {
700
+ if (next === this.state) return;
701
+ this.state = next;
702
+ this.emit("movementStateChanged", next);
566
703
  }
567
704
  };
568
705
  //#endregion