incanto 0.30.0 → 0.32.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 (49) hide show
  1. package/bin/incanto-play.mjs +7 -2
  2. package/dist/2d.d.ts +12 -5
  3. package/dist/2d.js +3 -3
  4. package/dist/3d.d.ts +67 -6
  5. package/dist/3d.js +4 -4
  6. package/dist/{audit-C6rMyict.js → audit-C4kmDK0o.js} +16 -0
  7. package/dist/{behavior-BAc0erXF.d.ts → behavior-D_jMpFh8.d.ts} +117 -55
  8. package/dist/{create-game-5z_QVtLx.js → create-game-bKHgHcsZ.js} +19 -10
  9. package/dist/{create-game-DuBTv2zI.js → create-game-gXI7PYl0.js} +61 -46
  10. package/dist/debug.d.ts +1 -1
  11. package/dist/debug.js +7 -2
  12. package/dist/{duplicate-BgtFrFo4.js → duplicate-KRPtUtzl.js} +1 -1
  13. package/dist/{editor-switch-B0wB_DSr.d.ts → editor-switch-BJb-CWfA.d.ts} +12 -1
  14. package/dist/{register-DJ0SByQg.js → environment-presets--DigHNg4.js} +42 -11
  15. package/dist/{gameplay-Cfr6aFZ1.js → gameplay-BftxM_It.js} +2 -2
  16. package/dist/gameplay.d.ts +1 -1
  17. package/dist/gameplay.js +1 -1
  18. package/dist/index.d.ts +13 -3
  19. package/dist/index.js +7 -7
  20. package/dist/{loader-B242FF6N.d.ts → loader-BYBrqTxP.d.ts} +1 -1
  21. package/dist/{loader-BZqOKfI2.js → loader-BlaRQGaA.js} +482 -6
  22. package/dist/net.d.ts +1 -1
  23. package/dist/net.js +3 -3
  24. package/dist/{pathfinding-BwD974Ss.d.ts → pathfinding-BwhqPD3i.d.ts} +1 -1
  25. package/dist/{physics-2d-3kOQCtgd.js → physics-2d-D9wquBvK.js} +4 -3
  26. package/dist/{physics-3d-CeRH-Ff_.js → physics-3d-CnPygVGo.js} +5 -4
  27. package/dist/react.d.ts +1 -1
  28. package/dist/react.js +1 -1
  29. package/dist/{register-MelqEdza.js → register-CUY284Is.js} +3 -3
  30. package/dist/{register-BTg0EM7s.js → register-Dzkd6-os.js} +56 -380
  31. package/dist/{register-DWcWq4QG.js → register-tkR_8tWg.js} +4 -4
  32. package/dist/{registry-BVJ2HbCn.js → registry-CJdGpT2V.js} +6 -2
  33. package/dist/test-WwRIlXsK.js +642 -0
  34. package/dist/test.d.ts +55 -8
  35. package/dist/test.js +3 -364
  36. package/dist/{touch-031PxtCR.js → touch-BoNg_MnF.js} +2 -1
  37. package/dist/vite.js +1 -1
  38. package/editor/assets/{agent8-CAp0i5qn.js → agent8-C5k1nTCH.js} +1 -1
  39. package/editor/assets/{debug-BoEYfbqK.js → debug-BT_0mjk-.js} +3 -2
  40. package/editor/assets/{index-BO6WU8by.js → index-gfyrByWw.js} +90 -90
  41. package/editor/index.html +1 -1
  42. package/package.json +1 -1
  43. package/skills/README.md +14 -2
  44. package/skills/incanto-building-2d-games.md +16 -0
  45. package/skills/incanto-building-3d-games.md +16 -0
  46. package/skills/incanto-verifying-your-game.md +118 -0
  47. package/templates-app/beacon-isle-3d/package.json +1 -1
  48. package/templates-app/tps-3d/package.json +1 -1
  49. package/templates-app/village-quest-3d/package.json +1 -1
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-BO6WU8by.js"></script>
8
+ <script type="module" crossorigin src="./assets/index-gfyrByWw.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.30.0",
3
+ "version": "0.32.0",
4
4
  "description": "Vibe-coding-first web game engine SDK — JSON-driven scenes on three.js",
