incanto 0.64.1 → 0.66.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 (59) hide show
  1. package/dist/2d.d.ts +38 -3
  2. package/dist/2d.js +3 -3
  3. package/dist/3d.d.ts +130 -3
  4. package/dist/3d.js +5 -5
  5. package/dist/{audio-player-D5GJgb_x.d.ts → audio-player-_UAcHxnC.d.ts} +1 -1
  6. package/dist/{behavior-DsgayMsH.d.ts → behavior-BXNLfIJk.d.ts} +128 -0
  7. package/dist/{create-game-B746rYbr.js → create-game-DYJCIzO0.js} +67 -6
  8. package/dist/{create-game-B1KM6dA6.js → create-game-Dd7H4bJV.js} +6 -6
  9. package/dist/debug.d.ts +1 -1
  10. package/dist/{duplicate-BOOKmkQ7.js → duplicate-CqSAtdrh.js} +1 -1
  11. package/dist/{environment-presets-D2vzw583.js → environment-presets-BlPsEmq6.js} +70 -6
  12. package/dist/{gameplay-CLgFdkh5.js → gameplay-D1RADWu3.js} +374 -18
  13. package/dist/gameplay.d.ts +94 -3
  14. package/dist/gameplay.js +2 -2
  15. package/dist/index.d.ts +4 -4
  16. package/dist/index.js +5 -5
  17. package/dist/{loader-DolLJWJn.d.ts → loader-Cga7FVP4.d.ts} +1 -1
  18. package/dist/{loader-zDynoew_.js → loader-lQDCwNag.js} +59 -3
  19. package/dist/net.d.ts +2 -2
  20. package/dist/net.js +1 -1
  21. package/dist/{physics-2d-BPJcJRRP.js → physics-2d-Cji5A6sX.js} +32 -4
  22. package/dist/{physics-3d-BOO58xzw.js → physics-3d-BkHjwJgI.js} +49 -5
  23. package/dist/{quiet-rapier-BAJ4K94N.js → quiet-rapier-C6fW4zcW.js} +14 -1
  24. package/dist/react.d.ts +1 -1
  25. package/dist/react.js +1 -1
  26. package/dist/{register-Btm7_Emq.js → register-BmuqYTiY.js} +44 -1
  27. package/dist/{register-dGsbnJ87.js → register-D0CxCveZ.js} +38 -4
  28. package/dist/{replay-C5x2vPF5.d.ts → replay-BHoB6fCU.d.ts} +1 -1
  29. package/dist/{replay-DKRmiVjk.js → replay-IsZbNu6d.js} +2 -2
  30. package/dist/{split-screen-Dx0LvzqS.js → split-screen-D_i7GRcY.js} +2 -2
  31. package/dist/{split-screen-D7OopelJ.d.ts → split-screen-eJUFjhi1.d.ts} +2 -2
  32. package/dist/{src-CNS2xQB1.js → src-BTLbXFPZ.js} +1 -1
  33. package/dist/{teardown-BwhkcNt8.js → teardown-byR9USax.js} +1 -1
  34. package/dist/{test-Ccob3X5i.js → test-DgrD0jHD.js} +13 -13
  35. package/dist/test.d.ts +4 -4
  36. package/dist/test.js +2 -2
  37. package/dist/vite.js +2 -2
  38. package/editor/assets/{agent8-Di-UEn5O.js → agent8-9N-Pd_YS.js} +1 -1
  39. package/editor/assets/{debug-DWztJ5_y.js → debug-CkbJICYp.js} +1 -1
  40. package/editor/assets/{index-fct4H89G.js → index-CeDhIPTC.js} +53 -53
  41. package/editor/index.html +1 -1
  42. package/package.json +1 -1
  43. package/schemas/scene.schema.json +19 -0
  44. package/skills/incanto-3d-character.md +11 -1
  45. package/skills/incanto-behaviors-and-scripts.md +21 -0
  46. package/skills/incanto-gameplay-behaviors.md +177 -9
  47. package/skills/incanto-node-reference.md +59 -1
  48. package/skills/incanto-physics-and-input.md +84 -1
  49. package/skills/incanto-web-integration.md +24 -3
  50. package/templates-app/beacon-isle-3d/package.json +1 -1
  51. package/templates-app/platformer-2d/package.json +1 -1
  52. package/templates-app/star-survivor/package.json +1 -1
  53. package/templates-app/tps-3d/package.json +1 -1
  54. package/templates-app/village-quest-3d/package.json +1 -1
  55. package/templates-app/beacon-isle-3d/coverage.json +0 -9
  56. package/templates-app/platformer-2d/coverage.json +0 -5
  57. package/templates-app/star-survivor/coverage.json +0 -5
  58. package/templates-app/tps-3d/coverage.json +0 -5
  59. package/templates-app/village-quest-3d/coverage.json +0 -9
