incanto 0.14.0 → 0.16.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 (29) hide show
  1. package/dist/3d.d.ts +34 -0
  2. package/dist/3d.js +3 -3
  3. package/dist/{create-game-OSntcIiM.js → create-game-B5GWzy1E.js} +2 -109
  4. package/dist/index.js +1 -1
  5. package/dist/{physics-3d-D_kUdOjr.js → physics-3d-CDlvC9Z_.js} +1 -1
  6. package/dist/react.js +1 -1
  7. package/dist/{register-C9-16x_t.js → register-Lp2qn1Wq.js} +435 -37
  8. package/dist/test.js +3 -3
  9. package/editor/assets/{agent8-BDC4LhjC.js → agent8-Cw7igxDG.js} +1 -1
  10. package/editor/assets/{index-7qOYg7t8.js → index-N3Hnrv0w.js} +217 -118
  11. package/editor/index.html +1 -1
  12. package/package.json +1 -1
  13. package/schemas/scene.schema.json +25 -0
  14. package/skills/incanto-environment.md +14 -2
  15. package/skills/incanto-node-reference.md +6 -0
  16. package/templates-app/beacon-isle-3d/PROJECT/Status.md +1 -1
  17. package/templates-app/beacon-isle-3d/generate-world.ts +23 -18
  18. package/templates-app/beacon-isle-3d/package.json +1 -1
  19. package/templates-app/beacon-isle-3d/src/behaviors.ts +70 -4
  20. package/templates-app/beacon-isle-3d/src/game.scene.json +20 -21
  21. package/templates-app/tps-3d/package.json +1 -1
  22. package/templates-app/village-quest-3d/PROJECT/Context.md +1 -1
  23. package/templates-app/village-quest-3d/PROJECT/Requirements.md +1 -1
  24. package/templates-app/village-quest-3d/PROJECT/Structure.md +6 -1
  25. package/templates-app/village-quest-3d/generate-dressing.ts +769 -0
  26. package/templates-app/village-quest-3d/package.json +1 -1
  27. package/templates-app/village-quest-3d/src/behaviors.ts +74 -8
  28. package/templates-app/village-quest-3d/src/grove.scene.json +1 -13
  29. package/templates-app/village-quest-3d/src/village.scene.json +2989 -162
@@ -13,7 +13,7 @@
13
13
  "@dimforge/rapier2d-compat": "0.19.3",
14
14
  "@dimforge/rapier3d-compat": "0.19.3",
15
15
  "@pixiv/three-vrm": "^3.5.3",
16
- "incanto": "^0.14.0",
16
+ "incanto": "^0.16.0",
17
17
  "three": "^0.184.0"
18
18
  },
19
19
  "devDependencies": {
@@ -25,7 +25,7 @@ import type {
25
25
  } from 'incanto';
26
26
  import { Behavior, findPath, gridFromRows } from 'incanto';
27
27
  import { mergeSolidRects, parseCells } from 'incanto/2d';
28
- import type { CharacterController3D, Node3D, Trail3D } from 'incanto/3d';
28
+ import type { Camera3D, CharacterController3D, Node3D, Trail3D } from 'incanto/3d';
29
29
  import { MeshInstance3D, Particles3D, StaticBody3D } from 'incanto/3d';
30
30
  import { goToScene } from 'incanto/gameplay';
31
31
  import groveJsonRaw from './grove.scene.json';
@@ -79,8 +79,53 @@ function windFor(state: string): { pitch: number; volume: number } {
79
79
  }
80
80
  }
81
81
 