5
5
  "keywords": [
6
6
  "game-engine",
package/skills/README.md CHANGED
@@ -2,8 +2,20 @@
2
2
 
3
3
  Full agent skills for building games with this **installed version** of Incanto.
4
4
  An AI agent should start with `incanto-scene-json-authoring.md`, then the domain skill
5
- for the task at hand. `incanto-node-reference.md` (+ `../schemas/scene.schema.json`)
6
- is generated from the engine source and lists every node type, prop, and default.
5
+ for the task at hand, and **finish every task with `incanto-verifying-your-game.md`**
6
+ you cannot see the game, and that skill is how you find out whether it works.
7
+ `incanto-node-reference.md` (+ `../schemas/scene.schema.json`) is generated from the
8
+ engine source and lists every node type, prop, and default.
9
+
10
+ **Never hand a game back without reading these four.** Each answers a failure that
11
+ otherwise looks like nothing at all — see `incanto-verifying-your-game.md`:
12
+
13
+ | read | it is not clean when |
14
+ | --- | --- |
15
+ | `bunx incanto-check` | the scene will not load, or renders black (no light) |
16
+ | `stats().errors` | something threw and was skipped to keep the game alive |
17
+ | `assetErrors()` | a model or texture 404'd — the thing is simply not there |
18
+ | `framing` | the camera points the wrong way, or nothing is lit |
7
19
 
8
20
  These files are the source of truth; the thin platform-delivery entries live in the
9
21
  repo's root `skills/` folder and merely point here.
@@ -277,3 +277,19 @@ engine seed — runScript verifies particle gameplay reproducibly.
277
277
  Mark unchanging subtrees (tile decor, backgrounds) `static: true` — the
278
278
  renderer syncs them once and skips them every frame. Flip to `false` to
279
279
  resume live syncing.
280
+
281
+ ## Before you hand it back
282
+
283
+ You cannot see this game. `incanto-verifying-your-game.md` is how you find out
284
+ whether it works — run it after every edit, not once at the end:
285
+
286
+ ```bash
287
+ bunx incanto-check # will it load? is anything lit?
288
+ printf 'step 500\nframing\nquit\n' | bunx incanto-play src/game.scene.json
289
+ ```
290
+
291
+ `framing` tells you what the camera can actually SEE (on screen / off screen /
292
+ behind it), what lights the scene, and which colliders intersect. Then read
293
+ `stats().errors` (something threw and got skipped) and `assetErrors()` (a model
294
+ 404'd). All four are silent failures otherwise — the screen just looks wrong,
295
+ or empty, and nothing throws.
@@ -562,3 +562,19 @@ if (cells) follow.setPath(cells.map(([cx, cy]) => nav.toWorld(cx, cy)));
562
562
  dense-forest frame). Use when shadow CASTERS don't move — pair moving
563
563
  characters with sprite/blob shadows. After a one-off world edit call
564
564
  `renderer.refreshShadows()`.
565
+
566
+ ## Before you hand it back
567
+
568
+ You cannot see this game. `incanto-verifying-your-game.md` is how you find out
569
+ whether it works — run it after every edit, not once at the end:
570
+
571
+ ```bash
572
+ bunx incanto-check # will it load? is anything lit?
573
+ printf 'step 500\nframing\nquit\n' | bunx incanto-play src/game.scene.json
574
+ ```
575
+
576
+ `framing` tells you what the camera can actually SEE (on screen / off screen /
577
+ behind it), what lights the scene, and which colliders intersect. Then read
578
+ `stats().errors` (something threw and got skipped) and `assetErrors()` (a model
579
+ 404'd). All four are silent failures otherwise — the screen just looks wrong,
580
+ or empty, and nothing throws.
@@ -12,6 +12,20 @@ You usually cannot SEE the game (no browser in most agent environments). The
12
12
  scene state itself is your screenshot. The loop is: **edit → check → scripted
13
13
  run → read the capture → fix**. Never hand a game back without steps 2–3.
14
14
 
15
+ **The four things to read before you call it done.** Three of them are new, and
16
+ each answers a failure that used to look like nothing at all:
17
+
18
+ | read this | when it is not clean | see |
19
+ | --- | --- | --- |
20
+ | `incanto-check` | the scene will not load, or renders black | §1, §5b |
21
+ | `stats().errors` | something threw and was skipped to keep the game alive | §6b |
22
+ | `assetErrors()` | a model/texture 404'd — the thing is simply not there | §6c |
23
+ | `framing` | the camera is pointed the wrong way, or nothing is lit | §6a |
24
+
25
+ A run that finishes is not a run that worked. `runScript()` fails the run when
26
+ `stats().errors` is non-zero, and prints the engine's warnings under the
27
+ failures — but `framing` and `assetErrors()` you have to ASK for.
28
+
15
29
  ## 1. After EVERY scene edit: `incanto-check`
16
30
 
17
31
  ```bash
@@ -144,6 +158,25 @@ const res = validateScene(sceneJson); // { ok } | { ok: false, error }
144
158
  if (!res.ok) console.error(res.error.code, res.error.details); // details.path/prop/signal/validOptions
145
159
  ```
146
160
 
161
+ ## 5b. What a green `incanto-check` now covers
162
+
163
+ `validateScene` / `incanto-check` hard-fail on all of these, so they never reach
164
+ a browser:
165
+
166
+ - **Every prop's type AND its enum values** — `"mesh": "crystal"` names what is
167
+ valid instead of printing `ok` and throwing at boot. All 31 enum props.
168
+ - **`input{}`** — a declaration's shape (`"keys": "Space"` instead of
169
+ `["Space"]`, an unknown `type`, a wrong `touch`). This used to be checked only
170
+ by `Engine.setScene`, i.e. never headlessly.
171
+ - **`assets{}`** — every entry an object with a non-empty `url`.
172
+ - **`environment.preset`** — resolved only inside Renderer3D before, so a typo
173
+ survived every check and died at boot.
174
+
175
+ And `auditScene` (the warnings `incanto-check` prints) gained the loudest
176
+ "why is my screen black" of all: **nothing lights this 3D scene** — no light
177
+ node, no `environment` sky/hdri/preset, no ambient intensity. Ambient defaults to
178
+ 0, so such a scene renders nothing, throws nothing and logs nothing.
179
+
147
180
  ## 6. Catch broken edits at save time (vite)
148
181
 
149
182
  ```ts
@@ -161,6 +194,91 @@ ANY scene in the project — not just the one the game booted with. `incantoScen
161
194
  root })` bounds that to a directory other than vite's; nothing outside it is readable
162
195
  or writable, and none of it exists in a build (`configureServer`).
163
196
 
197
+ ## 6a. See what the camera sees — without a GPU
198
+
199
+ `describe()` tells you a gem is at `[4, 1, -2]`. It cannot tell you whether you
200
+ can SEE it, which is the question you actually had. `framing()` answers that,
201
+ from geometry alone — no renderer, no canvas, no browser:
202
+
203
+ ```
204
+ > framing
205
+ camera /World/Player/Cam at [0, 2, 8] looking [0, -0.2, -0.97] fov 60
206
+ lit by: /World/Sun (DirectionalLight3D), environment.sky
207
+ 3 on screen, 1 off screen, 2 behind the camera
208
+ onScreen /World/Island (MeshInstance3D) [0, 0, 0] screen [0.02, -0.11] 8.2m
209
+ behind /World/Chest (ModelInstance3D) [0, 0, 40] 32m
210
+ offscreen /World/Gem (MeshInstance3D) [80, 0, 0] screen [4.4, 0] 80.1m
211
+ overlap /World/Platform ∩ /World/Gem
212
+ ```
213
+
214
+ Read it for the three visual failures that look identical from a node list:
215
+
216
+ - **`behind`** — the thing is fine, the camera has its back to it. Indistinguishable
217
+ from "my model did not load" until you can see this line.
218
+ - **`lit by: NOTHING`** — the scene renders black. No error, no log, nothing wrong
219
+ with any node.
220
+ - **`overlap`** — two declared colliders intersect: the gem is inside the platform.
221
+
222
+ Also `incanto-play`'s `framing` command, and `describeFraming(scene)` /
223
+ `framingText(report)` from `incanto/test` for the structured form. A node counts
224
+ as on screen when its `size` box (or collider) is, not only its origin — so a
225
+ 200 m island whose origin sits off to the left is correctly `onScreen`.
226
+
227
+ **For actual pixels**, turn on `preserveDrawingBuffer` — WebGL clears the buffer
228
+ the instant a frame composites, so `canvas.toDataURL()` and every screenshot
229
+ built on it come back BLANK without it:
230
+
231
+ ```ts
232
+ createGame3D({ ..., preserveDrawingBuffer: true });
233
+ // or in the scene: "environment": { "rendering": { "preserveDrawingBuffer": true } }
234
+ ```
235
+
236
+ Then drive the page with whatever browser tool you have. It costs a buffer copy
237
+ per frame, which is why it is off by default.
238
+
239
+ ## 6b. A thrown error no longer stops the game
240
+
241
+ One exception used to end everything: `requestAnimationFrame` reschedules AFTER
242
+ your callback returns, so a throw in any behavior's `update` killed the loop
243
+ permanently — no message, `stats().running` still `true`, a game that just
244
+ stopped. It no longer can.
245
+
246
+ - The thing that threw is **reported once** (which node, which script, which
247
+ phase) to `engine.log` AND the console, then **skipped**. Its children and
248
+ every other node keep running.
249
+ - **Your SCRIPT and the engine's NODE are skipped separately.** A typo in a
250
+ behavior attached to a `CharacterController3D` stops that behavior — the
251
+ character still walks and the camera still turns. Only a throw from the node's
252
+ own update quarantines the node.
253
+ - `engine.stats().errors` counts everything swallowed to stay alive. **A game
254
+ that looks fine with `errors: 3` is a broken game** — read `engine.log`.
255
+ - `engine.erroredNodes()` lists them; `engine.resumeErroredNodes()` puts them
256
+ back after you fix the code.
257
+ - `runScript()` FAILS the run when the count is non-zero. "It finished" must not
258
+ mean "nothing broke" — that is right for a player and wrong for a harness.
259
+
260
+ So the check after every edit is still `ok`, plus: **read `stats().errors`**.
261
+
262
+ ## 6c. Engine diagnostics you can actually read
263
+
264
+ Every internal warning the engine produces — a GLB that 404'd, a body with no
265
+ collider, a retarget that dropped bones, an exception swallowed inside a physics
266
+ trigger handler — used to go to `console.*` and nowhere else, which is the one
267
+ channel a headless tool cannot read. The symptom was always the same: the model
268
+ simply never appears, and `logs` comes back empty.
269
+
270
+ They now go to **`engine.log` as well as the console**, so:
271
+
272
+ - `runScript().logs` contains them, and `describe()` prints them under the
273
+ failures (`! warn: …`).
274
+ - The debug overlay's **Logs** panel shows them live.
275
+ - `game.assetErrors()` lists every asset that failed **with its reason and ref**
276
+ — the direct answer to "why is my model not there". The store itself is
277
+ reachable as `renderer.assets` in both dimensions.
278
+
279
+ So the after-every-edit check is: `ok`, then **`stats().errors`**, then
280
+ **`assetErrors()`**.
281
+
164
282
  ## 7. See inside the RUNNING game (debug overlay)
165
283
 
166
284
  ```ts
@@ -14,7 +14,7 @@
14
14
  "@dimforge/rapier2d-compat": "0.19.3",
15
15
  "@dimforge/rapier3d-compat": "0.19.3",
16
16
  "@pixiv/three-vrm": "^3.5.3",
17
- "incanto": "^0.30.0",
17
+ "incanto": "^0.32.0",
18
18
  "three": "^0.184.0"
19
19
  },
20
20
  "devDependencies": {
@@ -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.30.0",
16
+ "incanto": "^0.32.0",
17
17
  "three": "^0.184.0"
18
18
  },
19
19
  "devDependencies": {
@@ -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.30.0",
16
+ "incanto": "^0.32.0",
17
17
  "three": "^0.184.0"
18
18
  },
19
19
  "devDependencies": {