package/editor/index.html CHANGED
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>Incanto Scene Editor</title>
7
7
  <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><rect width='16' height='16' rx='3' fill='%236ee7dc'/><text x='8' y='12' text-anchor='middle' font-size='11' font-family='monospace' fill='%230e1018'>i</text></svg>" />
8
- <script type="module" crossorigin src="./assets/index-fct4H89G.js"></script>
8
+ <script type="module" crossorigin src="./assets/index-CeDhIPTC.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="./assets/GameServer-C56iOUgF.js">
10
10
  </head>
11
11
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "incanto",
3
- "version": "0.64.1",
3
+ "version": "0.66.0",
4
4
  "description": "Vibe-coding-first web game engine SDK — JSON-driven scenes on three.js",
5
5
  "keywords": [
6
6
  "game-engine",
@@ -2350,6 +2350,10 @@
2350
2350
  "type": "number",
2351
2351
  "default": 0.01
2352
2352
  },
2353
+ "stepHeight": {
2354
+ "type": "number",
2355
+ "default": 0.35
2356
+ },
2353
2357
  "mouseLook": {
2354
2358
  "type": "boolean",
2355
2359
  "default": true
@@ -3423,6 +3427,9 @@
3423
3427
  "damping": {
3424
3428
  "type": "number",
3425
3429
  "default": 5
3430
+ },
3431
+ "collide": {
3432
+ "default": null
3426
3433
  }
3427
3434
  },
3428
3435
  "additionalProperties": false
@@ -3577,6 +3584,9 @@
3577
3584
  "damping": {
3578
3585
  "type": "number",
3579
3586
  "default": 5
3587
+ },
3588
+ "collide": {
3589
+ "default": null
3580
3590
  }
3581
3591
  },
3582
3592
  "additionalProperties": false
@@ -5670,6 +5680,15 @@
5670
5680
  "minItems": 3,
5671
5681
  "maxItems": 3,
5672
5682
  "default": [0, 0, 0]
5683
+ },
5684
+ "angularVelocity": {
5685
+ "type": "array",
5686
+ "items": {
5687
+ "type": "number"
5688
+ },
5689
+ "minItems": 3,
5690
+ "maxItems": 3,
5691
+ "default": [0, 0, 0]
5673
5692
  }
5674
5693
  },
5675
5694
  "additionalProperties": false
@@ -269,7 +269,17 @@ target being "where I'm walking." There is ONE facing formula; only the directio
269
269
  its FACE, not its back. (Headless, no three.js: with the yaw you set, the model's
270
270
  forward is `(sin(yaw), cos(yaw))`; dot it with the desired unit dir — `< 0` = wrong way.)
271
271
 
272
- **NON-player models (enemies/NPCs) face + animate via the SAME rules, by hand.**
272
+ **NON-player models (enemies/NPCs) face by the SAME rule — but do not hand-write
273
+ it if a built-in is moving them.** `Patrol` and `Chase` take `facePath` (+
274
+ `turnSpeed`), which applies exactly the formula above to the skin you name:
275
+
276
+ ```jsonc
277
+ { "name": "Hunt", "type": "Node3D",
278
+ "script": { "name": "Chase",
279
+ "props": { "target": "/root/Player", "moveParent": true, "facePath": "../Skin" } } }
280
+ ```
281
+
282
+ Only a model driven by YOUR OWN behavior needs the formula written out.
273
283
  `CharacterController3D` automates this for the player; a custom-driven model does it
274
284
  itself — face with the formula above, and drive `skin.animation` from your AI state (see
275
285
  incanto-gameplay-behaviors.md "Driving a model's animation from a custom AI"). Ground a
