incanto 0.70.0 → 0.71.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/bin/incanto-verify.mjs +36 -98
  2. package/dist/2d.d.ts +18 -3
  3. package/dist/2d.js +3 -3
  4. package/dist/3d.d.ts +3 -3
  5. package/dist/3d.js +6 -6
  6. package/dist/{agent8-CmNF01gA.js → agent8-Cfmd3ar_.js} +1 -1
  7. package/dist/{audio-player-DaMxqfNE.d.ts → audio-player-L8yccdMP.d.ts} +1 -1
  8. package/dist/{behavior-DZExDn9o.d.ts → behavior-Dcz0fr1S.d.ts} +11 -0
  9. package/dist/{create-game-Bwvh6q8A.js → create-game-CH4kt78v.js} +7 -7
  10. package/dist/{create-game-C7ffQWW7.js → create-game-LCHBLfA4.js} +7 -7
  11. package/dist/debug.d.ts +1 -1
  12. package/dist/editor.js +29 -25
  13. package/dist/{environment-presets-D6Q5BxeE.js → environment-presets-99BzP_L-.js} +3 -3
  14. package/dist/{frame-report-Dlq13Gyj.js → frame-report-D-_7YF2G.js} +1 -1
  15. package/dist/{gameplay-BfHkuzVb.js → gameplay-CNULJvwh.js} +108 -6
  16. package/dist/gameplay.d.ts +68 -4
  17. package/dist/gameplay.js +2 -2
  18. package/dist/index.d.ts +27 -4
  19. package/dist/index.js +4 -4
  20. package/dist/{loader-Cff09LMm.d.ts → loader-8-IHvbKD.d.ts} +1 -1
  21. package/dist/net.d.ts +2 -2
  22. package/dist/net.js +2 -2
  23. package/dist/{physics-2d-CE0Qvy3V.js → physics-2d-FhrXUc6B.js} +2 -2
  24. package/dist/{physics-3d-CpH-2gn5.js → physics-3d-CFEGxBXZ.js} +3 -3
  25. package/dist/{picking-CQJ_PJKh.js → picking-DVo7fI13.js} +3 -3
  26. package/dist/react.d.ts +1 -1
  27. package/dist/react.js +1 -1
  28. package/dist/{register-Bkk0wSDB.js → register-BYQCBySi.js} +100 -8
  29. package/dist/{register-6DYnKZcy.js → register-DuVdzxbq.js} +49 -15
  30. package/dist/{replay-DjAkAzMq.js → replay-DEvp3kyV.js} +2 -2
  31. package/dist/{replay-Dmw-PKQu.d.ts → replay-F7IZHdFR.d.ts} +1 -1
  32. package/dist/{save-slots-BXVg148r.js → save-slots-Bvuh2p_r.js} +56 -3
  33. package/dist/{split-screen--k-XpBjr.d.ts → split-screen-CIYf1zSf.d.ts} +2 -2
  34. package/dist/{split-screen-PL78oVXP.js → split-screen-eULetcg-.js} +3 -3
  35. package/dist/{src-Cxfiv1Hg.js → src-_jk0qLsS.js} +1 -1
  36. package/dist/{test-iHYVUcDK.js → test-CmN54kWv.js} +252 -24
  37. package/dist/test.d.ts +113 -5
  38. package/dist/test.js +3 -3
  39. package/dist/vite.js +3 -3
  40. package/editor/assets/{agent8-D0MS174y.js → agent8-yLIEYHbd.js} +1 -1
  41. package/editor/assets/{debug-BnXkKuYu.js → debug-u31w_yhq.js} +1 -1
  42. package/editor/assets/{index-CIu3uc3l.js → index-DK9xMGpW.js} +94 -94
  43. package/editor/index.html +1 -1
  44. package/package.json +1 -1
  45. package/schemas/scene.schema.json +4 -0
  46. package/skills/incanto-behaviors-and-scripts.md +30 -9
  47. package/skills/incanto-building-2d-games.md +20 -2
  48. package/skills/incanto-gameplay-behaviors.md +63 -1
  49. package/skills/incanto-hud.md +14 -0
  50. package/skills/incanto-node-reference.md +18 -2
  51. package/skills/incanto-playtesting.md +34 -0
  52. package/skills/incanto-save-slots.md +56 -1
  53. package/skills/incanto-verifying-your-game.md +11 -0
  54. package/templates-app/beacon-isle-3d/package.json +1 -1
  55. package/templates-app/beacon-isle-3d/src/game.scene.json +44 -226
  56. package/templates-app/molehill-2d/package.json +1 -1
  57. package/templates-app/molehill-2d/src/game.scene.json +10 -60
  58. package/templates-app/platformer-2d/package.json +1 -1
  59. package/templates-app/platformer-2d/src/game.scene.json +80 -416
  60. package/templates-app/star-survivor/package.json +1 -1
  61. package/templates-app/star-survivor/src/game.scene.json +5 -24
  62. package/templates-app/tps-3d/package.json +1 -1
  63. package/templates-app/tps-3d/src/game.scene.json +13 -71
  64. package/templates-app/village-quest-3d/package.json +1 -1
  65. package/templates-app/village-quest-3d/src/behaviors.ts +21 -4
  66. package/templates-app/village-quest-3d/src/grove.scene.json +16 -87
  67. package/templates-app/village-quest-3d/src/village.scene.json +5 -27
