incanto 0.51.0 → 0.53.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/dist/2d.d.ts +86 -3
- package/dist/2d.js +3 -3
- package/dist/3d.d.ts +9 -3
- package/dist/3d.js +4 -4
- package/dist/{behavior-rZNfzVbH.d.ts → behavior-Do0Da56m.d.ts} +108 -0
- package/dist/{create-game-CkzKZ4v5.js → create-game-9D87XaiX.js} +112 -15
- package/dist/{create-game-BNaOC3Px.js → create-game-CDJ1lVqK.js} +21 -13
- package/dist/debug.d.ts +1 -1
- package/dist/debug.js +1 -1
- package/dist/{duplicate-EybyYeyL.js → duplicate-MNLMAcbz.js} +1 -1
- package/dist/editor.js +1931 -1579
- package/dist/{environment-presets-C08pOC6H.js → environment-presets-SkGanr2s.js} +3 -3
- package/dist/{gameplay-bStgtZBV.js → gameplay-CRYw_Q-T.js} +6 -4
- package/dist/gameplay.d.ts +1 -1
- package/dist/gameplay.js +1 -1
- package/dist/index.d.ts +237 -5
- package/dist/index.js +9 -9
- package/dist/{loader-B2asghWa.js → loader-BTkHYrQn.js} +86 -50
- package/dist/{loader-Dkbn56KC.d.ts → loader-Cu_7kJDy.d.ts} +1 -1
- package/dist/{log-report-lxrQY9cH.js → log-report-CPFm4OXf.js} +0 -0
- package/dist/net.d.ts +15 -410
- package/dist/net.js +1 -822
- package/dist/{pathfinding-Bz34pvQD.d.ts → pathfinding-HNGFqGUZ.d.ts} +24 -1
- package/dist/{physics-2d-Ccq2L9R0.js → physics-2d-BhZZ-HAp.js} +2 -2
- package/dist/{physics-3d-B2c5KNYh.js → physics-3d-DmyO2oaN.js} +3 -3
- package/dist/react.d.ts +1 -1
- package/dist/react.js +1 -1
- package/dist/{register-CGq-Hee8.js → register-BTomIiYG.js} +305 -25
- package/dist/{register-B_BaaGx-.js → register-DROK2l7J.js} +3 -3
- package/dist/{registry-IyWCGe4q.js → registry-C7u42TID.js} +23 -1
- package/dist/{replay-DIP2_as4.d.ts → replay-9Fy6C10F.d.ts} +1 -1
- package/dist/{replay-BgKxGcXH.js → replay-DYNUL4BU.js} +195 -2
- package/dist/split-screen-CaF9hO7g.js +1267 -0
- package/dist/split-screen-DNmcX1Pz.d.ts +441 -0
- package/dist/{src-C3UwzYXl.js → src-C9xyZW7M.js} +1 -1
- package/dist/{teardown-BKTCzLek.js → teardown-Bw2aeyGI.js} +32 -2
- package/dist/{test-D2gRpk5V.js → test-CJRsciFk.js} +119 -22
- package/dist/test.d.ts +60 -5
- package/dist/test.js +3 -3
- package/dist/{touch-BoNg_MnF.js → touch-BnMyy9tr.js} +4 -4
- package/dist/vite.js +2 -2
- package/editor/assets/{agent8-DbX_msaO.js → agent8-PlFHzJsh.js} +1 -1
- package/editor/assets/{debug-C-kMxdl1.js → debug-CebV7CDW.js} +1 -1
- package/editor/assets/index-CdbsY31G.js +10958 -0
- package/editor/index.html +1 -1
- package/package.json +1 -1
- package/schemas/scene.schema.json +213 -0
- package/skills/incanto-assets.md +4 -1
- package/skills/incanto-audio.md +97 -10
- package/skills/incanto-building-2d-games.md +20 -3
- package/skills/incanto-editor.md +48 -11
- package/skills/incanto-gameplay-behaviors.md +14 -5
- package/skills/incanto-hud.md +7 -0
- package/skills/incanto-multiplayer.md +39 -3
- package/skills/incanto-node-reference.md +34 -0
- package/skills/incanto-physics-and-input.md +38 -0
- package/skills/incanto-verifying-your-game.md +75 -1
- package/skills/incanto-web-integration.md +1 -0
- package/templates/agent8-server.ts +79 -2
- package/templates-app/beacon-isle-3d/index.html +3 -3
- package/templates-app/beacon-isle-3d/package.json +1 -1
- package/templates-app/tps-3d/index.html +3 -3
- package/templates-app/tps-3d/package.json +1 -1
- package/templates-app/village-quest-3d/index.html +3 -3
- package/templates-app/village-quest-3d/package.json +1 -1
- package/dist/register-CLVhzWcI.js +0 -374
- package/editor/assets/index-CUc1U7wm.js +0 -10951
|
@@ -1,4 +1,196 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { E as behaviorSchema, w as parseNodePath } from "./loader-BTkHYrQn.js";
|
|
2
|
+
import { N as translationKey } from "./register-BTomIiYG.js";
|
|
3
|
+
import { i as getNodeSchema } from "./registry-C7u42TID.js";
|
|
4
|
+
//#region src/core/node-refs.ts
|
|
5
|
+
/**
|
|
6
|
+
* Node-path PROPS, resolved against scene JSON.
|
|
7
|
+
*
|
|
8
|
+
* `Chase.target`, `Camera2D.follow`, `Spawner.prefab`, `Joint3D.target`,
|
|
9
|
+
* `CharacterController3D.skinPath`, `Foliage3D.terrain` … each holds a path to
|
|
10
|
+
* another node, and nothing checked any of them. A connection whose end does not
|
|
11
|
+
* resolve is a hard `DANGLING_CONNECTION` at load; a PROP that does not resolve
|
|
12
|
+
* is nothing at all — `getNodeOrNull` returns null, the scene loads, the enemy
|
|
13
|
+
* never chases, and no error is ever printed.
|
|
14
|
+
*
|
|
15
|
+
* That makes it exactly the class `auditScene` exists for: loads fine, plays
|
|
16
|
+
* wrong. The editor uses the same resolver so the two cannot drift.
|
|
17
|
+
*
|
|
18
|
+
* The grammar comes from `parseNodePath` — the loader's own parser — so a path
|
|
19
|
+
* this module accepts is one the engine accepts.
|
|
20
|
+
*/
|
|
21
|
+
function nodeAt(root, path) {
|
|
22
|
+
let node = root;
|
|
23
|
+
for (const index of path) {
|
|
24
|
+
node = node?.children?.[index];
|
|
25
|
+
if (!node) return null;
|
|
26
|
+
}
|
|
27
|
+
return node ?? null;
|
|
28
|
+
}
|
|
29
|
+
function byName(root, name) {
|
|
30
|
+
const found = [];
|
|
31
|
+
const walk = (node, at) => {
|
|
32
|
+
if (node.name === name) found.push(at);
|
|
33
|
+
(node.children ?? []).forEach((child, i) => {
|
|
34
|
+
walk(child, [...at, i]);
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
walk(root, []);
|
|
38
|
+
return found;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Resolve `value` from the node at `owner`, the way the engine would.
|
|
42
|
+
*
|
|
43
|
+
* Note the asymmetry with connections, whose paths resolve from the SCENE ROOT:
|
|
44
|
+
* a prop's path resolves from the node holding it, which is why `'../Skin'` is
|
|
45
|
+
* the documented default of `CharacterController3D.skinPath` and would mean
|
|
46
|
+
* nothing in a connection.
|
|
47
|
+
*/
|
|
48
|
+
function resolveRefInJson(root, owner, value) {
|
|
49
|
+
if (!root) return {
|
|
50
|
+
ok: false,
|
|
51
|
+
problem: {
|
|
52
|
+
kind: "noMatch",
|
|
53
|
+
name: value
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
let parsed;
|
|
57
|
+
try {
|
|
58
|
+
parsed = parseNodePath(value);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
return {
|
|
61
|
+
ok: false,
|
|
62
|
+
problem: {
|
|
63
|
+
kind: "grammar",
|
|
64
|
+
message: error instanceof Error ? error.message : String(error)
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
if (parsed.kind === "unique") {
|
|
69
|
+
const found = byName(root, parsed.name);
|
|
70
|
+
if (found.length === 1) return {
|
|
71
|
+
ok: true,
|
|
72
|
+
path: found[0]
|
|
73
|
+
};
|
|
74
|
+
if (found.length === 0) return {
|
|
75
|
+
ok: false,
|
|
76
|
+
problem: {
|
|
77
|
+
kind: "noMatch",
|
|
78
|
+
name: parsed.name
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
return {
|
|
82
|
+
ok: false,
|
|
83
|
+
problem: {
|
|
84
|
+
kind: "ambiguous",
|
|
85
|
+
name: parsed.name,
|
|
86
|
+
count: found.length
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
let at;
|
|
91
|
+
let segments = parsed.segments;
|
|
92
|
+
if (parsed.kind === "absolute") {
|
|
93
|
+
const first = segments[0] ?? "";
|
|
94
|
+
if (first !== root.name && first !== "root") return {
|
|
95
|
+
ok: false,
|
|
96
|
+
problem: {
|
|
97
|
+
kind: "wrongRoot",
|
|
98
|
+
first,
|
|
99
|
+
root: String(root.name ?? "")
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
at = [];
|
|
103
|
+
segments = segments.slice(1);
|
|
104
|
+
} else at = [...owner];
|
|
105
|
+
for (const segment of segments) {
|
|
106
|
+
if (segment === "..") {
|
|
107
|
+
if (at.length === 0) return {
|
|
108
|
+
ok: false,
|
|
109
|
+
problem: { kind: "aboveRoot" }
|
|
110
|
+
};
|
|
111
|
+
at.pop();
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
const here = nodeAt(root, at);
|
|
115
|
+
const index = (here?.children ?? []).findIndex((child) => child.name === segment);
|
|
116
|
+
if (index < 0) return {
|
|
117
|
+
ok: false,
|
|
118
|
+
problem: {
|
|
119
|
+
kind: "noChild",
|
|
120
|
+
parent: String(here?.name ?? "?"),
|
|
121
|
+
segment
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
at.push(index);
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
ok: true,
|
|
128
|
+
path: at
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/** The problem in English, for `incanto-check` and as the editor's base text. */
|
|
132
|
+
function describeRefProblem(problem, value) {
|
|
133
|
+
switch (problem.kind) {
|
|
134
|
+
case "grammar": return problem.message;
|
|
135
|
+
case "noMatch": return `'%${problem.name}' matches no node in this scene.`;
|
|
136
|
+
case "ambiguous": return `'%${problem.name}' matches ${problem.count} nodes — a %name must be unique.`;
|
|
137
|
+
case "wrongRoot": return `'${value}' starts at '${problem.first}', but the scene root is '${problem.root}' (or write '/root/…').`;
|
|
138
|
+
case "noChild": return `'${value}' leads nowhere — '${problem.parent}' has no child named '${problem.segment}'.`;
|
|
139
|
+
case "aboveRoot": return `'${value}' walks above the scene root.`;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/** Every prop on this node declared `nodePath: true`, per bag. */
|
|
143
|
+
function refProps(node) {
|
|
144
|
+
const collect = (schema) => Object.entries(schema).filter(([, def]) => def?.nodePath).map(([key]) => key);
|
|
145
|
+
let props = [];
|
|
146
|
+
if (node.type) try {
|
|
147
|
+
props = collect(getNodeSchema(node.type));
|
|
148
|
+
} catch {}
|
|
149
|
+
const name = node.script?.name;
|
|
150
|
+
return {
|
|
151
|
+
props,
|
|
152
|
+
script: name ? collect(behaviorSchema(name)) : []
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Scene lints for node-path props that point at nothing.
|
|
157
|
+
*
|
|
158
|
+
* An EMPTY value is not reported: `''` is the default of most of these props and
|
|
159
|
+
* means "not set", which is a choice rather than a typo.
|
|
160
|
+
*/
|
|
161
|
+
function nodeRefWarnings(scene) {
|
|
162
|
+
const root = scene.root;
|
|
163
|
+
if (!root) return [];
|
|
164
|
+
const warnings = [];
|
|
165
|
+
const walk = (node, at, path) => {
|
|
166
|
+
const keys = refProps(node);
|
|
167
|
+
const bags = [[
|
|
168
|
+
node.props,
|
|
169
|
+
keys.props,
|
|
170
|
+
""
|
|
171
|
+
], [
|
|
172
|
+
node.script?.props,
|
|
173
|
+
keys.script,
|
|
174
|
+
`${node.script?.name ?? "script"}.`
|
|
175
|
+
]];
|
|
176
|
+
for (const [bag, propKeys, prefix] of bags) {
|
|
177
|
+
if (!bag) continue;
|
|
178
|
+
for (const key of propKeys) {
|
|
179
|
+
const value = bag[key];
|
|
180
|
+
if (typeof value !== "string" || value === "") continue;
|
|
181
|
+
const result = resolveRefInJson(root, at, value);
|
|
182
|
+
if (result.ok) continue;
|
|
183
|
+
warnings.push(`${path}: ${prefix}${key} — ${describeRefProblem(result.problem, value)} The scene loads and the prop does nothing.`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
(node.children ?? []).forEach((child, i) => {
|
|
187
|
+
walk(child, [...at, i], `${path}/${child.name ?? "?"}`);
|
|
188
|
+
});
|
|
189
|
+
};
|
|
190
|
+
walk(root, [], String(root.name ?? "root"));
|
|
191
|
+
return warnings;
|
|
192
|
+
}
|
|
193
|
+
//#endregion
|
|
2
194
|
//#region src/core/audit.ts
|
|
3
195
|
const ANIMATED_TYPES = new Set([
|
|
4
196
|
"Particles2D",
|
|
@@ -88,6 +280,7 @@ function auditScene(scene) {
|
|
|
88
280
|
if (cameras > 0 && currentCameras === 0) warnings.push("no camera has \"current\": true — the screen renders from a default view, probably not what you framed.");
|
|
89
281
|
if (currentCameras > 1) warnings.push(`${currentCameras} cameras claim "current": true — only the first found wins.`);
|
|
90
282
|
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\": \"...\" }.");
|
|
283
|
+
for (const warning of nodeRefWarnings(scene)) warnings.push(warning);
|
|
91
284
|
for (const warning of auditStrings(scene)) warnings.push(warning);
|
|
92
285
|
return warnings;
|
|
93
286
|
}
|
|
@@ -217,4 +410,4 @@ function replay(engine, recording, opts) {
|
|
|
217
410
|
}
|
|
218
411
|
}
|
|
219
412
|
//#endregion
|
|
220
|
-
export { startRecording as n, auditScene as r, replay as t };
|
|
413
|
+
export { nodeRefWarnings as a, describeRefProblem as i, startRecording as n, resolveRefInJson as o, auditScene as r, replay as t };
|