@@ -84,6 +84,27 @@ under an existing name (re-registering a different class without it → `DUPLICA
84
84
  with `new Engine({ seed: 42 })` a run replays identically (scripted verification).
85
85
  - `this.log` — the engine log channel (`debug/info/warn/error(...parts)`; debug overlay
86
86
  and headless harnesses tail it via `entries()` / the live `added` signal)
87
+ - `this.physics` — the physics world, for the queries AI asks:
88
+ `castRay(origin, dir, maxLen, exclude?)` for line of sight, `castSphere(...)`
89
+ (3D) for a probe that must not skim past a wall. `null` in a game with no
90
+ physics. Pass `this.node` as `exclude` when casting from your own body.
91
+ See `incanto-physics-and-input.md`.
92
+ - `this.enabled` — whether this behavior's `update`/`fixedUpdate` run, with
93
+ `enable()` / `disable()` to flip it. Every behavior has it, it is authorable
94
+ as a `script` prop, and because they are METHODS a connection can call them:
95
+
96
+ ```jsonc
97
+ { "signal": "spotted", "from": "Eyes", "to": "Hunt", "handler": "enable" }
98
+ ```
99
+
100
+ That is what makes an enemy STATE MACHINE expressible in JSON — a node carries
101
+ one behavior, so each state gets a child node and only one of them ticks. Full
102
+ recipe: "An enemy that changes its mind" in `incanto-gameplay-behaviors.md`.
103
+
104
+ Asleep is not detached: props, accumulated state, signal connections and
105
+ `serialize()` all survive; only the per-frame hooks pause. The lifecycle hooks
106
+ are never gated, so a behavior authored `enabled: false` is fully initialized
107
+ and simply idle.
87
108
 
88
109
  ## Runtime API (the imperative half)
89
110
 
@@ -382,6 +382,9 @@ author markers). Emits `reachedPoint(index)` on arrival.
382
382
  | `loop` | `true` | wrap to the first point after the last (loop mode) |
383
383
  | `mode` | `"loop"` | `'loop'` wraps · `'pingpong'` reverses at the ends |
384
384
  | `pauseAt` | `0` | seconds to wait at each reached point |
385
+ | `moveParent` | `false` | move the PARENT node instead of this one |
386
+ | `facePath` | `""` | node path to TURN toward the direction of travel (e.g. `"../Skin"`) |
387
+ | `turnSpeed` | `100` | turn rate rad/s; 100 is an instant snap |
385
388
 
386
389
  Signals: `reachedPoint(index)`
387
390
 
@@ -390,6 +393,12 @@ Signals: `reachedPoint(index)`
390
393
  "script": { "name": "Patrol", "props": { "points": [[100, 0], [300, 0]], "speed": 80, "mode": "pingpong" } } }
391
394
  ```
392
395
 
396
+ `moveParent` is the same prop `Chase` has, and it exists for the same reason: a
397
+ node carries ONE behavior, so a guard that must patrol AND hold `Health` on its
398
+ body puts `Patrol` on a CHILD. Without it a patrolling body had to carry
399
+ `Patrol` itself and could hold nothing else — a patrolling enemy you could not
400
+ hurt.
401
+
393
402
  ## Chase
394
403
 
395
404
  Homing enemy AI: each frame moves toward `target.position` at `speed`, stopping
@@ -403,6 +412,8 @@ within `stopRange` (melee reach). `reachedTarget` fires once on entering range
403
412
  | `stopRange` | `0` | stop (and emit reachedTarget) within this distance |
404
413
  | `loseRange` | `0` | give up beyond this distance (0 = never lose) |
405
414
  | `moveParent` | `false` | move the PARENT node instead of this one |
415
+ | `facePath` | `""` | node path to TURN toward the direction of travel (e.g. `"../Skin"`) |
416
+ | `turnSpeed` | `100` | turn rate rad/s; 100 is an instant snap |
406
417
 
407
418
  Signals: `reachedTarget` · `lostTarget`
408
419
 
@@ -420,6 +431,25 @@ whole entity toward the target. (Distance is measured from the moved parent.)
420
431
  "script": { "name": "Chase", "props": { "target": "/root/Player", "speed": 120, "stopRange": 20 } } }