@@ -1,5 +1,5 @@
1
1
  import { o as __exportAll } from "./json-CfTjpvW8.js";
2
- import { B as TAPPABLE, G as requireText, H as UiBar, K as Node, L as FITS, N as resolveOrderGroups, P as InputMap, R as HudLayer, U as UiText, V as UiBanner, a as restoreBehaviors, i as captureBehaviors, j as ORDER_GROUP_BASE, k as createSaveStore, ot as Signal, q as diagnose, t as ENGINE_STATE_KEY, z as HudWidgetBase } from "./save-slots-BXVg148r.js";
2
+ import { B as HudLayer, F as resolveOrderGroups, G as UiText, H as TAPPABLE, I as InputMap, J as Node, N as ORDER_GROUP_BASE, U as UiBanner, V as HudWidgetBase, W as UiBar, Y as diagnose, a as restoreBehaviors, ct as Signal, i as captureBehaviors, j as createSaveStore, q as requireText, t as ENGINE_STATE_KEY, z as FITS } from "./save-slots-Bvuh2p_r.js";
3
3
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
4
4
  import { t as Rng } from "./rng-CDOMybym.js";
5
5
  import { p as registerNode } from "./touch-BnCyPA0G.js";
@@ -2470,7 +2470,11 @@ var Engine = class {
2470
2470
  this._scene.root.free();
2471
2471
  this._scene.tree._setEngine(null);
2472
2472
  this._scene = null;
2473
- this.sceneChanged.emit(null);
2473
+ try {
2474
+ this.sceneChanged.emit(null);
2475
+ } catch (error) {
2476
+ diagnose(this, "warn", "[incanto] teardown: a `sceneChanged` listener threw (continuing) — it fires with NULL here, so a handler that reads the scene has to check for it", error);
2477
+ }
2474
2478
  }
2475
2479
  this.input.dispose();
2476
2480
  this.input.clear();
