incanto 0.71.0 → 0.73.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.
- package/bin/incanto-check.mjs +114 -42
- package/bin/incanto-editor.mjs +15 -9
- package/bin/incanto-feel.mjs +11 -1
- package/bin/incanto-play.mjs +11 -1
- package/bin/incanto-playtest.mjs +11 -1
- package/bin/incanto-verify.mjs +9 -2
- package/dist/2d.d.ts +29 -4
- package/dist/2d.js +3 -3
- package/dist/3d.d.ts +137 -15
- package/dist/3d.js +6 -6
- package/dist/{agent8-Cfmd3ar_.js → agent8-CHTT4unP.js} +1 -1
- package/dist/{audio-player-L8yccdMP.d.ts → audio-player-BOrk4eQx.d.ts} +1 -1
- package/dist/{behavior-Dcz0fr1S.d.ts → behavior-J1cmVah0.d.ts} +9 -1
- package/dist/{create-game-LCHBLfA4.js → create-game-CAu-ucHg.js} +8 -7
- package/dist/{create-game-CH4kt78v.js → create-game-CbTLFjis.js} +37 -13
- package/dist/debug.d.ts +1 -1
- package/dist/debug.js +1 -1
- package/dist/editor.js +1036 -930
- package/dist/{environment-presets-99BzP_L-.js → environment-presets-BsFJM3v0.js} +277 -60
- package/dist/{gameplay-CNULJvwh.js → gameplay-CRte0OVf.js} +589 -35
- package/dist/gameplay.d.ts +136 -4
- package/dist/gameplay.js +2 -2
- package/dist/index.d.ts +6 -4
- package/dist/index.js +6 -6
- package/dist/{loader-8-IHvbKD.d.ts → loader-BAeWMYfF.d.ts} +10 -3
- package/dist/net.d.ts +2 -2
- package/dist/net.js +2 -2
- package/dist/{physics-2d-FhrXUc6B.js → physics-2d-CovokK7s.js} +94 -11
- package/dist/{physics-3d-CFEGxBXZ.js → physics-3d-BiK6Emwn.js} +202 -15
- package/dist/{picking-DVo7fI13.js → picking-Dtd8JInh.js} +2 -2
- package/dist/react.d.ts +1 -1
- package/dist/react.js +1 -1
- package/dist/{register-BYQCBySi.js → register-CY9QCCZs.js} +45 -9
- package/dist/{register-DuVdzxbq.js → register-CjociOtt.js} +19 -4
- package/dist/{replay-DEvp3kyV.js → replay-B6BbgGM_.js} +55 -12
- package/dist/{replay-F7IZHdFR.d.ts → replay-wZK5Ok5c.d.ts} +2 -2
- package/dist/{save-slots-Bvuh2p_r.js → save-slots-CEUGeLuB.js} +57 -3
- package/dist/{split-screen-eULetcg-.js → split-screen-BxZ5rBe7.js} +4 -4
- package/dist/{split-screen-CIYf1zSf.d.ts → split-screen-D6sx8z3Z.d.ts} +2 -2
- package/dist/{sprite-animation-CqR2o3SA.js → sprite-animation-Bz3DHD_4.js} +1 -1
- package/dist/{src-_jk0qLsS.js → src-D-dnVrFY.js} +1 -1
- package/dist/{test-CmN54kWv.js → test-DZmCFz0Q.js} +162 -30
- package/dist/test.d.ts +56 -5
- package/dist/test.js +2 -2
- package/dist/{touch-BnCyPA0G.js → touch-CioTZB-y.js} +1 -1
- package/dist/vite.d.ts +8 -1
- package/dist/vite.js +22 -5
- package/editor/assets/{agent8-yLIEYHbd.js → agent8-GuZ5u5bi.js} +1 -1
- package/editor/assets/{debug-u31w_yhq.js → debug-BbxionO2.js} +1 -1
- package/editor/assets/{index-DK9xMGpW.js → index-CNlTzQbm.js} +92 -92
- package/editor/index.html +1 -1
- package/package.json +1 -1
- package/schemas/scene.schema.json +634 -10
- package/skills/incanto-3d-character.md +113 -5
- package/skills/incanto-assets.md +9 -3
- package/skills/incanto-behaviors-and-scripts.md +26 -2
- package/skills/incanto-building-2d-games.md +15 -0
- package/skills/incanto-building-3d-games.md +45 -0
- package/skills/incanto-editor.md +10 -1
- package/skills/incanto-environment.md +7 -2
- package/skills/incanto-game-feel.md +16 -11
- package/skills/incanto-gameplay-behaviors.md +174 -15
- package/skills/incanto-node-reference.md +122 -45
- package/skills/incanto-physics-and-input.md +67 -5
- package/skills/incanto-scene-json-authoring.md +37 -1
- package/skills/incanto-verifying-your-game.md +19 -2
- package/templates-app/beacon-isle-3d/PROJECT/Status.md +8 -0
- package/templates-app/beacon-isle-3d/PROJECT/Structure.md +3 -0
- package/templates-app/beacon-isle-3d/package.json +1 -1
- package/templates-app/beacon-isle-3d/src/game.scene.json +512 -184
- package/templates-app/beacon-isle-3d/verify.ts +144 -0
- package/templates-app/molehill-2d/package.json +1 -1
- package/templates-app/platformer-2d/PROJECT/Status.md +7 -0
- package/templates-app/platformer-2d/PROJECT/Structure.md +3 -0
- package/templates-app/platformer-2d/package.json +1 -1
- package/templates-app/platformer-2d/src/game.scene.json +319 -93
- package/templates-app/platformer-2d/verify.ts +54 -0
- package/templates-app/star-survivor/package.json +1 -1
- package/templates-app/star-survivor/src/game.scene.json +18 -9
- package/templates-app/tps-3d/PROJECT/Context.md +1 -1
- package/templates-app/tps-3d/PROJECT/Status.md +40 -1
- package/templates-app/tps-3d/PROJECT/Structure.md +19 -0
- package/templates-app/tps-3d/package.json +1 -1
- package/templates-app/tps-3d/src/behaviors.ts +9 -0
- package/templates-app/tps-3d/src/cover.scene.json +26 -0
- package/templates-app/tps-3d/src/game.scene.json +379 -80
- package/templates-app/tps-3d/src/main.ts +14 -0
- package/templates-app/tps-3d/src/pillar.scene.json +25 -0
- package/templates-app/tps-3d/verify.ts +582 -3
- package/templates-app/village-quest-3d/package.json +1 -1
- package/templates-app/village-quest-3d/src/grove.scene.json +98 -46
- package/templates-app/village-quest-3d/src/village.scene.json +449 -223
|
@@ -285,3 +285,147 @@ const ok = (label: string, cond: boolean): void => {
|
|
|
285
285
|
}
|
|
286
286
|
|
|
287
287
|
console.log(process.exitCode ? '\nVERIFY FAILED' : '\nVERIFY OK');
|
|
288
|
+
|
|
289
|
+
// ---- HEIGHTFIELD: this round's surfaces, on terrain instead of a flat arena -----
|
|
290
|
+
// A trampoline pad snapped to the island (`snapToGround: 0.1` on a StaticBody3D
|
|
291
|
+
// with `restitution 1`), a dead ball dropped on it, and a `CharacterBody3D`
|
|
292
|
+
// walker chasing the player up the island's slope (`Chase.ground` auto: the
|
|
293
|
+
// KCC's slopeLimitDeg and stepHeight decide, gravity keeps it on the ground).
|
|
294
|
+
{
|
|
295
|
+
type Vec = { position: number[] };
|
|
296
|
+
type Terrain = { heightAt(x: number, z: number): number };
|
|
297
|
+
const ballY: number[] = [];
|
|
298
|
+
let walkerFrom: number[] = [];
|
|
299
|
+
let walkerTo: number[] = [];
|
|
300
|
+
let groundUnderWalker = 0;
|
|
301
|
+
let targetY = 0;
|
|
302
|
+
let closed = 0;
|
|
303
|
+
const result = await runScript(gameJson, {
|
|
304
|
+
behaviors: BEHAVIORS,
|
|
305
|
+
durationMs: 8000,
|
|
306
|
+
steps: [
|
|
307
|
+
{
|
|
308
|
+
atMs: 100,
|
|
309
|
+
do: (ctx) => {
|
|
310
|
+
const pad = ctx.getNode('/root/Pad') as unknown as Vec;
|
|
311
|
+
const ball = ctx.getNode('/root/Ball') as unknown as Vec;
|
|
312
|
+
ball.position = [pad.position[0] ?? 0, (pad.position[1] ?? 0) + 3, pad.position[2] ?? 0];
|
|
313
|
+
},
|
|
314
|
+
},
|
|
315
|
+
...Array.from({ length: 30 }, (_, i) => ({
|
|
316
|
+
atMs: 200 + i * 100,
|
|
317
|
+
do: (ctx: { getNode(p: string): unknown }) => {
|
|
318
|
+
ballY.push((ctx.getNode('/root/Ball') as Vec).position[1] ?? 0);
|
|
319
|
+
},
|
|
320
|
+
})),
|
|
321
|
+
{
|
|
322
|
+
atMs: 3300,
|
|
323
|
+
do: (ctx) => {
|
|
324
|
+
const terrain = ctx.getNode('/root/Terrain/Ground/Surface') as unknown as Terrain;
|
|
325
|
+
const walker = ctx.getNode('/root/Walker') as unknown as Vec;
|
|
326
|
+
const player = ctx.getNode('/root/Player') as unknown as Vec;
|
|
327
|
+
const [wx = 0, , wz = 0] = walker.position;
|
|
328
|
+
// the steepest way UP from the walker, 10 m out: that is where the player goes
|
|
329
|
+
let best = { x: wx + 10, z: wz, h: -Infinity };
|
|
330
|
+
for (let a = 0; a < 16; a++) {
|
|
331
|
+
const x = wx + 10 * Math.cos((a / 16) * Math.PI * 2);
|
|
332
|
+
const z = wz + 10 * Math.sin((a / 16) * Math.PI * 2);
|
|
333
|
+
const h = terrain.heightAt(x, z);
|
|
334
|
+
if (h > best.h) best = { x, z, h };
|
|
335
|
+
}
|
|
336
|
+
player.position = [best.x, best.h + 1.4, best.z];
|
|
337
|
+
targetY = best.h;
|
|
338
|
+
walkerFrom = [...walker.position];
|
|
339
|
+
const d0 = Math.hypot(best.x - wx, best.z - wz);
|
|
340
|
+
for (let i = 0; i < 240; i++) ctx.engine.step();
|
|
341
|
+
walkerTo = [...walker.position];
|
|
342
|
+
const [tx = 0, , tz = 0] = walkerTo;
|
|
343
|
+
groundUnderWalker = terrain.heightAt(tx, tz);
|
|
344
|
+
closed = d0 - Math.hypot(best.x - tx, best.z - tz);
|
|
345
|
+
player.position = [-4.5, 18.73, 70.5]; // and back to the spawn for the quest
|
|
346
|
+
},
|
|
347
|
+
},
|
|
348
|
+
],
|
|
349
|
+
});
|
|
350
|
+
ok('runScript reported no failures (HEIGHTFIELD path)', result.ok);
|
|
351
|
+
let touch = ballY.findIndex((y, i) => i > 0 && y > (ballY[i - 1] ?? 0));
|
|
352
|
+
if (touch < 1) touch = 1;
|
|
353
|
+
const low = ballY[touch - 1] ?? 0;
|
|
354
|
+
const rebound = Math.max(...ballY.slice(touch)) - low;
|
|
355
|
+
ok(
|
|
356
|
+
`a pad snapped to the island with restitution 1 launches a dead ball back up ${rebound.toFixed(2)} m of its 3 m drop`,
|
|
357
|
+
rebound > 1.8,
|
|
358
|
+
);
|
|
359
|
+
const rose = (walkerTo[1] ?? 0) - (walkerFrom[1] ?? 0);
|
|
360
|
+
const gap = targetY - (walkerFrom[1] ?? 0);
|
|
361
|
+
ok(
|
|
362
|
+
`the walker chased the player UP the slope: closed ${closed.toFixed(1)} m of 10, rose ${rose.toFixed(2)} m of a ${gap.toFixed(2)} m climb`,
|
|
363
|
+
closed > 5 && rose > gap * 0.5,
|
|
364
|
+
);
|
|
365
|
+
const hover = (walkerTo[1] ?? 0) - groundUnderWalker;
|
|
366
|
+
ok(
|
|
367
|
+
`…and stayed on the ground while doing it (capsule centre ${hover.toFixed(2)} m over the terrain, 0.95 expected)`,
|
|
368
|
+
hover > 0.5 && hover < 1.6,
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// ---- FLOATS: what `Buoyancy.water`, `samples` and `drag` do on the open sea --------
|
|
373
|
+
// Set by no example: every raft in the repo took the first Water3D, four samples
|
|
374
|
+
// and the default drag. Two crates float west of the island (the heightfield
|
|
375
|
+
// ends at x −120): the Buoy names its water, samples once and says `drag: 0.1`;
|
|
376
|
+
// the Crate keeps the defaults. Both `drift: 0`, so a 3 m/s shove is the only
|
|
377
|
+
// thing moving them and drag is the only thing that differs. Asserted by how
|
|
378
|
+
// far each travels in 2 s — e^{-drag·t} says 5.4 m against 2.8 m — and by both
|
|
379
|
+
// still sitting at the waterline afterwards.
|
|
380
|
+
{
|
|
381
|
+
type Vec = { position: number[] };
|
|
382
|
+
type Body = { mass: number; applyImpulse(i: [number, number, number]): void };
|
|
383
|
+
type Water = { heightAt(x: number, z: number): number };
|
|
384
|
+
let buoyRest = 0;
|
|
385
|
+
let crateRest = 0;
|
|
386
|
+
let buoyRun = 0;
|
|
387
|
+
let crateRun = 0;
|
|
388
|
+
let buoyOver = 99;
|
|
389
|
+
let crateOver = 99;
|
|
390
|
+
const result = await runScript(gameJson, {
|
|
391
|
+
behaviors: BEHAVIORS,
|
|
392
|
+
durationMs: 8000,
|
|
393
|
+
steps: [
|
|
394
|
+
{
|
|
395
|
+
atMs: 100,
|
|
396
|
+
do: (ctx) => {
|
|
397
|
+
const sea = ctx.getNode('/root/Terrain/Sea') as unknown as Water;
|
|
398
|
+
const buoy = ctx.getNode('/root/Buoy') as unknown as Vec & Body;
|
|
399
|
+
const crate = ctx.getNode('/root/Crate') as unknown as Vec & Body;
|
|
400
|
+
for (let i = 0; i < 240; i++) ctx.engine.step(); // 4 s to settle on the waterline
|
|
401
|
+
buoyRest = buoy.position[1] ?? 0;
|
|
402
|
+
crateRest = crate.position[1] ?? 0;
|
|
403
|
+
const bx = buoy.position[0] ?? 0;
|
|
404
|
+
const cx = crate.position[0] ?? 0;
|
|
405
|
+
buoy.applyImpulse([3 * buoy.mass, 0, 0]);
|
|
406
|
+
crate.applyImpulse([3 * crate.mass, 0, 0]);
|
|
407
|
+
for (let i = 0; i < 120; i++) ctx.engine.step();
|
|
408
|
+
buoyRun = (buoy.position[0] ?? 0) - bx;
|
|
409
|
+
crateRun = (crate.position[0] ?? 0) - cx;
|
|
410
|
+
buoyOver =
|
|
411
|
+
(buoy.position[1] ?? 0) - sea.heightAt(buoy.position[0] ?? 0, buoy.position[2] ?? 0);
|
|
412
|
+
crateOver =
|
|
413
|
+
(crate.position[1] ?? 0) - sea.heightAt(crate.position[0] ?? 0, crate.position[2] ?? 0);
|
|
414
|
+
},
|
|
415
|
+
},
|
|
416
|
+
],
|
|
417
|
+
});
|
|
418
|
+
ok('runScript reported no failures (FLOATS path)', result.ok);
|
|
419
|
+
ok(
|
|
420
|
+
`water "/root/Terrain/Sea", samples 1: the Buoy settles at y ${buoyRest.toFixed(2)} (sea 9.19 + draft 0.35 ≈ 9.54), the Crate at ${crateRest.toFixed(2)}`,
|
|
421
|
+
Math.abs(buoyRest - 9.54) < 0.5 && Math.abs(crateRest - 9.54) < 0.5,
|
|
422
|
+
);
|
|
423
|
+
ok(
|
|
424
|
+
`drag 0.1 vs 0.9: the same 3 m/s shove carries the Buoy ${buoyRun.toFixed(2)} m and the Crate ${crateRun.toFixed(2)} m in 2 s (5.4 vs 2.8 expected)`,
|
|
425
|
+
buoyRun > crateRun * 1.5 && buoyRun > 4 && crateRun > 1.5 && crateRun < 3.5,
|
|
426
|
+
);
|
|
427
|
+
ok(
|
|
428
|
+
`…and both still ride the waterline (${buoyOver.toFixed(2)} m and ${crateOver.toFixed(2)} m over the sea)`,
|
|
429
|
+
Math.abs(buoyOver - 0.35) < 0.5 && Math.abs(crateOver - 0.35) < 0.5,
|
|
430
|
+
);
|
|
431
|
+
}
|
|
@@ -58,3 +58,10 @@
|
|
|
58
58
|
- One hand-authored level — extend by copying nodes into the right GROUP
|
|
59
59
|
(`enemy`/`hazard`/`pit`/`checkpoint`/`platform`); `PlayerController` picks them
|
|
60
60
|
up with no extra wiring.
|
|
61
|
+
|
|
62
|
+
## The round's surfaces, in 2D (2026-09-04)
|
|
63
|
+
|
|
64
|
+
A course past GroundD (x 3000+): `Trampoline` (`restitution 1`) with a dead `Bouncer`, `Ice`
|
|
65
|
+
(`friction 0`) with a `Sled` at 260 px/s, and a `Hound` (`CharacterBody2D` + `Chase`
|
|
66
|
+
`ground: true`) under a `Perch`. `verify.ts` measures the rebound (287 of 300 px), the slide
|
|
67
|
+
(400 px of ice) and that the hound stays on the floor under a perched player.
|
|
@@ -75,3 +75,6 @@ The vibe-coding rules: read the skills, JSON structure, built-ins first, omit
|
|
|
75
75
|
uids, declared assets, y-down space, collider props, GROUP-DRIVEN centralised
|
|
76
76
|
damage (§8), deferred engine access (§11), debug draw, delta discipline, verify
|
|
77
77
|
like a user.
|
|
78
|
+
|
|
79
|
+
- `CourseFloor`, `Trampoline`, `Bouncer`, `Ice`, `Sled`, `Hound`, `Perch` — the harness's course for
|
|
80
|
+
the 2D surfaces and the ground chaser.
|