421
432
  ```
422
433
 
434
+ **`facePath` — an NPC that does not run backwards.** `CharacterController3D`
435
+ turns the PLAYER's skin toward its travel; for an NPC nothing did, so every 3D
436
+ game hand-wrote the same `yaw = atan2(dx, dz)` — against a formula
437
+ `incanto-3d-character.md` simultaneously teaches and calls "the #1 recurring
438
+ bug", because the wrong version shows you the enemy's BACK while it charges at
439
+ you. Point `facePath` at the skin and the engine writes the right one:
440
+
441
+ ```jsonc
442
+ { "name": "Hunt", "type": "Node3D",
443
+ "script": { "name": "Chase",
444
+ "props": { "target": "/root/Player", "moveParent": true, "facePath": "../Skin" } } }
445
+ ```
446
+
447
+ It is opt-in (empty default turns nothing), it is the exact math and the exact
448
+ `turnSpeed` units the controller uses, and `bunx incanto-feel` verifies it:
449
+ `facing: /Guard/Skin faces its travel (dot 1.00)`. In 2D it writes the scalar
450
+ `rotation` (`atan2(dy, dx)`) — right for top-down, so a PLATFORMER that faces
451
+ with `flipX` should leave `facePath` empty.
452
+
423
453
  **A character body chases through the level, not through walls.** When the node
424
454
  being moved is a `CharacterBody2D`/`3D`, `Chase` (and `Patrol`) move it with
425
455
  `moveAndSlide()`, so it collides and slides like any character. A plain
@@ -431,6 +461,114 @@ geometry it used to walk through. When enemies must find their way around a
431
461
  level, that is what `buildTerrainNav` + `findPath` + `PathFollow` are for (see
432
462
  `incanto-building-3d-games.md`).
433
463
 
464
+ ## An enemy that changes its mind (`enabled`)
465
+
466
+ An enemy that patrols until it spots you and then chases is the most ordinary AI
467
+ there is, and it used to be inexpressible: a node carries ONE behavior, and
468
+ nothing could stop one. `ZombieAI` exists because of that — it is `Wander` and
469
+ `Chase` FUSED into a single class, which solves exactly one pair and no others.
470
+
471
+ **Every behavior has `enabled`** (default `true`), plus `enable()` and
472
+ `disable()` methods. They are methods, so a CONNECTION can call them — which is
473
+ the whole point. Give the guard one child per state, sleep the ones that are not
474
+ running, and the state machine is scene JSON:
475
+
476
+ ```jsonc
477
+ { "name": "Guard", "type": "CharacterBody3D", "groups": ["enemy"],
478
+ "script": { "name": "Health", "props": { "max": 40 } },
479
+ "children": [
480
+ { "name": "Skin", "type": "ModelInstance3D", "props": { "model": "$guard" } },
481
+
482
+ { "name": "Walk", "type": "Node3D",
483
+ "script": { "name": "Patrol",
484
+ "props": { "points": ["%PostA", "%PostB"], "speed": 2,
485
+ "moveParent": true, "facePath": "../Skin" } } },
486
+
487
+ { "name": "Hunt", "type": "Node3D",
488
+ "script": { "name": "Chase",
489
+ "props": { "target": "/root/Player", "speed": 5, "stopRange": 1.4,
490
+ "moveParent": true, "facePath": "../Skin", "enabled": false } } }
491
+ ] }
492
+ ```
493
+ ```jsonc
494
+ "connections": [
495
+ { "signal": "spotted", "from": "Guard/Eyes", "to": "Guard/Walk", "handler": "disable" },
496
+ { "signal": "spotted", "from": "Guard/Eyes", "to": "Guard/Hunt", "handler": "enable" },
497
+ { "signal": "lostTarget", "from": "Guard/Hunt", "to": "Guard/Hunt", "handler": "disable" },
498
+ { "signal": "lostTarget", "from": "Guard/Hunt", "to": "Guard/Walk", "handler": "enable" }
499
+ ]
500
+ ```
501
+
502
+ That is a two-state guard, with health, that walks a route, turns to face where
503
+ it is going, gives up when you break away, and goes back to its patrol — in
504
+ zero TypeScript.
505
+
506
+ **Asleep is not detached.** A disabled behavior keeps its props and everything
507
+ it had accumulated, stays reachable as `node.behavior`, keeps its signal
508
+ connections, and still saves through `serialize()`. Only `update`/`fixedUpdate`
509
+ pause. The lifecycle hooks are never gated: they are structural, they fire once,
510
+ and a behavior that skipped its own `onReady` would wake up uninitialized —
511
+ so a behavior authored `enabled: false` is fully set up and simply idle.
512
+
513
+ **OFF is explicit.** The tick is gated on `enabled !== false`, so a hand-attached
514
+ object that never heard of the prop still runs. A behavior silently not running
515
+ is a far worse failure than one running when it was meant to sleep.
516
+
517
+ From TypeScript it is the same switch: `this.getNode('../Hunt').behavior.enable()`.
518
+
519
+ ## FaceTarget
520
+
521
+ Turn toward the nearest thing in a group and say when you are pointed at it —
522
+ turrets, sentry guns, security cameras, an NPC that watches you walk past.
523
+
524
+ `Chase` finds the nearest target and turns toward it, and then MOVES, which is
525
+ the one thing a turret must not do; `Patrol`/`Chase`'s `facePath` only turns as
526
+ a side effect of locomotion, so a stationary node could not use it. A tower
527
+ defense built before this hand-wrote acquisition and the aim gate.
528
+
529
+ | Prop | Default | Meaning |
530
+ |---|---|---|
531
+ | `targetGroup` | `""` | group to acquire from — nearest live member wins (required) |
532
+ | `range` | `0` | only acquire within this distance (0 = anywhere) |
533
+ | `facePath` | `""` | node to TURN (usually a skin); empty turns its own node |
534
+ | `turnSpeed` | `6` | turn rate rad/s; 100 is an instant snap |
535
+ | `aimTolerance` | `6` | degrees of slop that still counts as pointed at it |
536
+
537
+ Signals: `acquired(node)` · `lostTarget` · **`aimed`** · `lostAim`
538
+
539
+ ```jsonc
540
+ { "name": "Turret", "type": "Node3D",
541
+ "script": { "name": "FaceTarget",
542
+ "props": { "targetGroup": "enemy", "range": 12, "facePath": "../Barrel" } } }
543
+ ```
544
+
545
+ **`aimed` is the useful one.** "Only fire while actually pointed at it" becomes a
546
+ wire instead of an angle comparison every caller repeats and one caller forgets.
547
+ `onTarget` and `target` are readable from TypeScript for the rest.
548
+
549
+ ## Currency
550
+
551
+ Money you can spend. `ScoreKeeper` is a SCORE — it counts up, it has no spend,
552
+ no affordability question, and its counter already drives the win condition, so
553
+ every shop, build mode, upgrade tree and economy game wrote its own wallet.
554
+
555
+ | Prop | Default | Meaning |
556
+ |---|---|---|
557
+ | `amount` | `0` | what you start with |
558
+ | `max` | `0` | ceiling (0 = none) |
559
+
560
+ Signals: `changed(amount)` · `earned(gain)` · `spent(cost)` · **`refused(cost)`**
561
+ Methods: `spend(cost) -> boolean` · `earn(gain)` · `canAfford(cost)` · `setAmount(n)`
562
+
563
+ ```ts
564
+ if (!wallet.spend(TOWER_COST)) return; // `refused` already fired for the HUD
565
+ placeTower(pad);
566
+ ```
567
+
568
+ `spend` returns whether it went through and emits `refused` when it did not, so
569
+ "you cannot afford that" is one wire to a banner. It saves through `serialize()`
570
+ like `Health` and `ScoreKeeper`.
571
+
434
572
  ## Wander
435
573
 
436
574
  Seeded random roaming inside a circle around the spawn point — idle critters,
@@ -452,9 +590,13 @@ run (replayable, test-stable).
452
590
 
453
591
  The staple monster AI in ONE behavior: SHAMBLE around on its own, then LOCK ON and
454
592
  CHARGE `aggroTarget` (the player) once it wanders within `aggroRange` — giving up
455
- again past `deAggroRange` (hysteresis, no boundary flicker). Use this instead of
456
- `Wander`+`Chase` (a node carries ONE behavior, so they can't co-exist) whenever you
457
- want enemies that idle/roam until the player gets close, then rush in.
593
+ again past `deAggroRange` (hysteresis, no boundary flicker). Use this whenever you want enemies that idle/roam until the player gets close,
594
+ then rush in — it is one behavior, so it needs no wiring at all.
595
+
596
+ For any OTHER pair (patrol-then-chase, guard-then-flee, the state machine your
597
+ game actually wants), compose them yourself with `enabled` — see
598
+ "An enemy that changes its mind" below. This behavior is the fused
599
+ `Wander`+`Chase` from when that was the only way.
458
600
 
459
601
  | Prop | Default | Meaning |
460
602
  |---|---|---|
@@ -679,8 +821,35 @@ Signals: `waveStarted(index)` · `waveCleared(index)` · `allCleared`
679
821
  ## Projectile
680
822
 
681
823
  Straight-line, constant-velocity motion — bullets, arrows, thrown rocks. Pure
682
- MOVEMENT by design: **compose** it with `DamageOnContact` (deal damage), `Lifetime`
683
- (auto-despawn) and an Area collider on the SAME node — don't conflate them.
824
+ MOVEMENT by design: **compose** it with `DamageOnContact` (deal damage) and
825
+ `Lifetime` (auto-despawn) — don't conflate them. A node carries ONE script, so
826
+ those go on CHILD nodes and each one sets **`freeParent: true`** so it frees the
827
+ BULLET rather than itself. Copy this:
828
+
829
+ ```jsonc
830
+ { "name": "Bullet", "type": "Node3D",
831
+ "script": { "name": "Projectile", "props": { "speed": 30 } },
832
+ "children": [
833
+ { "name": "Hit", "type": "Area3D",
834
+ "props": { "collider": { "shape": "sphere", "radius": 0.2 } },
835
+ "script": { "name": "DamageOnContact",
836
+ "props": { "amount": 10, "targetGroup": "enemy",
837
+ "destroySelf": true, "freeParent": true } } },
838
+ { "name": "Life", "type": "Node3D",
839
+ "script": { "name": "Lifetime", "props": { "seconds": 2, "freeParent": true } } }
840
+ ] }
841
+ ```
842
+
843
+ **Without `freeParent` the bullet never dies.** Both behaviors used to free only
844
+ their own node, so the hitbox and the timer removed THEMSELVES and the bullet
845
+ flew on forever — one leaked node per shot, damaging exactly once, with no error.
846
+ Measured on a tower defense built from the published package:
847
+
848
+ ```
849
+ t=0.58 bullet z=2.92 | Dmg child: alive | Life child: gone | foe hp: 100
850
+ t=1.18 bullet z=5.92 | Dmg child: gone | Life child: gone | foe hp: 90
851
+ t=2.88 bullet z=14.42 | (both gone) | foe hp: 90 <- still flying
852
+ ```
684
853
  `direction` is a vector OR `'forward'` (from the node `rotation`); it's a
685
854
  union-typed prop so its default is `null` (= `'forward'`).
686
855
 
@@ -703,10 +872,9 @@ as a gun that simply never hits anything.) In 2D `rotation` is a scalar and
703
872
  { "name": "Bullet", "type": "Area2D", "props": { "rotation": 0 },
704
873
  "script": { "name": "Projectile", "props": { "speed": 600, "direction": "forward" } } }
705
874
  ```