@@ -3612,7 +3616,20 @@ var UiSlider = class extends HudWidgetBase {
3612
3616
  /** Left/right nudge by one step — the only sane thing a d-pad can mean here. */
3613
3617
  _activate(dir) {
3614
3618
  if (dir === 0) return;
3615
- const next = Math.min(this.max, Math.max(this.min, this.value + dir * this.step));
3619
+ this.choose(this.value + dir * this.step);
3620
+ }
3621
+ /**
3622
+ * Move it as a PERSON would: set the value and say so.
3623
+ *
3624
+ * `UiButton` has `press()` and the value widgets had nothing, because
3625
+ * assigning `.value` deliberately does not emit (restoring a saved setting
3626
+ * must not fire the handler that saved it). So a harness — or a game driving
3627
+ * its own menu: a preset button, "reset to defaults", a tutorial that moves a
3628
+ * slider for you — had one route to "the player changed this", and it was
3629
+ * `_activate`, which is internal and takes a direction rather than a value.
3630
+ */
3631
+ choose(value) {
3632
+ const next = Math.min(this.max, Math.max(this.min, value));
3616
3633
  if (next === this.value) return;
3617
3634
  this.value = next;
3618
3635
  this.last = next;
@@ -3670,12 +3687,15 @@ var UiToggle = class extends HudWidgetBase {
3670
3687
  focusable = true;
3671
3688
  /** Confirm flips it; left/right set it explicitly, which reads better on a pad. */
3672
3689
  _activate(dir) {
3673
- const next = dir === 0 ? !this.value : dir > 0;
3674
- if (next === this.value) return;
3675
- this.value = next;
3676
- this.last = next;
3677
- if (this.input) this.input.checked = next;
3678
- this.emit("changed", next);
3690
+ this.choose(dir === 0 ? !this.value : dir > 0);
3691
+ }
3692
+ /** Flip it as a PERSON would: set the value and say so. See `UiSlider.choose`. */
3693
+ choose(value) {
3694
+ if (value === this.value) return;
3695
+ this.value = value;
3696
+ this.last = value;
3697
+ if (this.input) this.input.checked = value;
3698
+ this.emit("changed", value);
3679
3699
  }
3680
3700
  input = null;
3681
3701
  last = null;
@@ -3754,14 +3774,28 @@ var UiSelect = class extends HudWidgetBase {
3754
3774
  }
3755
3775
  /** Left/right (and confirm) walk the list — a select has no other gesture. */
3756
3776
  _activate(dir) {
3757
- const choices = this.options.split(",").map((o) => o.trim()).filter(Boolean);
3777
+ const choices = this.choices();
3758
3778
  if (choices.length === 0) return;
3759
3779
  const next = choices[(Math.max(0, choices.indexOf(this.value)) + (dir === 0 ? 1 : dir) + choices.length) % choices.length];
3760
- if (!next || next === this.value) return;
3761
- this.value = next;
3762
- this.last = next;
3763
- if (this.select) this.select.value = next;
3764
- this.emit("changed", next);
3780
+ if (next) this.choose(next);
3781
+ }
3782
+ /**
3783
+ * Pick an option as a PERSON would: set the value and say so. See
3784
+ * `UiSlider.choose`.
3785
+ *
3786
+ * An option the list does not offer is refused, for the reason
3787
+ * `validateJson` refuses it in the file: the browser would show the first one
3788
+ * while the node kept answering with what it was given, and nothing would
3789
+ * ever reconcile them.
3790
+ */
3791
+ choose(value) {
3792
+ const choices = this.choices();
3793
+ if (choices.length > 0 && !choices.includes(value)) throw new IncantoError("PROP_TYPE_MISMATCH", `UiSelect '${this.name}': choose('${value}') is not one of its options [${choices.join(", ")}].`, { prop: "value" });
3794
+ if (value === this.value) return;
3795
+ this.value = value;
3796
+ this.last = value;
3797
+ if (this.select) this.select.value = value;
3798
+ this.emit("changed", value);
3765
3799
  }
3766
3800
  select = null;
3767
3801
  labelEl = null;
@@ -1,5 +1,5 @@
1
- import { F as handlerShadowWarnings, I as sceneDimension, Y as nodeRefWarnings, z as HudWidgetBase } from "./save-slots-BXVg148r.js";
2
- import { N as translationKey } from "./register-6DYnKZcy.js";
1
+ import { L as handlerShadowWarnings, R as sceneDimension, V as HudWidgetBase, Z as nodeRefWarnings } from "./save-slots-Bvuh2p_r.js";
2
+ import { N as translationKey } from "./register-DuVdzxbq.js";
3
3
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
4
4
  import { u as getNodeType } from "./touch-BnCyPA0G.js";
5
5
  //#region src/core/audit.ts
@@ -1,4 +1,4 @@
1
- import { b as Engine } from "./behavior-DZExDn9o.js";
1
+ import { b as Engine } from "./behavior-Dcz0fr1S.js";
2
2
  import { s as JsonObject } from "./rng-Bb-IutXB.js";
3
3
 
4
4
  //#region src/core/audit.d.ts
@@ -113,7 +113,10 @@ var Behavior = class {
113
113
  */
114
114
  get engine() {
115
115
  const engine = this.node?.tree?.engine;
116
- if (!engine) throw new IncantoError("TREE_VIOLATION", `Behavior on '${this.node?.name ?? "?"}' is not attached to a running engine. If this came from \`onReady\` during a load, the scene does not know its engine yet: pass it — \`loadScene(json, { engine })\` — which is what createGame2D/3D and runScript do. Otherwise the node is not in a scene set on an Engine.`);
116
+ if (!engine) {
117
+ if (this.node?.freed) throw new IncantoError("TREE_VIOLATION", `Behavior on '${this.node.name}' has been FREED — a scene swap frees the tree it replaces, so \`this.engine\` (which reads \`this.node.tree\`) is gone with it. Hold it BEFORE the swap: \`const engine = this.engine; goToScene(engine, next);\` and use that afterwards.`);
118
+ throw new IncantoError("TREE_VIOLATION", `Behavior on '${this.node?.name ?? "?"}' is not attached to a running engine. If this came from \`onReady\` during a load, the scene does not know its engine yet: pass it — \`loadScene(json, { engine })\` — which is what createGame2D/3D and runScript do. Otherwise the node is not in a scene set on an Engine.`);
119
+ }
117
120
  return engine;
118
121
  }
119
122
  /** Shortcut for `this.engine.input`. */
@@ -669,7 +672,11 @@ var Node = class {
669
672
  */
670
673
  wiringPath() {
671
674
  const parts = [];
672
- for (let n = this; n; n = n._parent) parts.unshift(n._cloneOf ?? n._name);
675
+ for (let n = this; n; n = n._parent) {
676
+ const authored = n._authoredPath;
677
+ if (authored !== void 0) return parts.length === 0 ? authored : `${authored}/${parts.join("/")}`;
678
+ parts.unshift(n._cloneOf ?? n._name);
679
+ }
673
680
  return `/${parts.join("/")}`;
674
681
  }
675
682
  getNode(path) {
@@ -874,9 +881,23 @@ var Node = class {
874
881
  if (this._tree) this._tree._queueFree(this);
875
882
  else this.free();
876
883
  }
884
+ /**
885
+ * Has this node been torn down?
886
+ *
887
+ * A scene swap frees the tree it replaces, so the answer is `true` for every
888
+ * node of the scene you just left — which is exactly the state a behavior is
889
+ * in when it runs one more line after `goToScene`. Kept as a flag rather than
890
+ * inferred from `parent`/`tree`, because a DETACHED node (a prefab on a
891
+ * shelf, a clone waiting to be added) has neither and is perfectly alive.
892
+ */
893
+ get freed() {
894
+ return this._freed;
895
+ }
896
+ _freed = false;
877
897
  /** Immediately detach and tear down this node and its children. */
878
898
  free() {
879
899
  const tree = this._tree;
900
+ this._freed = true;
880
901
  for (const child of [...this._children]) child.free();
881
902
  this._parent?.removeChild(this);
882
903
  if (this._tree) this._propagateExitTree();
@@ -5145,6 +5166,7 @@ function duplicateNode(node, opts) {
5145
5166
  const clone = buildNodeJson(stripUids(serializeNode(node)), opts ?? rebuildOptions(node));
5146
5167
  markCloned(clone);
5147
5168
  markTemplateName(clone);
5169
+ markTemplateOrigin(clone, node);
5148
5170
  return clone;
5149
5171
  }
5150
5172
  /**
@@ -5196,6 +5218,37 @@ function markCloned(node) {
5196
5218
  function markTemplateName(node) {
5197
5219
  node._cloneOf = node.name;
5198
5220
  }
5221
+ /**
5222
+ * @internal Where the template this clone came from was AUTHORED.
5223
+ *
5224
+ * `_cloneOf` (the name) is enough only while clones land where the template
5225
+ * was authored — the `Spawner` case (`/Waves/Grunt` template, `/Waves/Grunt2`
5226
+ * clone) and no other. A game that clones from a SHELF authors the template at
5227
+ * `/Game/Prefabs/Tower` and adds the clone under `/Game/Towers`, so its audio
5228
+ * was logged at `/Game/Towers/Tower/Thud`: a path no scene file contains, and
5229
+ * one no report that reads the file can ever match. Recording the address
5230
+ * itself is what lets `wiringPath` answer with the authored node wherever the
5231
+ * clone ends up — including a clone OF a clone, which carries it forward.
5232
+ *
5233
+ * Taken while the source is still in a tree; a template detached before the
5234
+ * first clone (`PrefabShelf`, `SpawnSource`) records its own on the way out.
5235
+ */
5236
+ function markTemplateOrigin(clone, source) {
5237
+ const origin = source._authoredPath ?? (source.tree ? source.getPath() : null);
5238
+ if (origin) clone._authoredPath = origin;
5239
+ }
5240
+ /**
5241
+ * @internal Remember where a node was authored, before it leaves the tree.
5242
+ *
5243
+ * A shelf or a spawner lifts its templates out at enter; from then on the
5244
+ * template cannot say where it came from, and neither can anything cloned from
5245
+ * it. Called on the way out, while the answer is still there.
5246
+ */
5247
+ function rememberAuthoredPath(node) {
5248
+ const at = node;
5249
+ if (at._authoredPath !== void 0) return;
5250
+ if (node.tree || node.parent?.tree) at._authoredPath = node.getPath();
5251
+ }
5199
5252
  /** @internal Was this node made at runtime rather than loaded from a file? */
5200
5253
  function isCloned(node) {
5201
5254
  return node._cloned === true;
@@ -5648,4 +5701,4 @@ function savesWithoutUid(root) {
5648
5701
  return out;
5649
5702
  }
5650
5703
  //#endregion
5651
- export { StubBehavior as $, resetMemorySaves as A, TAPPABLE as B, isConstRef as C, newUid as D, resolveViewport as E, handlerShadowWarnings as F, requireText as G, UiBar as H, sceneDimension as I, describeRefProblem as J, Node as K, FITS$1 as L, effectiveOrder as M, resolveOrderGroups as N, SceneTree as O, InputMap as P, Behavior as Q, HudLayer as R, CONST_REF_KEY as S, computeViewport as T, UiText as U, UiBanner as V, requireNumber as W, resolveRefInJson as X, nodeRefWarnings as Y, parseNodePath as Z, SCENE_FORMAT as _, restoreBehaviors as a, registeredBehaviors as at, sunDirectionFromElevationAzimuth as b, markCloned as c, stripUids as d, behaviorSchema as et, buildSceneSubtree as f, serializeNode as g, Scene as h, captureBehaviors as i, registerBehavior as it, ORDER_GROUP_BASE as j, createSaveStore as k, markTemplateName as l, loader_exports as m, SaveSlots as n, clearBehaviors as nt, savesWithoutUid as o, Signal as ot, loadScene as p, diagnose as q, behaviorsWithoutSave as r, getBehavior as rt, duplicateNode as s, ENGINE_STATE_KEY as t, behaviorSignals as tt, rebuildOptions as u, horizonColorFromSky as v, resolveConstants as w, sunDirectionFromSky as x, parseEnvironment3D as y, HudWidgetBase as z };
5704
+ export { parseNodePath as $, SceneTree as A, HudLayer as B, sunDirectionFromSky as C, computeViewport as D, resolveConstants as E, resolveOrderGroups as F, UiText as G, TAPPABLE as H, InputMap as I, Node as J, requireNumber as K, handlerShadowWarnings as L, resetMemorySaves as M, ORDER_GROUP_BASE as N, resolveViewport as O, effectiveOrder as P, resolveRefInJson as Q, sceneDimension as R, sunDirectionFromElevationAzimuth as S, isConstRef as T, UiBanner as U, HudWidgetBase as V, UiBar as W, describeRefProblem as X, diagnose as Y, nodeRefWarnings as Z, Scene as _, restoreBehaviors as a, getBehavior as at, horizonColorFromSky as b, isCloned as c, Signal as ct, rebuildOptions as d, Behavior as et, rememberAuthoredPath as f, loader_exports as g, loadScene as h, captureBehaviors as i, clearBehaviors as it, createSaveStore as j, newUid as k, markCloned as l, buildSceneSubtree as m, SaveSlots as n, behaviorSchema as nt, savesWithoutUid as o, registerBehavior as ot, stripUids as p, requireText as q, behaviorsWithoutSave as r, behaviorSignals as rt, duplicateNode as s, registeredBehaviors as st, ENGINE_STATE_KEY as t, StubBehavior as tt, markTemplateName as u, serializeNode as v, CONST_REF_KEY as w, parseEnvironment3D as x, SCENE_FORMAT as y, FITS$1 as z };
@@ -1,7 +1,7 @@
1
- import { Lt as Signal, b as Engine, jt as Node } from "./behavior-DZExDn9o.js";
1
+ import { Lt as Signal, b as Engine, jt as Node } from "./behavior-Dcz0fr1S.js";
2
2
  import { t as DiagnosticSink } from "./diagnostics-Cu85N3tL.js";
3
3
  import { a as SceneJson, c as JsonValue, s as JsonObject } from "./rng-Bb-IutXB.js";
4
- import { t as LoadSceneOptions } from "./loader-Cff09LMm.js";
4
+ import { t as LoadSceneOptions } from "./loader-8-IHvbKD.js";
5
5
 
6
6
  //#region src/net/types.d.ts
7
7
  type Unsubscribe = () => void;
@@ -1,6 +1,6 @@
1
1
  import { n as jsonEquals, t as jsonClone } from "./json-CfTjpvW8.js";
2
- import { K as Node, c as markCloned, d as stripUids, f as buildSceneSubtree, l as markTemplateName, ot as Signal, p as loadScene, q as diagnose, u as rebuildOptions } from "./save-slots-BXVg148r.js";
3
- import { t as registerCoreNodes, v as Engine } from "./register-6DYnKZcy.js";
2
+ import { J as Node, Y as diagnose, ct as Signal, d as rebuildOptions, h as loadScene, l as markCloned, m as buildSceneSubtree, p as stripUids, u as markTemplateName } from "./save-slots-Bvuh2p_r.js";
3
+ import { t as registerCoreNodes, v as Engine } from "./register-DuVdzxbq.js";
4
4
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
5
5
  import { p as registerNode } from "./touch-BnCyPA0G.js";
6
6
  //#region src/net/loopback.ts
@@ -1150,7 +1150,7 @@ function applySyncPatch(root, patch, setProp, onMissingNode) {
1150
1150
  }
1151
1151
  }
1152
1152
  async function createDefaultTransport(config, diagnostics) {
1153
- const { createAgent8Server } = await import("./agent8-CmNF01gA.js").then((n) => n.t);
1153
+ const { createAgent8Server } = await import("./agent8-Cfmd3ar_.js").then((n) => n.t);
1154
1154
  return createAgent8Server(config, diagnostics);
1155
1155
  }
1156
1156
  //#endregion
@@ -199,6 +199,6 @@ async function preloadSceneAssets(assets, title) {
199
199
  //#endregion
200
200
  //#region src/index.ts
201
201
  /** Engine version. Kept in sync with package.json by the release pipeline. */
202
- const VERSION = "0.70.0";
202
+ const VERSION = "0.71.0";
203
203
  //#endregion
204
204
  export { findPath as a, preloadUrls as i, assetUrls as n, gridFromRows as o, preloadSceneAssets as r, VERSION as t };