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.
Files changed (67) hide show
  1. package/dist/2d.d.ts +86 -3
  2. package/dist/2d.js +3 -3
  3. package/dist/3d.d.ts +9 -3
  4. package/dist/3d.js +4 -4
  5. package/dist/{behavior-rZNfzVbH.d.ts → behavior-Do0Da56m.d.ts} +108 -0
  6. package/dist/{create-game-CkzKZ4v5.js → create-game-9D87XaiX.js} +112 -15
  7. package/dist/{create-game-BNaOC3Px.js → create-game-CDJ1lVqK.js} +21 -13
  8. package/dist/debug.d.ts +1 -1
  9. package/dist/debug.js +1 -1
  10. package/dist/{duplicate-EybyYeyL.js → duplicate-MNLMAcbz.js} +1 -1
  11. package/dist/editor.js +1931 -1579
  12. package/dist/{environment-presets-C08pOC6H.js → environment-presets-SkGanr2s.js} +3 -3
  13. package/dist/{gameplay-bStgtZBV.js → gameplay-CRYw_Q-T.js} +6 -4
  14. package/dist/gameplay.d.ts +1 -1
  15. package/dist/gameplay.js +1 -1
  16. package/dist/index.d.ts +237 -5
  17. package/dist/index.js +9 -9
  18. package/dist/{loader-B2asghWa.js → loader-BTkHYrQn.js} +86 -50
  19. package/dist/{loader-Dkbn56KC.d.ts → loader-Cu_7kJDy.d.ts} +1 -1
  20. package/dist/{log-report-lxrQY9cH.js → log-report-CPFm4OXf.js} +0 -0
  21. package/dist/net.d.ts +15 -410
  22. package/dist/net.js +1 -822
  23. package/dist/{pathfinding-Bz34pvQD.d.ts → pathfinding-HNGFqGUZ.d.ts} +24 -1
  24. package/dist/{physics-2d-Ccq2L9R0.js → physics-2d-BhZZ-HAp.js} +2 -2
  25. package/dist/{physics-3d-B2c5KNYh.js → physics-3d-DmyO2oaN.js} +3 -3
  26. package/dist/react.d.ts +1 -1
  27. package/dist/react.js +1 -1
  28. package/dist/{register-CGq-Hee8.js → register-BTomIiYG.js} +305 -25
  29. package/dist/{register-B_BaaGx-.js → register-DROK2l7J.js} +3 -3
  30. package/dist/{registry-IyWCGe4q.js → registry-C7u42TID.js} +23 -1
  31. package/dist/{replay-DIP2_as4.d.ts → replay-9Fy6C10F.d.ts} +1 -1
  32. package/dist/{replay-BgKxGcXH.js → replay-DYNUL4BU.js} +195 -2
  33. package/dist/split-screen-CaF9hO7g.js +1267 -0
  34. package/dist/split-screen-DNmcX1Pz.d.ts +441 -0
  35. package/dist/{src-C3UwzYXl.js → src-C9xyZW7M.js} +1 -1
  36. package/dist/{teardown-BKTCzLek.js → teardown-Bw2aeyGI.js} +32 -2
  37. package/dist/{test-D2gRpk5V.js → test-CJRsciFk.js} +119 -22
  38. package/dist/test.d.ts +60 -5
  39. package/dist/test.js +3 -3
  40. package/dist/{touch-BoNg_MnF.js → touch-BnMyy9tr.js} +4 -4
  41. package/dist/vite.js +2 -2
  42. package/editor/assets/{agent8-DbX_msaO.js → agent8-PlFHzJsh.js} +1 -1
  43. package/editor/assets/{debug-C-kMxdl1.js → debug-CebV7CDW.js} +1 -1
  44. package/editor/assets/index-CdbsY31G.js +10958 -0
  45. package/editor/index.html +1 -1
  46. package/package.json +1 -1
  47. package/schemas/scene.schema.json +213 -0
  48. package/skills/incanto-assets.md +4 -1
  49. package/skills/incanto-audio.md +97 -10
  50. package/skills/incanto-building-2d-games.md +20 -3
  51. package/skills/incanto-editor.md +48 -11
  52. package/skills/incanto-gameplay-behaviors.md +14 -5
  53. package/skills/incanto-hud.md +7 -0
  54. package/skills/incanto-multiplayer.md +39 -3
  55. package/skills/incanto-node-reference.md +34 -0
  56. package/skills/incanto-physics-and-input.md +38 -0
  57. package/skills/incanto-verifying-your-game.md +75 -1
  58. package/skills/incanto-web-integration.md +1 -0
  59. package/templates/agent8-server.ts +79 -2
  60. package/templates-app/beacon-isle-3d/index.html +3 -3
  61. package/templates-app/beacon-isle-3d/package.json +1 -1
  62. package/templates-app/tps-3d/index.html +3 -3
  63. package/templates-app/tps-3d/package.json +1 -1
  64. package/templates-app/village-quest-3d/index.html +3 -3
  65. package/templates-app/village-quest-3d/package.json +1 -1
  66. package/dist/register-CLVhzWcI.js +0 -374
  67. package/editor/assets/index-CUc1U7wm.js +0 -10951
@@ -1,4 +1,196 @@
1
- import { D as translationKey } from "./register-CGq-Hee8.js";
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 };