706
- > Compose: add `DamageOnContact` and `Lifetime` to make it actually hurt and
707
- > despawn. One node carries one script, so attach `Projectile` here and wire the
708
- > others onto sibling/child Area nodes (or use a Spawner that clones a fully-built
709
- > bullet template).
875
+ > Compose: add `DamageOnContact` and `Lifetime` on CHILD nodes, both with
876
+ > `freeParent: true`, as shown above — or use a Spawner that clones a
877
+ > fully-built bullet template.
710
878
 
711
879
  ---
712
880
 
@@ -290,6 +290,7 @@ Signals: `movementStateChanged(state)`
290
290
  | `airControl` | `0.2` | number |
291
291
  | `fallGravity` | `2.5` | number |
292
292
  | `floatHeight` | `0.01` | number |
293
+ | `stepHeight` | `0.35` | number |
293
294
  | `mouseLook` | `true` | boolean |
294
295
  | `zoomMin` | `0` | number |
295
296
  | `zoomMax` | `0` | number |
@@ -446,6 +447,7 @@ Signals: `movementStateChanged(state)`
446
447
  | `length` | `0` | number |
447
448
  | `stiffness` | `50` | number |
448
449
  | `damping` | `5` | number |
450
+ | `collide` | `null` | null |
449
451
 
