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.
- package/bin/incanto-play.mjs +7 -2
- package/dist/2d.d.ts +12 -5
- package/dist/2d.js +3 -3
- package/dist/3d.d.ts +67 -6
- package/dist/3d.js +4 -4
- package/dist/{audit-C6rMyict.js → audit-C4kmDK0o.js} +16 -0
- package/dist/{behavior-BAc0erXF.d.ts → behavior-D_jMpFh8.d.ts} +117 -55
- package/dist/{create-game-5z_QVtLx.js → create-game-bKHgHcsZ.js} +19 -10
- package/dist/{create-game-DuBTv2zI.js → create-game-gXI7PYl0.js} +61 -46
- package/dist/debug.d.ts +1 -1
- package/dist/debug.js +7 -2
- package/dist/{duplicate-BgtFrFo4.js → duplicate-KRPtUtzl.js} +1 -1
- package/dist/{editor-switch-B0wB_DSr.d.ts → editor-switch-BJb-CWfA.d.ts} +12 -1
- package/dist/{register-DJ0SByQg.js → environment-presets--DigHNg4.js} +42 -11
- package/dist/{gameplay-Cfr6aFZ1.js → gameplay-BftxM_It.js} +2 -2
- package/dist/gameplay.d.ts +1 -1
- package/dist/gameplay.js +1 -1
- package/dist/index.d.ts +13 -3
- package/dist/index.js +7 -7
- package/dist/{loader-B242FF6N.d.ts → loader-BYBrqTxP.d.ts} +1 -1
- package/dist/{loader-BZqOKfI2.js → loader-BlaRQGaA.js} +482 -6
- package/dist/net.d.ts +1 -1
- package/dist/net.js +3 -3
- package/dist/{pathfinding-BwD974Ss.d.ts → pathfinding-BwhqPD3i.d.ts} +1 -1
- package/dist/{physics-2d-3kOQCtgd.js → physics-2d-D9wquBvK.js} +4 -3
- package/dist/{physics-3d-CeRH-Ff_.js → physics-3d-CnPygVGo.js} +5 -4
- package/dist/react.d.ts +1 -1
- package/dist/react.js +1 -1
- package/dist/{register-MelqEdza.js → register-CUY284Is.js} +3 -3
- package/dist/{register-BTg0EM7s.js → register-Dzkd6-os.js} +56 -380
- package/dist/{register-DWcWq4QG.js → register-tkR_8tWg.js} +4 -4
- package/dist/{registry-BVJ2HbCn.js → registry-CJdGpT2V.js} +6 -2
- package/dist/test-WwRIlXsK.js +642 -0
- package/dist/test.d.ts +55 -8
- package/dist/test.js +3 -364
- package/dist/{touch-031PxtCR.js → touch-BoNg_MnF.js} +2 -1
- package/dist/vite.js +1 -1
- package/editor/assets/{agent8-CAp0i5qn.js → agent8-C5k1nTCH.js} +1 -1
- package/editor/assets/{debug-BoEYfbqK.js → debug-BT_0mjk-.js} +3 -2
- package/editor/assets/{index-BO6WU8by.js → index-gfyrByWw.js} +90 -90
- package/editor/index.html +1 -1
- package/package.json +1 -1
- package/skills/README.md +14 -2
- package/skills/incanto-building-2d-games.md +16 -0
- package/skills/incanto-building-3d-games.md +16 -0
- package/skills/incanto-verifying-your-game.md +118 -0
- package/templates-app/beacon-isle-3d/package.json +1 -1
- package/templates-app/tps-3d/package.json +1 -1
- package/templates-app/village-quest-3d/package.json +1 -1
package/bin/incanto-play.mjs
CHANGED
|
@@ -17,6 +17,7 @@ import { dirname, join, resolve } from 'node:path';
|
|
|
17
17
|
* pointer <dx> <dy> look deltas wheel <dy>
|
|
18
18
|
* capture [file] current state as scene JSON (stdout or file)
|
|
19
19
|
* describe one line per node, non-default props only
|
|
20
|
+
* framing what the camera SEES: on/off screen, lights, overlaps
|
|
20
21
|
* logs engine.log entries since the last call
|
|
21
22
|
* quit
|
|
22
23
|
*
|
|
@@ -56,7 +57,7 @@ if (args.help || !args.scene) {
|
|
|
56
57
|
console.error(`Usage: incanto-play <scene.json> [--behaviors <file.ts|.js>] [--seed N] [--fixed-hz 60] [--commands <file>]
|
|
57
58
|
|
|
58
59
|
Headless play: stdin commands (step/press/release/vector/key/mouse/pointer/
|
|
59
|
-
wheel/capture/describe/logs/quit), JSON-line responses on stdout.`);
|
|
60
|
+
wheel/capture/describe/framing/logs/quit), JSON-line responses on stdout.`);
|
|
60
61
|
process.exit(args.help && args.scene !== undefined ? 0 : 1);
|
|
61
62
|
}
|
|
62
63
|
|
|
@@ -175,6 +176,10 @@ function handle(line) {
|
|
|
175
176
|
case 'describe':
|
|
176
177
|
out({ ok: true, cmd, text: session.describe() });
|
|
177
178
|
break;
|
|
179
|
+
case 'framing':
|
|
180
|
+
// what the camera can SEE — on/off screen, lit or black, overlaps
|
|
181
|
+
out({ ok: true, cmd, text: session.framing() });
|
|
182
|
+
break;
|
|
178
183
|
case 'logs':
|
|
179
184
|
out({ ok: true, cmd, entries: session.drainLogs() });
|
|
180
185
|
break;
|
|
@@ -188,7 +193,7 @@ function handle(line) {
|
|
|
188
193
|
cmd,
|
|
189
194
|
error: {
|
|
190
195
|
code: 'UNKNOWN_COMMAND',
|
|
191
|
-
message: `unknown command '${cmd}'. Commands: step press release vector key mouse pointer wheel capture describe logs quit`,
|
|
196
|
+
message: `unknown command '${cmd}'. Commands: step press release vector key mouse pointer wheel capture describe framing logs quit`,
|
|
192
197
|
},
|
|
193
198
|
});
|
|
194
199
|
}
|
package/dist/2d.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { C as RendererStats, S as GameStats, X as Node, b as Scheduler, l as PropSchema, n as BehaviorCtor, v as Engine, w as Scene$1 } from "./behavior-D_jMpFh8.js";
|
|
2
|
+
import { n as DiagnosticSink, t as EditorSwitchOptions } from "./editor-switch-BJb-CWfA.js";
|
|
1
3
|
import { i as SceneJson, s as JsonObject } from "./schema-CcoWb32N.js";
|
|
2
|
-
import {
|
|
3
|
-
import { t as EditorSwitchOptions } from "./editor-switch-B0wB_DSr.js";
|
|
4
|
-
import { t as LoadSceneOptions } from "./loader-B242FF6N.js";
|
|
4
|
+
import { t as LoadSceneOptions } from "./loader-BYBrqTxP.js";
|
|
5
5
|
import { t as ParticleSim } from "./particle-sim-CwJ5rI_P.js";
|
|
6
6
|
import { Group, Mesh, Object3D, Scene, Texture } from "three";
|
|
7
7
|
import * as RapierNs from "@dimforge/rapier2d-compat";
|
|
@@ -24,9 +24,15 @@ interface SheetInfo {
|
|
|
24
24
|
* `"$key"`. The texture loader is injectable so tests run headless.
|
|
25
25
|
*/
|
|
26
26
|
declare class AssetStore2D {
|
|
27
|
+
private readonly engine;
|
|
27
28
|
private readonly entries;
|
|
28
29
|
private readonly loader;
|
|
29
|
-
|
|
30
|
+
/**
|
|
31
|
+
* `engine` is optional so a bare store still constructs in tests. Given one,
|
|
32
|
+
* a texture that fails to load lands in `engine.log` — where the overlay and
|
|
33
|
+
* `runScript()` can see it — instead of only in the browser console.
|
|
34
|
+
*/
|
|
35
|
+
constructor(loader?: (url: string, callbacks?: TextureLoadCallbacks) => Texture, engine?: DiagnosticSink | null);
|
|
30
36
|
/** Load (or extend with) a scene's `assets` declarations. Hard-validates each entry. */
|
|
31
37
|
load(assets: Record<string, JsonObject>): void;
|
|
32
38
|
/** Load status of a declared asset KEY (no '$'), or undefined if unknown. */
|
|
@@ -757,7 +763,8 @@ declare class Renderer2D {
|
|
|
757
763
|
private readonly worldCam;
|
|
758
764
|
private readonly uiCam;
|
|
759
765
|
private readonly engine;
|
|
760
|
-
|
|
766
|
+
/** The texture store. Public so `renderer.assets.status(key)` is reachable. */
|
|
767
|
+
readonly assets: AssetStore2D;
|
|
761
768
|
private readonly loadedAssetScenes;
|
|
762
769
|
private readonly disconnect;
|
|
763
770
|
private readonly canvas;
|
package/dist/2d.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
2
|
-
import { a as AssetStore2D, i as syncTree2D, r as Renderer2D, t as createGame2D } from "./create-game-
|
|
3
|
-
import { _ as RigidBody2D, a as parseCells, c as ColorRect2D, d as AnimatedSprite2D, f as Sprite2D, g as PhysicsBody2D, h as CharacterBody2D, i as mergeSolidRects, l as CharacterController2D, m as Area2D, n as UILayer, o as Particles2D, p as Joint2D, r as TileMap2D, s as Label, t as registerNodes2D, u as Camera2D, v as StaticBody2D, y as Node2D } from "./register-
|
|
4
|
-
import { n as enablePhysics2D, t as Physics2D } from "./physics-2d-
|
|
2
|
+
import { a as AssetStore2D, i as syncTree2D, r as Renderer2D, t as createGame2D } from "./create-game-bKHgHcsZ.js";
|
|
3
|
+
import { _ as RigidBody2D, a as parseCells, c as ColorRect2D, d as AnimatedSprite2D, f as Sprite2D, g as PhysicsBody2D, h as CharacterBody2D, i as mergeSolidRects, l as CharacterController2D, m as Area2D, n as UILayer, o as Particles2D, p as Joint2D, r as TileMap2D, s as Label, t as registerNodes2D, u as Camera2D, v as StaticBody2D, y as Node2D } from "./register-tkR_8tWg.js";
|
|
4
|
+
import { n as enablePhysics2D, t as Physics2D } from "./physics-2d-D9wquBvK.js";
|
|
5
5
|
//#region src/2d/library-sprite.ts
|
|
6
6
|
/**
|
|
7
7
|
* Turn a library sprite-animation JSON into a scene-ready spritesheet asset
|
package/dist/3d.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { C as RendererStats, S as GameStats, X as Node, b as Scheduler, l as PropSchema, n as BehaviorCtor, v as Engine, w as Scene$1 } from "./behavior-D_jMpFh8.js";
|
|
2
|
+
import { n as DiagnosticSink, t as EditorSwitchOptions } from "./editor-switch-BJb-CWfA.js";
|
|
1
3
|
import { i as SceneJson, s as JsonObject } from "./schema-CcoWb32N.js";
|
|
2
|
-
import {
|
|
3
|
-
import { t as EditorSwitchOptions } from "./editor-switch-B0wB_DSr.js";
|
|
4
|
-
import { t as LoadSceneOptions } from "./loader-B242FF6N.js";
|
|
4
|
+
import { t as LoadSceneOptions } from "./loader-BYBrqTxP.js";
|
|
5
5
|
import { t as ParticleSim } from "./particle-sim-CwJ5rI_P.js";
|
|
6
|
-
import { n as PathGrid, s as SpatialPose } from "./pathfinding-
|
|
6
|
+
import { n as PathGrid, s as SpatialPose } from "./pathfinding-BwhqPD3i.js";
|
|
7
7
|
import { AnimationClip, AnimationMixer, BufferGeometry, Color, DirectionalLight, Group, InstancedMesh, Mesh, MeshPhysicalMaterial, Object3D, PerspectiveCamera, Scene, ShaderMaterial, Vector3, WebGLRenderer } from "three";
|
|
8
8
|
import { VRM } from "@pixiv/three-vrm";
|
|
9
9
|
import { Sky } from "three/examples/jsm/objects/Sky.js";
|
|
@@ -44,12 +44,28 @@ interface AnimationEntry {
|
|
|
44
44
|
* @pixiv/three-vrm (VRM 0.x facing fixed via rotateVRM0).
|
|
45
45
|
*/
|
|
46
46
|
declare class AssetStore3D {
|
|
47
|
+
private readonly engine;
|
|
47
48
|
private readonly models;
|
|
48
49
|
private readonly animations;
|
|
49
50
|
private readonly modelKeys;
|
|
50
51
|
private readonly animationKeys;
|
|
51
52
|
private readonly loader;
|
|
52
|
-
|
|
53
|
+
/**
|
|
54
|
+
* `engine` is optional only so a bare store still constructs in tests. Give it
|
|
55
|
+
* one and every load failure lands in `engine.log` — where the debug overlay,
|
|
56
|
+
* `runScript()` and any agent tool can see it. Without it a 404'd GLB was a
|
|
57
|
+
* console line and an invisible model.
|
|
58
|
+
*/
|
|
59
|
+
constructor(engine?: DiagnosticSink | null);
|
|
60
|
+
/**
|
|
61
|
+
* Every asset that failed, with the reason — the answer to "why is my model
|
|
62
|
+
* not there". Empty is the healthy case.
|
|
63
|
+
*/
|
|
64
|
+
errors(): Array<{
|
|
65
|
+
ref: string;
|
|
66
|
+
url: string;
|
|
67
|
+
error: string;
|
|
68
|
+
}>;
|
|
53
69
|
/** Map a scene's `assets` header. */
|
|
54
70
|
load(assets: JsonObject): void;
|
|
55
71
|
/** Animation `$key`s declared by the scene (editor dropdowns). */
|
|
@@ -472,6 +488,16 @@ interface CreateGame3DOptions {
|
|
|
472
488
|
/** Keep the frame rate by rendering fewer pixels while frames run slow
|
|
473
489
|
* (default ON). `false` pins the resolution — screenshots, capture, tests. */
|
|
474
490
|
adaptiveResolution?: boolean;
|
|
491
|
+
/**
|
|
492
|
+
* Keep the drawing buffer readable so the canvas can be screenshot.
|
|
493
|
+
*
|
|
494
|
+
* WebGL clears it the instant a frame composites, so `toDataURL()` — and every
|
|
495
|
+
* screenshot built on it — is BLANK without this. Off by default: it costs a
|
|
496
|
+
* buffer copy per frame and only a run that means to look at the output should
|
|
497
|
+
* pay for it. `environment.rendering.preserveDrawingBuffer` does the same from
|
|
498
|
+
* the scene.
|
|
499
|
+
*/
|
|
500
|
+
preserveDrawingBuffer?: boolean;
|
|
475
501
|
resolveScene?: LoadSceneOptions["resolveScene"];
|
|
476
502
|
/**
|
|
477
503
|
* Boot progress, for the loading screen an app already has.
|
|
@@ -495,6 +521,14 @@ interface CreateGame3DOptions {
|
|
|
495
521
|
interface GameRenderer {
|
|
496
522
|
dispose(): void;
|
|
497
523
|
stats?(): RendererStats;
|
|
524
|
+
/** Loaded models/animations — `assets.errors()` answers "why is my model missing". */
|
|
525
|
+
assets?: {
|
|
526
|
+
errors(): Array<{
|
|
527
|
+
ref: string;
|
|
528
|
+
url: string;
|
|
529
|
+
error: string;
|
|
530
|
+
}>;
|
|
531
|
+
};
|
|
498
532
|
}
|
|
499
533
|
interface Game3D {
|
|
500
534
|
engine: Engine;
|
|
@@ -513,6 +547,18 @@ interface Game3D {
|
|
|
513
547
|
/** Engine + renderer perf counters in one read (fps/nodes/triangles/…). */
|
|
514
548
|
stats(): GameStats;
|
|
515
549
|
/**
|
|
550
|
+
* Assets that failed to load, with the reason.
|
|
551
|
+
*
|
|
552
|
+
* "The model just never appeared" used to be unanswerable from outside: the
|
|
553
|
+
* store was private on the renderer and the 404 went to the console only.
|
|
554
|
+
* Check this — and `stats().errors` — before assuming a scene is fine.
|
|
555
|
+
*/
|
|
556
|
+
assetErrors(): Array<{
|
|
557
|
+
ref: string;
|
|
558
|
+
url: string;
|
|
559
|
+
error: string;
|
|
560
|
+
}>;
|
|
561
|
+
/**
|
|
516
562
|
* Turn this page into the scene editor, right where the player is standing —
|
|
517
563
|
* what the ☰ debug menu's "edit this scene" calls. Bind it to your own key or
|
|
518
564
|
* button if you want the switch without the overlay.
|
|
@@ -2962,6 +3008,16 @@ interface Renderer3DOptions {
|
|
|
2962
3008
|
* the resolution — for screenshots, video capture and pixel-diff tests.
|
|
2963
3009
|
*/
|
|
2964
3010
|
adaptiveResolution?: boolean;
|
|
3011
|
+
/**
|
|
3012
|
+
* Keep the drawing buffer readable so the canvas can be SCREENSHOT.
|
|
3013
|
+
*
|
|
3014
|
+
* WebGL clears it the moment a frame composites, so `canvas.toDataURL()` and
|
|
3015
|
+
* anything built on it (a Playwright screenshot, a pixel-diff test) comes
|
|
3016
|
+
* back blank without this. Off by default — it costs a buffer copy per frame
|
|
3017
|
+
* and only a run that intends to look at the output should pay it. The scene
|
|
3018
|
+
* can ask for it too: `environment.rendering.preserveDrawingBuffer`.
|
|
3019
|
+
*/
|
|
3020
|
+
preserveDrawingBuffer?: boolean;
|
|
2965
3021
|
}
|
|
2966
3022
|
/**
|
|
2967
3023
|
* WebGL presentation layer: subscribes to `engine.updated`, mirrors the active
|
|
@@ -2991,7 +3047,12 @@ declare class Renderer3D {
|
|
|
2991
3047
|
private readonly engine;
|
|
2992
3048
|
private readonly disconnect;
|
|
2993
3049
|
private readonly canvas;
|
|
2994
|
-
|
|
3050
|
+
/**
|
|
3051
|
+
* The model/animation store. Public so a game (and an agent's tool) can ask
|
|
3052
|
+
* `renderer.assets.errors()` why a model is not there — it used to be private,
|
|
3053
|
+
* so a 404'd GLB was unanswerable from outside.
|
|
3054
|
+
*/
|
|
3055
|
+
readonly assets: AssetStore3D;
|
|
2995
3056
|
private readonly ownsAssets;
|
|
2996
3057
|
private readonly loadedAssetScenes;
|
|
2997
3058
|
/** The scene whose GPU programs have been pre-compiled (warm-up, see render). */
|
package/dist/3d.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
2
|
-
import { A as Water3D, F as PhysicsBody3D, I as RigidBody3D, L as StaticBody3D, M as WaterCutout3D, N as Area3D, P as CharacterBody3D, R as Node3D, V as WATER_MAX_RIPPLES, j as WATER_CUTOUT_MAX } from "./gameplay-
|
|
3
|
-
import { a as Environment3D, c as sunDirectionFromElevationAzimuth, i as syncTree, l as sunDirectionFromSky, o as horizonColorFromSky, r as Renderer3D, s as parseEnvironment3D, t as createGame3D, u as AssetStore3D } from "./create-game-
|
|
4
|
-
import { A as
|
|
2
|
+
import { A as Water3D, F as PhysicsBody3D, I as RigidBody3D, L as StaticBody3D, M as WaterCutout3D, N as Area3D, P as CharacterBody3D, R as Node3D, V as WATER_MAX_RIPPLES, j as WATER_CUTOUT_MAX } from "./gameplay-BftxM_It.js";
|
|
3
|
+
import { a as Environment3D, c as sunDirectionFromElevationAzimuth, i as syncTree, l as sunDirectionFromSky, o as horizonColorFromSky, r as Renderer3D, s as parseEnvironment3D, t as createGame3D, u as AssetStore3D } from "./create-game-gXI7PYl0.js";
|
|
4
|
+
import { A as Terrain3D, B as keyboardIntensity, C as resolveFlowerDensity, D as BoneLookAt3D, E as Camera3D, F as InstancedMesh3D, H as rigPose, I as MeshInstance3D, M as TERRAIN_THEMES, N as terrainThemeLayers, O as BoneAttachment3D, P as Joint3D, R as QUARTER_PITCH, S as Flowers3D, T as CharacterController3D, V as movementState, _ as LoftMesh3D, a as Tree3D, b as Foliage3D, c as buildRiverRings, d as riverCarveChannels, f as riverStepFor, g as ModelInstance3D, h as Particles3D, i as VoxelGrid3D, j as DEFAULT_TERRAIN_TEXTURE_BASE, k as Billboard3D, l as findRiverCoverageGaps, m as traceDownhillPath, n as registerNodes3D, o as Trail3D, p as smoothCourse, r as VOXEL_PALETTE, s as River3D, u as projectToRiver, v as DirectionalLight3D, w as FLOWER_VARIETIES, x as DENSITY_PRESETS, y as OmniLight3D, z as cameraRelative } from "./environment-presets--DigHNg4.js";
|
|
5
5
|
import { n as splatWeights, t as buildHeightmap } from "./heightmap-CRK0M4jT.js";
|
|
6
|
-
import { n as enablePhysics3D, t as Physics3D } from "./physics-3d-
|
|
6
|
+
import { n as enablePhysics3D, t as Physics3D } from "./physics-3d-CnPygVGo.js";
|
|
7
7
|
//#region src/3d/environment-runtime.ts
|
|
8
8
|
/**
|
|
9
9
|
* Live environment editing — the renderer re-applies `scene.environment`
|
|
@@ -25,6 +25,19 @@ const HUD_WIDGETS = new Set([
|
|
|
25
25
|
"UiButton",
|
|
26
26
|
"UiDialogue"
|
|
27
27
|
]);
|
|
28
|
+
const LIGHT_TYPES = new Set([
|
|
29
|
+
"DirectionalLight3D",
|
|
30
|
+
"OmniLight3D",
|
|
31
|
+
"SpotLight3D"
|
|
32
|
+
]);
|
|
33
|
+
/** Does the scene header light anything by itself (sky, hdri, ambient, preset)? */
|
|
34
|
+
function isLit(environment) {
|
|
35
|
+
if (!environment) return false;
|
|
36
|
+
if (environment.preset !== void 0 || environment.sky !== void 0) return true;
|
|
37
|
+
if (environment.hdri !== void 0) return true;
|
|
38
|
+
const ambient = environment.ambient;
|
|
39
|
+
return typeof ambient?.intensity === "number" && ambient.intensity > 0;
|
|
40
|
+
}
|
|
28
41
|
/** Human-readable warnings (empty = clean). Pure JSON walk, no registry. */
|
|
29
42
|
function auditScene(scene) {
|
|
30
43
|
const warnings = [];
|
|
@@ -32,6 +45,7 @@ function auditScene(scene) {
|
|
|
32
45
|
if (!root) return warnings;
|
|
33
46
|
let currentCameras = 0;
|
|
34
47
|
let cameras = 0;
|
|
48
|
+
let lights = 0;
|
|
35
49
|
const walk = (node, path, insideHud, staticRoot) => {
|
|
36
50
|
const type = node.type ?? "";
|
|
37
51
|
const props = node.props ?? {};
|
|
@@ -39,6 +53,7 @@ function auditScene(scene) {
|
|
|
39
53
|
cameras += 1;
|
|
40
54
|
if (props.current === true) currentCameras += 1;
|
|
41
55
|
}
|
|
56
|
+
if (LIGHT_TYPES.has(type)) lights += 1;
|
|
42
57
|
if (BODY_TYPES.has(type)) {
|
|
43
58
|
const collider = props.collider;
|
|
44
59
|
if (!collider || collider.shape === void 0) warnings.push(`${path}: ${type} has no collider — physics will skip it (add "collider": { "shape": ... }).`);
|
|
@@ -52,6 +67,7 @@ function auditScene(scene) {
|
|
|
52
67
|
walk(root, root.name ?? "root", false, root.props?.static === true ? root.name ?? "root" : null);
|
|
53
68
|
if (cameras > 0 && currentCameras === 0) warnings.push("no camera has \"current\": true — the screen renders from a default view, probably not what you framed.");
|
|
54
69
|
if (currentCameras > 1) warnings.push(`${currentCameras} cameras claim "current": true — only the first found wins.`);
|
|
70
|
+
if (scene.dimension === "3d" && lights === 0 && !isLit(scene.environment)) warnings.push("nothing lights this 3D scene — no light node, no environment sky/hdri, and no ambient intensity. It will render black. Add a DirectionalLight3D, or an \"environment\": { \"sky\": { \"type\": \"atmosphere\" } } / { \"preset\": \"...\" }.");
|
|
55
71
|
return warnings;
|
|
56
72
|
}
|
|
57
73
|
//#endregion
|
|
@@ -1,5 +1,59 @@
|
|
|
1
1
|
import { a as Rng, c as JsonValue, i as SceneJson, s as JsonObject } from "./schema-CcoWb32N.js";
|
|
2
2
|
|
|
3
|
+
//#region src/core/signal.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Minimal typed observer used for every engine event (Godot signal semantics).
|
|
6
|
+
*
|
|
7
|
+
* Emission is snapshot-based: listeners added during an emit do not fire in that
|
|
8
|
+
* emit, and listeners removed during an emit are skipped.
|
|
9
|
+
*/
|
|
10
|
+
type SignalListener<Args extends unknown[]> = (...args: Args) => void;
|
|
11
|
+
declare class Signal<Args extends unknown[] = unknown[]> {
|
|
12
|
+
private connections;
|
|
13
|
+
/** Connect a listener. Returns a disposer. Connecting the same fn twice is a no-op. */
|
|
14
|
+
connect(fn: SignalListener<Args>, opts?: {
|
|
15
|
+
once?: boolean;
|
|
16
|
+
}): () => void;
|
|
17
|
+
disconnect(fn: SignalListener<Args>): void;
|
|
18
|
+
disconnectAll(): void;
|
|
19
|
+
emit(...args: Args): void;
|
|
20
|
+
get connectionCount(): number;
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/core/log.d.ts
|
|
24
|
+
type LogLevel = "debug" | "info" | "warn" | "error";
|
|
25
|
+
interface LogEntry {
|
|
26
|
+
/** Monotonic per-manager sequence number (1-based). */
|
|
27
|
+
readonly seq: number;
|
|
28
|
+
/** Wall-clock-ish timestamp (performance.now when available) — diagnostics only. */
|
|
29
|
+
readonly timeMs: number;
|
|
30
|
+
readonly level: LogLevel;
|
|
31
|
+
/** The raw logged values, untouched — consumers format. */
|
|
32
|
+
readonly parts: readonly unknown[];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The engine's log channel (`engine.log`, `this.log` in a Behavior): a ring
|
|
36
|
+
* buffer plus a live `added` signal, so debug overlays and headless test
|
|
37
|
+
* harnesses can tail game logs without scraping the browser console.
|
|
38
|
+
*/
|
|
39
|
+
declare class LogManager {
|
|
40
|
+
/** Fires once per entry, after it is buffered. */
|
|
41
|
+
readonly added: Signal<[LogEntry]>;
|
|
42
|
+
private readonly buffer;
|
|
43
|
+
private readonly capacity;
|
|
44
|
+
private seq;
|
|
45
|
+
constructor(capacity?: number);
|
|
46
|
+
debug(...parts: unknown[]): void;
|
|
47
|
+
info(...parts: unknown[]): void;
|
|
48
|
+
warn(...parts: unknown[]): void;
|
|
49
|
+
error(...parts: unknown[]): void;
|
|
50
|
+
/** Buffered entries, oldest first (capped at capacity). */
|
|
51
|
+
entries(): readonly LogEntry[];
|
|
52
|
+
/** Empty the buffer. The sequence keeps counting (entries stay unique). */
|
|
53
|
+
clear(): void;
|
|
54
|
+
private push;
|
|
55
|
+
}
|
|
56
|
+
//#endregion
|
|
3
57
|
//#region src/core/scene-tree.d.ts
|
|
4
58
|
/**
|
|
5
59
|
* Owns a node tree and drives its lifecycle:
|
|
@@ -63,26 +117,6 @@ declare class SceneTree {
|
|
|
63
117
|
private flushFreeQueue;
|
|
64
118
|
}
|
|
65
119
|
//#endregion
|
|
66
|
-
//#region src/core/signal.d.ts
|
|
67
|
-
/**
|
|
68
|
-
* Minimal typed observer used for every engine event (Godot signal semantics).
|
|
69
|
-
*
|
|
70
|
-
* Emission is snapshot-based: listeners added during an emit do not fire in that
|
|
71
|
-
* emit, and listeners removed during an emit are skipped.
|
|
72
|
-
*/
|
|
73
|
-
type SignalListener<Args extends unknown[]> = (...args: Args) => void;
|
|
74
|
-
declare class Signal<Args extends unknown[] = unknown[]> {
|
|
75
|
-
private connections;
|
|
76
|
-
/** Connect a listener. Returns a disposer. Connecting the same fn twice is a no-op. */
|
|
77
|
-
connect(fn: SignalListener<Args>, opts?: {
|
|
78
|
-
once?: boolean;
|
|
79
|
-
}): () => void;
|
|
80
|
-
disconnect(fn: SignalListener<Args>): void;
|
|
81
|
-
disconnectAll(): void;
|
|
82
|
-
emit(...args: Args): void;
|
|
83
|
-
get connectionCount(): number;
|
|
84
|
-
}
|
|
85
|
-
//#endregion
|
|
86
120
|
//#region src/core/node.d.ts
|
|
87
121
|
/** Lifecycle hooks a SceneTree drives. Wired in scene-tree.ts. */
|
|
88
122
|
interface NodeLifecycle {
|
|
@@ -186,6 +220,42 @@ declare class Node implements NodeLifecycle {
|
|
|
186
220
|
_propagateUpdate(dt: number): void;
|
|
187
221
|
/** @internal */
|
|
188
222
|
_propagateFixedUpdate(dt: number): void;
|
|
223
|
+
/**
|
|
224
|
+
* Report an engine-level problem about THIS node.
|
|
225
|
+
*
|
|
226
|
+
* Goes to `engine.log` (what the debug overlay, `runScript()` and any agent
|
|
227
|
+
* tool read) as well as the console. A `console.warn` alone is invisible to
|
|
228
|
+
* every channel except a human with devtools open.
|
|
229
|
+
*/
|
|
230
|
+
protected diagnose(level: "warn" | "error", ...parts: unknown[]): void;
|
|
231
|
+
/**
|
|
232
|
+
* This node threw and is being skipped. Its CHILDREN still run.
|
|
233
|
+
*
|
|
234
|
+
* The alternative is what used to happen: the exception escaped into the rAF
|
|
235
|
+
* callback, the loop was never rescheduled, and the game froze — permanently,
|
|
236
|
+
* silently, with `stats().running` still reporting true. In a vibe-coded
|
|
237
|
+
* project the least-tested code in the repo is a behavior's `update`, so this
|
|
238
|
+
* is not an edge case; it is Tuesday.
|
|
239
|
+
*
|
|
240
|
+
* Set it back to false to try the node again (the debug overlay's "resume").
|
|
241
|
+
*/
|
|
242
|
+
errored: boolean;
|
|
243
|
+
/**
|
|
244
|
+
* This node's SCRIPT threw and is being skipped — the node itself still runs.
|
|
245
|
+
*
|
|
246
|
+
* A behavior is your code; the node is the engine's. A typo in the first must
|
|
247
|
+
* not switch off the second, or breaking a sword script stops the character
|
|
248
|
+
* from walking.
|
|
249
|
+
*/
|
|
250
|
+
behaviorErrored: boolean;
|
|
251
|
+
/**
|
|
252
|
+
* Report ONCE and stop running this node's own update.
|
|
253
|
+
*
|
|
254
|
+
* Once, because a behavior that throws throws every frame: sixty identical
|
|
255
|
+
* stack traces a second buries the one line that matters and costs more than
|
|
256
|
+
* the game did.
|
|
257
|
+
*/
|
|
258
|
+
private _quarantine;
|
|
189
259
|
onEnterTree(): void;
|
|
190
260
|
onReady(): void;
|
|
191
261
|
onExitTree(): void;
|
|
@@ -646,40 +716,6 @@ declare class InputMap {
|
|
|
646
716
|
private button;
|
|
647
717
|
}
|
|
648
718
|
//#endregion
|
|
649
|
-
//#region src/core/log.d.ts
|
|
650
|
-
type LogLevel = "debug" | "info" | "warn" | "error";
|
|
651
|
-
interface LogEntry {
|
|
652
|
-
/** Monotonic per-manager sequence number (1-based). */
|
|
653
|
-
readonly seq: number;
|
|
654
|
-
/** Wall-clock-ish timestamp (performance.now when available) — diagnostics only. */
|
|
655
|
-
readonly timeMs: number;
|
|
656
|
-
readonly level: LogLevel;
|
|
657
|
-
/** The raw logged values, untouched — consumers format. */
|
|
658
|
-
readonly parts: readonly unknown[];
|
|
659
|
-
}
|
|
660
|
-
/**
|
|
661
|
-
* The engine's log channel (`engine.log`, `this.log` in a Behavior): a ring
|
|
662
|
-
* buffer plus a live `added` signal, so debug overlays and headless test
|
|
663
|
-
* harnesses can tail game logs without scraping the browser console.
|
|
664
|
-
*/
|
|
665
|
-
declare class LogManager {
|
|
666
|
-
/** Fires once per entry, after it is buffered. */
|
|
667
|
-
readonly added: Signal<[LogEntry]>;
|
|
668
|
-
private readonly buffer;
|
|
669
|
-
private readonly capacity;
|
|
670
|
-
private seq;
|
|
671
|
-
constructor(capacity?: number);
|
|
672
|
-
debug(...parts: unknown[]): void;
|
|
673
|
-
info(...parts: unknown[]): void;
|
|
674
|
-
warn(...parts: unknown[]): void;
|
|
675
|
-
error(...parts: unknown[]): void;
|
|
676
|
-
/** Buffered entries, oldest first (capped at capacity). */
|
|
677
|
-
entries(): readonly LogEntry[];
|
|
678
|
-
/** Empty the buffer. The sequence keeps counting (entries stay unique). */
|
|
679
|
-
clear(): void;
|
|
680
|
-
private push;
|
|
681
|
-
}
|
|
682
|
-
//#endregion
|
|
683
719
|
//#region src/core/scene/scene.d.ts
|
|
684
720
|
/**
|
|
685
721
|
* A loaded, live scene: the node tree plus everything needed to round-trip
|
|
@@ -733,6 +769,13 @@ interface EngineStats {
|
|
|
733
769
|
nodes: number;
|
|
734
770
|
/** Whether the loop is scheduled (start() without stop()/dispose()). */
|
|
735
771
|
running: boolean;
|
|
772
|
+
/**
|
|
773
|
+
* Errors swallowed to keep the game alive since start: nodes that quarantined
|
|
774
|
+
* themselves plus frames that threw. Non-zero means something IS broken and
|
|
775
|
+
* `engine.log` says what — a green-looking game with `errors: 7` is the case
|
|
776
|
+
* this counter exists for.
|
|
777
|
+
*/
|
|
778
|
+
errors: number;
|
|
736
779
|
}
|
|
737
780
|
/** GPU-side counters from `renderer.stats()` — the LAST rendered frame. */
|
|
738
781
|
interface RendererStats {
|
|
@@ -838,6 +881,25 @@ declare class Engine {
|
|
|
838
881
|
/** Replace the active scene. The previous scene's root is freed. */
|
|
839
882
|
setScene(scene: Scene): void;
|
|
840
883
|
start(): void;
|
|
884
|
+
/**
|
|
885
|
+
* A frame threw somewhere outside a single node (a renderer, physics, a
|
|
886
|
+
* signal handler). Report the first one and count the rest: a broken frame
|
|
887
|
+
* repeats sixty times a second, and sixty identical traces a second is how
|
|
888
|
+
* you lose the first one.
|
|
889
|
+
*/
|
|
890
|
+
private reportFrameError;
|
|
891
|
+
/** @internal A node quarantined itself; count it for `stats().errors`. */
|
|
892
|
+
_nodeErrored(_node: unknown): void;
|
|
893
|
+
private errorCount;
|
|
894
|
+
private lastFrameError;
|
|
895
|
+
/**
|
|
896
|
+
* Every node with something quarantined — its own update, its script, or
|
|
897
|
+
* both — so a tool can list them and put them back. Walks on demand; there is
|
|
898
|
+
* no per-frame bookkeeping for this.
|
|
899
|
+
*/
|
|
900
|
+
erroredNodes(): Node[];
|
|
901
|
+
/** Un-skip every quarantined node — the "try again" after you fix the code. */
|
|
902
|
+
resumeErroredNodes(): number;
|
|
841
903
|
stop(): void;
|
|
842
904
|
/**
|
|
843
905
|
* Live performance counters, queryable at ANY time. fps/frameMs come from a
|
|
@@ -973,4 +1035,4 @@ declare function registeredBehaviors(): string[];
|
|
|
973
1035
|
/** Test isolation helper. */
|
|
974
1036
|
declare function clearBehaviors(): void;
|
|
975
1037
|
//#endregion
|
|
976
|
-
export {
|
|
1038
|
+
export { LogEntry as $, VoicePreset as A, SfxParams as B, RendererStats as C, SfxPlayOptions as D, SfxEngine as E, Vec3 as F, MusicManager as G, SynthOptions as H, spatialGain as I, AudioBuses as J, MusicTrack as K, spatialPan as L, ROLLOFF_MODELS as M, RolloffModel as N, isAudioContextAvailable as O, SpatialParams as P, SceneTree as Q, SFX_PRESETS as R, GameStats as S, InputMap as T, synthSfx as U, SfxWave as V, MusicBackend as W, Node as X, BusName as Y, NodeLifecycle as Z, registeredTypes as _, registerBehavior as a, Scheduler as b, PropDef as c, createNode as d, LogLevel as et, getNodeSchema as f, registerNode as g, mergeStaticSignals as h, getBehavior as i, Listener as j, Voice as k, PropSchema as l, getNodeType as m, BehaviorCtor as n, Signal as nt, registeredBehaviors as o, getNodeSignals as p, PlayMusicOptions as q, clearBehaviors as r, SignalListener as rt, NodeCtor as s, Behavior as t, LogManager as tt, clearRegistry as u, Engine as v, Scene as w, EngineStats as x, EngineOptions as y, SFX_PRESET_NAMES as z };
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
-
import {
|
|
3
|
-
import { l as AudioPlayer, u as Engine } from "./register-
|
|
2
|
+
import { m as diagnose, n as loadScene, o as computeViewport, s as resolveViewport, y as registerBehavior } from "./loader-BlaRQGaA.js";
|
|
3
|
+
import { l as AudioPlayer, u as Engine } from "./register-Dzkd6-os.js";
|
|
4
4
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
5
|
-
import { i as resolveRendering, n as attachTouchControls } from "./touch-
|
|
5
|
+
import { i as resolveRendering, n as attachTouchControls } from "./touch-BoNg_MnF.js";
|
|
6
6
|
import { i as openBundledEditor, n as crossFade, r as devServerLibrary, t as teardown } from "./teardown-ByzfDPyu.js";
|
|
7
|
-
import { n as registerGameplayBehaviors } from "./gameplay-
|
|
8
|
-
import { g as PhysicsBody2D, n as UILayer, t as registerNodes2D, u as Camera2D, y as Node2D } from "./register-
|
|
7
|
+
import { n as registerGameplayBehaviors } from "./gameplay-BftxM_It.js";
|
|
8
|
+
import { g as PhysicsBody2D, n as UILayer, t as registerNodes2D, u as Camera2D, y as Node2D } from "./register-tkR_8tWg.js";
|
|
9
9
|
import { t as debugSources } from "./debug-draw-BM3DsvtT.js";
|
|
10
|
-
import { n as enablePhysics2D } from "./physics-2d-
|
|
10
|
+
import { n as enablePhysics2D } from "./physics-2d-D9wquBvK.js";
|
|
11
11
|
import { Box3, BufferAttribute, BufferGeometry, Color, LineBasicMaterial, LineSegments, LinearFilter, NearestFilter, OrthographicCamera, Raycaster, SRGBColorSpace, Scene, TextureLoader, Vector2, Vector3, WebGLRenderer } from "three";
|
|
12
12
|
//#region src/2d/assets.ts
|
|
13
13
|
/**
|
|
@@ -17,9 +17,16 @@ import { Box3, BufferAttribute, BufferGeometry, Color, LineBasicMaterial, LineSe
|
|
|
17
17
|
* `"$key"`. The texture loader is injectable so tests run headless.
|
|
18
18
|
*/
|
|
19
19
|
var AssetStore2D = class {
|
|
20
|
+
engine;
|
|
20
21
|
entries = /* @__PURE__ */ new Map();
|
|
21
22
|
loader;
|
|
22
|
-
|
|
23
|
+
/**
|
|
24
|
+
* `engine` is optional so a bare store still constructs in tests. Given one,
|
|
25
|
+
* a texture that fails to load lands in `engine.log` — where the overlay and
|
|
26
|
+
* `runScript()` can see it — instead of only in the browser console.
|
|
27
|
+
*/
|
|
28
|
+
constructor(loader, engine = null) {
|
|
29
|
+
this.engine = engine;
|
|
23
30
|
this.loader = loader ?? ((url, callbacks) => new TextureLoader().load(url, () => callbacks?.onLoad?.(), void 0, (err) => callbacks?.onError?.(err)));
|
|
24
31
|
}
|
|
25
32
|
/** Load (or extend with) a scene's `assets` declarations. Hard-validates each entry. */
|
|
@@ -43,7 +50,7 @@ var AssetStore2D = class {
|
|
|
43
50
|
},
|
|
44
51
|
onError: (err) => {
|
|
45
52
|
state.status = "error";
|
|
46
|
-
|
|
53
|
+
diagnose(this.engine, "error", `[incanto] failed to load texture '${key}' (${decl.url}):`, err);
|
|
47
54
|
}
|
|
48
55
|
});
|
|
49
56
|
if (previous && previous.texture !== texture) previous.texture.dispose();
|
|
@@ -251,6 +258,7 @@ var Renderer2D = class {
|
|
|
251
258
|
worldCam = new OrthographicCamera(-1, 1, 1, -1, -1e3, 1e3);
|
|
252
259
|
uiCam = new OrthographicCamera(-1, 1, 1, -1, -1e3, 1e3);
|
|
253
260
|
engine;
|
|
261
|
+
/** The texture store. Public so `renderer.assets.status(key)` is reachable. */
|
|
254
262
|
assets;
|
|
255
263
|
loadedAssetScenes = /* @__PURE__ */ new WeakSet();
|
|
256
264
|
disconnect;
|
|
@@ -258,10 +266,11 @@ var Renderer2D = class {
|
|
|
258
266
|
constructor(opts) {
|
|
259
267
|
this.canvas = opts.canvas;
|
|
260
268
|
this.engine = opts.engine;
|
|
261
|
-
this.assets = opts.assets ?? new AssetStore2D();
|
|
269
|
+
this.assets = opts.assets ?? new AssetStore2D(void 0, this.engine);
|
|
262
270
|
const rendering = resolveRendering(opts.engine.scene?.environment, {
|
|
263
271
|
antialias: true,
|
|
264
|
-
pixelRatio: 1
|
|
272
|
+
pixelRatio: 1,
|
|
273
|
+
preserveDrawingBuffer: false
|
|
265
274
|
}, globalThis.devicePixelRatio ?? 1, opts);
|
|
266
275
|
this.webgl = new WebGLRenderer({
|
|
267
276
|
canvas: opts.canvas,
|