82
- /** Wind ambience + sprint dash trail — shared per-frame polish. */
83
- function ambientUpdate(engine: Engine | null | undefined, root: Node, wind: Voice | null): void {
82
+ /** Sprint feel — see sprintFeel(): FOV kick + per-stride dust. */
83
+ const FOV_BASE = 60;
84
+ const FOV_SPRINT = 66.5;
85
+ const STRIDE_SECONDS = 0.22;
86
+
87
+ /**
88
+ * One kicked-up dirt puff, world-anchored at the foot spot (one-shot,
89
+ * self-removing). Non-additive expanding dust — it reads as path dirt
90
+ * leaving the ground, then thins out.
91
+ */
92
+ function spawnFootDust(root: Node, at: [number, number, number], scale = 1): void {
93
+ const puff = new Particles3D('FootDust');
94
+ puff.position = [at[0], at[1], at[2]];
95
+ puff.rate = 0;
96
+ puff.burst = Math.round(4 * scale);
97
+ puff.blend = 'normal';
98
+ puff.lifetime = [0.3, 0.5];
99
+ puff.speed = [8, 34];
100
+ puff.directionDeg = -90; // up out of the ground…
101
+ puff.spreadDeg = 75;
102
+ puff.gravity = [0, 30]; // …then settle back down
103
+ puff.drag = 2.5;
104
+ puff.sizeStart = 11 * scale;
105
+ puff.sizeEnd = 30 * scale; // dust EXPANDS as it dissipates
106
+ puff.colorStart = '#d3bd97'; // the packed path dirt, sunlit
107
+ puff.colorEnd = '#a08c6e';
108
+ puff.alphaStart = 0.24;
109
+ puff.alphaEnd = 0;
110
+ puff.maxParticles = 24;
111
+ puff.on('finished', () => puff.free());
112
+ root.addChild(puff);
113
+ }
114
+
115
+ /** Module-scope sprint-feel state (both directors share ambientUpdate). */
116
+ const sprint = { strideClock: 0, wasAirborne: false, fov: FOV_BASE };
117
+
118
+ /**
119
+ * Wind ambience + sprint FEEL — speed you can see instead of a ribbon glued
120
+ * to the hips: a soft camera FOV kick at full sprint, kicked-up dirt at the
121
+ * feet each stride, and a bigger thump-puff on landing.
122
+ */
123
+ function ambientUpdate(
124
+ engine: Engine | null | undefined,
125
+ root: Node,
126
+ wind: Voice | null,
127
+ dt: number,
128
+ ): void {
84
129
  const controller = root.getNode('Player/Controller') as CharacterController3D | null;
85
130
  if (!controller) return;
86
131
  const state = controller.state;
@@ -88,8 +133,29 @@ function ambientUpdate(engine: Engine | null | undefined, root: Node, wind: Voic
88
133
  const w = windFor(state);
89
134
  wind.set({ pitch: w.pitch, volume: w.volume });
90
135
  }
91
- const dash = root.getNode('Player/DashTrail') as Trail3D | null;
92
- if (dash) dash.emitting = state === 'fastRun';
136
+ const camera = root.getNodeOrNull('Camera') as Camera3D | null;
137
+ if (camera) {
138
+ sprint.fov +=
139
+ (((state === 'fastRun' ? FOV_SPRINT : FOV_BASE) as number) - sprint.fov) *
140
+ Math.min(1, dt * 6);
141
+ camera.fov = sprint.fov;
142
+ }
143
+ const player = root.getNodeOrNull('Player') as (Node3D & Positioned) | null;
144
+ if (!player) return;
145
+ const [px, py, pz] = player.position;
146
+ const feet: [number, number, number] = [px ?? 0, (py ?? 0) - 0.62, pz ?? 0];
147
+ const airborne = state === 'airborne';
148
+ if (sprint.wasAirborne && !airborne) spawnFootDust(root, feet, 1.7); // landing thump
149
+ sprint.wasAirborne = airborne;
150
+ if (state === 'fastRun') {
151
+ sprint.strideClock -= dt;
152
+ if (sprint.strideClock <= 0) {
153
+ sprint.strideClock = STRIDE_SECONDS;
154
+ spawnFootDust(root, feet);
155
+ }
156
+ } else {
157
+ sprint.strideClock = 0;
158
+ }
93
159
  }
94
160
 
95
161
  // ---- VillageDirector --------------------------------------------------------------
@@ -123,9 +189,9 @@ export class VillageDirector extends Behavior {
123
189
  this.wind?.stop();
124
190
  }
125
191
 
126
- override update(): void {
192
+ override update(dt: number): void {
127
193
  const engine = this.node.tree?.engine;
128
- ambientUpdate(engine, this.node, this.wind);
194
+ ambientUpdate(engine, this.node, this.wind, dt);
129
195
  // E advances an open dialogue (same key as "talk" — no extra binding)
130
196
  if (engine && this.dialogue.active && engine.input.justPressed('interact')) {
131
197
  this.dialogue.advance();
@@ -275,7 +341,7 @@ export class GroveDirector extends Behavior {
275
341
 
276
342
  override update(dt: number): void {
277
343
  const engine = this.node.tree?.engine;
278
- ambientUpdate(engine, this.node, this.wind);
344
+ ambientUpdate(engine, this.node, this.wind, dt);
279
345
  this.faceWolvesForward();
280
346
  this.huntOrPatrol(dt);
281
347
  this.syncHpBar();
@@ -705,18 +705,6 @@
705
705
  }
706
706
  ]
707
707
  },
708
- {
709
- "name": "DashTrail",
710
- "type": "Trail3D",
711
- "props": {
712
- "width": 0.5,
713
- "color": "#ffffff",
714
- "opacity": 0.3,
715
- "seconds": 0.45,
716
- "emitting": false,
717
- "position": [0, -0.4, 0]
718
- }
719
- },
720
708
  {
721
709
  "name": "SwingSfx",
722
710
  "type": "AudioPlayer",
@@ -798,7 +786,7 @@
798
786
  "type": "UiText",
799
787
  "uid": "n_gq_hint",
800
788
  "props": {
801
- "text": "click/F strike \u00b7 Shift sprint \u00b7 portal returns home",
789
+ "text": "click/F strike · Shift sprint · portal returns home",
802
790
  "size": 12,
803
791
  "color": "#cccccc",
804
792
  "anchor": "bottomRight",