450
452
  ## `Joint3D` — `incanto/3d`
451
453
 
@@ -466,6 +468,7 @@ Signals: `movementStateChanged(state)`
466
468
  | `length` | `0` | number |
467
469
  | `stiffness` | `50` | number |
468
470
  | `damping` | `5` | number |
471
+ | `collide` | `null` | null |
469
472
 
470
473
  ## `Label` — `incanto/2d`
471
474
 
@@ -760,6 +763,7 @@ Signals: `triggerEnter(other)` · `triggerExit(other)`
760
763
  | `friction` | `0.5` | number |
761
764
  | `restitution` | `0` | number |
762
765
  | `linearVelocity` | `[0,0,0]` | array |
766
+ | `angularVelocity` | `[0,0,0]` | array |
763
767
 
764
768
  Signals: `triggerEnter(other)` · `triggerExit(other)`
765
769
 
@@ -1327,6 +1331,7 @@ wire their signals through scene `connections`. Full guide: `incanto-gameplay-be
1327
1331
 
1328
1332
  | Prop | Default | Kind |
1329
1333
  |---|---|---|
1334
+ | `enabled` | `true` | boolean |
1330
1335
  | `water` | `""` | node path |
1331
1336
  | `draft` | `0.35` | number |
1332
1337
  | `stiffness` | `3` | number |
@@ -1342,17 +1347,21 @@ Signals: `submerged` · `surfaced`
1342
1347
 
1343
1348
  | Prop | Default | Kind |
1344
1349
  |---|---|---|
1350
+ | `enabled` | `true` | boolean |
1345
1351
  | `falloff` | `0.28` | number |
1346
1352
 
1347
1353
  ### `Chase`
1348
1354
 
1349
1355
  | Prop | Default | Kind |
1350
1356
  |---|---|---|
1357
+ | `enabled` | `true` | boolean |
1351
1358
  | `target` | `""` | node path |
1352
1359
  | `speed` | `60` | number |
1353
1360
  | `stopRange` | `0` | number |
1354
1361
  | `loseRange` | `0` | number |
1355
1362
  | `moveParent` | `false` | boolean |
1363
+ | `facePath` | `""` | node path |
1364
+ | `turnSpeed` | `100` | number |
1356
1365
 
1357
1366
  Signals: `reachedTarget` · `lostTarget`
1358
1367
 
@@ -1360,9 +1369,9 @@ Signals: `reachedTarget` · `lostTarget`
1360
1369
 
1361
1370
  | Prop | Default | Kind |
1362
1371
  |---|---|---|
1372
+ | `enabled` | `true` | boolean |
1363
1373
  | `button` | `0` | number |
1364
1374
  | `maxDistance` | `0` | number |
1365
- | `enabled` | `true` | boolean |
1366
1375
 
1367
1376
  Signals: `clicked` · `hovered` · `unhovered`
1368
1377
 
@@ -1370,19 +1379,32 @@ Signals: `clicked` · `hovered` · `unhovered`
1370
1379
 
1371
1380
  | Prop | Default | Kind |
1372
1381
  |---|---|---|
1382
+ | `enabled` | `true` | boolean |
1373
1383
  | `group` | `"player"` | string |
1374
1384
 
1375
1385
  Signals: `totalChanged(total)`
1376
1386
 
1387
+ ### `Currency`
1388
+
1389
+ | Prop | Default | Kind |
1390
+ |---|---|---|
1391
+ | `enabled` | `true` | boolean |
1392
+ | `amount` | `0` | number |
1393
+ | `max` | `0` | number |
1394
+
1395
+ Signals: `changed` · `earned` · `spent` · `refused`
1396
+
1377
1397
  ### `DamageOnContact`
1378
1398
 
1379
1399
  | Prop | Default | Kind |
1380
1400
  |---|---|---|
1401
+ | `enabled` | `true` | boolean |
1381
1402
  | `amount` | `10` | number |
1382
1403
  | `targetGroup` | `""` | string |
1383
1404
  | `oncePerTarget` | `true` | boolean |
1384
1405
  | `repeatEvery` | `0` | number |
1385
1406
  | `destroySelf` | `false` | boolean |
1407
+ | `freeParent` | `false` | boolean |
1386
1408
 
1387
1409
  Signals: `dealtDamage(amount, target)`
1388
1410
 
@@ -1390,6 +1412,7 @@ Signals: `dealtDamage(amount, target)`
1390
1412
 
1391
1413
  | Prop | Default | Kind |
1392
1414
  |---|---|---|
1415
+ | `enabled` | `true` | boolean |
1393
1416
  | `daySeconds` | `240` | number |
1394
1417
  | `startHour` | `10` | number |
1395
1418
  | `maxSunElevationDeg` | `55` | number |
@@ -1398,10 +1421,24 @@ Signals: `dealtDamage(amount, target)`
1398
1421
 
1399
1422
  Signals: `dayPhaseChanged`
1400
1423
 
1424
+ ### `FaceTarget`
1425
+
1426
+ | Prop | Default | Kind |
1427
+ |---|---|---|
1428
+ | `enabled` | `true` | boolean |
1429
+ | `targetGroup` | `""` | string |
1430
+ | `range` | `0` | number |
1431
+ | `facePath` | `""` | node path |
1432
+ | `turnSpeed` | `6` | number |
1433
+ | `aimTolerance` | `6` | number |
1434
+
1435
+ Signals: `acquired` · `lostTarget` · `aimed` · `lostAim`
1436
+
1401
1437
  ### `FloatAway`
1402
1438
 
1403
1439
  | Prop | Default | Kind |
1404
1440
  |---|---|---|
1441
+ | `enabled` | `true` | boolean |
1405
1442
  | `rise` | `1` | number |
1406
1443
  | `seconds` | `0.7` | number |
1407
1444
  | `hold` | `0.3` | number |
@@ -1412,6 +1449,7 @@ Signals: `dayPhaseChanged`
1412
1449
 
1413
1450
  | Prop | Default | Kind |
1414
1451
  |---|---|---|
1452
+ | `enabled` | `true` | boolean |
1415
1453
  | `target` | `""` | node path |
1416
1454
  | `offset` | `[]` | array |
1417
1455
  | `smoothing` | `0` | number |
@@ -1421,6 +1459,7 @@ Signals: `dayPhaseChanged`
1421
1459
 
1422
1460
  | Prop | Default | Kind |
1423
1461
  |---|---|---|
1462
+ | `enabled` | `true` | boolean |
1424
1463
  | `restartAction` | `"restart"` | string |
1425
1464
  | `pauseAction` | `"pause"` | string |
1426
1465
  | `freezeOnEnd` | `true` | boolean |
@@ -1433,6 +1472,7 @@ Signals: `flowChanged`
1433
1472
 
1434
1473
  | Prop | Default | Kind |
1435
1474
  |---|---|---|
1475
+ | `enabled` | `true` | boolean |
1436
1476
  | `max` | `100` | number |
1437
1477
  | `regenPerSec` | `0` | number |
1438
1478
  | `invulnerableFor` | `0` | number |
@@ -1444,6 +1484,7 @@ Signals: `damaged(amount, current)` · `healed(amount, current)` · `died` · `r
1444
1484
 
1445
1485
  | Prop | Default | Kind |
1446
1486
  |---|---|---|
1487
+ | `enabled` | `true` | boolean |
1447
1488
  | `action` | `"interact"` | string |
1448
1489
  | `range` | `2` | number |
1449
1490
  | `actorGroup` | `"player"` | string |
@@ -1454,8 +1495,10 @@ Signals: `interacted(actor)`
1454
1495
 
1455
1496
  | Prop | Default | Kind |
1456
1497
  |---|---|---|
1498
+ | `enabled` | `true` | boolean |
1457
1499
  | `seconds` | `1` | number |
1458
1500
  | `startOnSignal` | `false` | boolean |
1501
+ | `freeParent` | `false` | boolean |
1459
1502
 
1460
1503
  Signals: `expired`
1461
1504
 
@@ -1463,6 +1506,7 @@ Signals: `expired`
1463
1506
 
1464
1507
  | Prop | Default | Kind |
1465
1508
  |---|---|---|
1509
+ | `enabled` | `true` | boolean |
1466
1510
  | `to` | `[]` | array |
1467
1511
  | `duration` | `1` | number |
1468
1512
  | `ease` | `"easeInOut"` | one of: `linear` `easeIn` `easeOut` `easeInOut` |
@@ -1474,6 +1518,7 @@ Signals: `arrived`
1474
1518
 
1475
1519
  | Prop | Default | Kind |
1476
1520
  |---|---|---|
1521
+ | `enabled` | `true` | boolean |
1477
1522
  | `axis` | `"y"` | one of: `x` `y` `z` |
1478
1523
  | `amplitude` | `1` | number |
1479
1524
  | `frequency` | `1` | number |
@@ -1483,6 +1528,7 @@ Signals: `arrived`
1483
1528
 
1484
1529
  | Prop | Default | Kind |
1485
1530
  |---|---|---|
1531
+ | `enabled` | `true` | boolean |
1486
1532
  | `speed` | `120` | number |
1487
1533
  | `loop` | `false` | boolean |
1488
1534
 
@@ -1492,11 +1538,15 @@ Signals: `waypointReached` · `arrived`
1492
1538
 
1493
1539
  | Prop | Default | Kind |
1494
1540
  |---|---|---|
1541
+ | `enabled` | `true` | boolean |
1495
1542
  | `points` | `[]` | array |
1496
1543
  | `speed` | `60` | number |
1497
1544
  | `loop` | `true` | boolean |
1498
1545
  | `mode` | `"loop"` | one of: `loop` `pingpong` |
1499
1546
  | `pauseAt` | `0` | number |
1547
+ | `moveParent` | `false` | boolean |
1548
+ | `facePath` | `""` | node path |
1549
+ | `turnSpeed` | `100` | number |
1500
1550
 
1501
1551
  Signals: `reachedPoint(index)`
1502
1552
 
@@ -1504,6 +1554,7 @@ Signals: `reachedPoint(index)`
1504
1554
 
1505
1555
  | Prop | Default | Kind |
1506
1556
  |---|---|---|
1557
+ | `enabled` | `true` | boolean |
1507
1558
  | `value` | `1` | number |
1508
1559
  | `kind` | `"coin"` | string |
1509
1560
  | `collectorGroup` | `"player"` | string |
@@ -1514,6 +1565,7 @@ Signals: `collected(value, other)`
1514
1565
 
1515
1566
  | Prop | Default | Kind |
1516
1567
  |---|---|---|
1568
+ | `enabled` | `true` | boolean |
1517
1569
  | `speed` | `300` | number |
1518
1570
  | `direction` | `null` | null |
1519
1571
  | `gravity` | `0` | number |
@@ -1522,6 +1574,7 @@ Signals: `collected(value, other)`
1522
1574
 
1523
1575
  | Prop | Default | Kind |
1524
1576
  |---|---|---|
1577
+ | `enabled` | `true` | boolean |
1525
1578
  | `game` | `"game"` | string |
1526
1579
  | `slot` | `"1"` | string |
1527
1580
  | `label` | `""` | string |
@@ -1535,6 +1588,7 @@ Signals: `saved` · `restored` · `noSave` · `hasSave`
1535
1588
 
1536
1589
  | Prop | Default | Kind |
1537
1590
  |---|---|---|
1591
+ | `enabled` | `true` | boolean |
1538
1592
  | `score` | `0` | number |
1539
1593
  | `lives` | `0` | number |
1540
1594
  | `scoreToWin` | `0` | number |
@@ -1545,6 +1599,7 @@ Signals: `scoreChanged(score)` · `won` · `lost` · `lifeLost(lives)`
1545
1599
 
1546
1600
  | Prop | Default | Kind |
1547
1601
  |---|---|---|
1602
+ | `enabled` | `true` | boolean |
1548
1603
  | `prefab` | `""` | node path |
1549
1604
  | `interval` | `1` | number |
1550
1605
  | `max` | `0` | number |
@@ -1558,6 +1613,7 @@ Signals: `spawned(node)` · `finished`
1558
1613
 
1559
1614
  | Prop | Default | Kind |
1560
1615
  |---|---|---|
1616
+ | `enabled` | `true` | boolean |
1561
1617
  | `speed` | `40` | number |
1562
1618
  | `radius` | `50` | number |
1563
1619
  | `changeEvery` | `2` | number |
@@ -1566,6 +1622,7 @@ Signals: `spawned(node)` · `finished`
1566
1622
 
1567
1623
  | Prop | Default | Kind |
1568
1624
  |---|---|---|
1625
+ | `enabled` | `true` | boolean |
1569
1626
  | `waves` | `[]` | array |
1570
1627
  | `autoStart` | `true` | boolean |
1571
1628
 
@@ -1575,6 +1632,7 @@ Signals: `waveStarted(index)` · `waveCleared(index)` · `allCleared`
1575
1632
 
1576
1633
  | Prop | Default | Kind |
1577
1634
  |---|---|---|
1635
+ | `enabled` | `true` | boolean |
1578
1636
  | `aggroTarget` | `""` | node path |
1579
1637
  | `aggroRange` | `12` | number |
1580
1638
  | `deAggroRange` | `0` | number |