incanto 0.41.0 → 0.43.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 (52) hide show
  1. package/bin/incanto-feel.mjs +20 -1
  2. package/bin/incanto-frame.mjs +144 -0
  3. package/dist/2d.d.ts +2 -2
  4. package/dist/2d.js +3 -3
  5. package/dist/3d.d.ts +76 -4
  6. package/dist/3d.js +5 -5
  7. package/dist/{behavior-TA8nySqb.d.ts → behavior-62q0HWBO.d.ts} +3 -1
  8. package/dist/{create-game-u7TDbvln.js → create-game-B3vBWgVD.js} +160 -6
  9. package/dist/{create-game-CZpENyin.js → create-game-DFRbG0Bj.js} +6 -5
  10. package/dist/debug.d.ts +1 -1
  11. package/dist/{environment-presets-pGTHb_V4.js → environment-presets-Ds5kXLoF.js} +2 -2
  12. package/dist/{gameplay-By8mslMc.js → gameplay-BQOeAid6.js} +50 -0
  13. package/dist/gameplay.d.ts +30 -1
  14. package/dist/gameplay.js +1 -1
  15. package/dist/index.d.ts +4 -4
  16. package/dist/index.js +3 -167
  17. package/dist/{loader-CvSDKC3v.d.ts → loader-CeyU_bm1.d.ts} +1 -1
  18. package/dist/net.d.ts +1 -1
  19. package/dist/net.js +2 -2
  20. package/dist/{pathfinding-_UlKUoox.d.ts → pathfinding-C49JSNNq.d.ts} +1 -1
  21. package/dist/{physics-2d-CEnZFcpM.js → physics-2d-BmgXBNDB.js} +1 -1
  22. package/dist/{physics-3d-CumBBvQo.js → physics-3d-CLPFv99o.js} +2 -2
  23. package/dist/react.d.ts +1 -1
  24. package/dist/react.js +1 -1
  25. package/dist/{register-DuTlY56W.js → register-BSXV8T9F.js} +3 -1
  26. package/dist/{register-DnycZ91Q.js → register-D651it1J.js} +1 -1
  27. package/dist/{register-MSZvnHkp.js → register-R2JTnIMw.js} +1 -1
  28. package/dist/{replay-DN-kG4va.d.ts → replay-CAphXMyM.d.ts} +1 -1
  29. package/dist/{replay-DcWg4LT5.js → replay-DilbZgQI.js} +1 -1
  30. package/dist/src-CwYxzZKl.js +166 -0
  31. package/dist/{teardown-CCtAMDLB.js → teardown-yePMOE1K.js} +32 -1
  32. package/dist/{test-B4kSwQzq.js → test-BMgiiD5i.js} +135 -12
  33. package/dist/test.d.ts +38 -4
  34. package/dist/test.js +3 -3
  35. package/dist/vite.d.ts +61 -1
  36. package/dist/vite.js +149 -2
  37. package/editor/assets/{agent8-DCHkff44.js → agent8-Cl3qFuBB.js} +1 -1
  38. package/editor/assets/{debug-uHHxqj0a.js → debug-u3yLCciO.js} +1 -1
  39. package/editor/assets/{index-i1mIkyMA.js → index-Dk2ZlO68.js} +60 -60
  40. package/editor/index.html +1 -1
  41. package/package.json +3 -2
  42. package/skills/incanto-3d-character.md +19 -0
  43. package/skills/incanto-audio.md +6 -2
  44. package/skills/incanto-gameplay-behaviors.md +27 -0
  45. package/skills/incanto-node-reference.md +2 -0
  46. package/skills/incanto-verifying-your-game.md +41 -0
  47. package/templates-app/beacon-isle-3d/package.json +1 -1
  48. package/templates-app/beacon-isle-3d/src/game.scene.json +47 -0
  49. package/templates-app/tps-3d/package.json +1 -1
  50. package/templates-app/tps-3d/src/game.scene.json +53 -0
  51. package/templates-app/village-quest-3d/package.json +1 -1
  52. package/templates-app/village-quest-3d/src/grove.scene.json +43 -0
@@ -4973,10 +4973,15 @@ var GameFlow = class extends Behavior {
4973
4973
  static signals = ["flowChanged"];
4974
4974
  static props = {
4975
4975
  restartAction: { default: "restart" },
4976
+ pauseAction: { default: "pause" },
4976
4977
  freezeOnEnd: { default: true },
4977
4978
  bannerPath: {
4978
4979
  default: "%Banner",
4979
4980
  nodePath: true
4981
+ },
4982
+ pausePanelPath: {
4983
+ default: "%PauseMenu",
4984
+ nodePath: true
4980
4985
  }
4981
4986
  };
4982
4987
  /** Input action that restarts from gameover/won (declare it in `input{}`). */
@@ -4985,8 +4990,31 @@ var GameFlow = class extends Behavior {
4985
4990
  freezeOnEnd = true;
4986
4991
  /** Where the flow looks for a UiBanner ('' = never). */
4987
4992
  bannerPath = "%Banner";
4993
+ /**
4994
+ * Input action that toggles pause (declare it in `input{}`; undeclared is
4995
+ * fine and means pause is API-only).
4996
+ */
4997
+ pauseAction = "pause";
4998
+ /**
4999
+ * A node shown while paused and hidden otherwise — a `UiPanel` of buttons in
5000
+ * practice. '' turns the whole thing off.
5001
+ *
5002
+ * This lives here rather than in each game because it is identical in all of
5003
+ * them, and a pause menu that costs a script is a pause menu no generated
5004
+ * game will have. `UiPanel` appeared in zero template scenes before this.
5005
+ */
5006
+ pausePanelPath = "%PauseMenu";
4988
5007
  state = "playing";
4989
5008
  frozeScale = false;
5009
+ onReady() {
5010
+ this.syncPausePanel();
5011
+ }
5012
+ /** The panel follows the state; nothing else may own its visibility. */
5013
+ syncPausePanel() {
5014
+ if (this.pausePanelPath === "") return;
5015
+ const panel = this.node.getNodeOrNull(this.pausePanelPath);
5016
+ if (panel && "visible" in panel) panel.visible = this.state === "paused";
5017
+ }
4990
5018
  gameOver(text = "GAME OVER", color = "#ef4444") {
4991
5019
  this.transition("gameover", text, color);
4992
5020
  }
@@ -4997,12 +5025,14 @@ var GameFlow = class extends Behavior {
4997
5025
  if (this.state !== "playing") return;
4998
5026
  this.state = "paused";
4999
5027
  this.freeze();
5028
+ this.syncPausePanel();
5000
5029
  this.node.emit("flowChanged", this.state);
5001
5030
  }
5002
5031
  resume() {
5003
5032
  if (this.state !== "paused") return;
5004
5033
  this.state = "playing";
5005
5034
  this.thaw();
5035
+ this.syncPausePanel();
5006
5036
  this.node.emit("flowChanged", this.state);
5007
5037
  }
5008
5038
  restart() {
@@ -5035,6 +5065,13 @@ var GameFlow = class extends Behavior {
5035
5065
  this.engine.timeScale = 1;
5036
5066
  }
5037
5067
  update() {
5068
+ if (this.state === "playing" || this.state === "paused") {
5069
+ try {
5070
+ if (this.engine.input.justPressed(this.pauseAction)) if (this.state === "playing") this.pause();
5071
+ else this.resume();
5072
+ } catch {}
5073
+ return;
5074
+ }
5038
5075
  if (this.state !== "gameover" && this.state !== "won") return;
5039
5076
  try {
5040
5077
  if (this.engine.input.justPressed(this.restartAction)) this.restart();
@@ -5166,7 +5203,20 @@ var CameraShake = class extends Behavior {
5166
5203
  magnitude = 0;
5167
5204
  t = 0;
5168
5205
  prev = [];
5206
+ /**
5207
+ * Start a shake — unless the player asked for no motion.
5208
+ *
5209
+ * `settings.reduceMotion` is an accessibility switch, and camera movement the
5210
+ * player did not cause is the vestibular trigger it exists for. Checked HERE
5211
+ * rather than in `update` so a shake requested while it is on leaves nothing
5212
+ * queued to play the moment it is turned off.
5213
+ *
5214
+ * It deliberately does NOT touch gameplay timing (`hitStop`): freezing time
5215
+ * for 80 ms is feedback, not motion, and removing it would change what the
5216
+ * game is rather than how it moves.
5217
+ */
5169
5218
  shake(magnitude, seconds) {
5219
+ if (this.engine?.settings.get("reduceMotion") === true) return;
5170
5220
  this.magnitude = Math.max(this.magnitude, magnitude);
5171
5221
  this.t = seconds ?? this.falloff;
5172
5222
  }
@@ -1,4 +1,4 @@
1
- import { Et as Node, b as Engine, d as PropSchema, n as BehaviorCtor, t as Behavior } from "./behavior-TA8nySqb.js";
1
+ import { Et as Node, b as Engine, d as PropSchema, n as BehaviorCtor, t as Behavior } from "./behavior-62q0HWBO.js";
2
2
  import { c as JsonValue, s as JsonObject } from "./schema-CFeioQRE.js";
3
3
 
4
4
  //#region src/gameplay/chase.d.ts
@@ -622,8 +622,25 @@ declare class GameFlow extends Behavior {
622
622
  freezeOnEnd: boolean;
623
623
  /** Where the flow looks for a UiBanner ('' = never). */
624
624
  bannerPath: string;
625
+ /**
626
+ * Input action that toggles pause (declare it in `input{}`; undeclared is
627
+ * fine and means pause is API-only).
628
+ */
629
+ pauseAction: string;
630
+ /**
631
+ * A node shown while paused and hidden otherwise — a `UiPanel` of buttons in
632
+ * practice. '' turns the whole thing off.
633
+ *
634
+ * This lives here rather than in each game because it is identical in all of
635
+ * them, and a pause menu that costs a script is a pause menu no generated
636
+ * game will have. `UiPanel` appeared in zero template scenes before this.
637
+ */
638
+ pausePanelPath: string;
625
639
  state: GameFlowState;
626
640
  private frozeScale;
641
+ override onReady(): void;
642
+ /** The panel follows the state; nothing else may own its visibility. */
643
+ private syncPausePanel;
627
644
  gameOver(text?: string, color?: string): void;
628
645
  win(text?: string, color?: string): void;
629
646
  pause(): void;
@@ -685,6 +702,18 @@ declare class CameraShake extends Behavior {
685
702
  private magnitude;
686
703
  private t;
687
704
  private prev;
705
+ /**
706
+ * Start a shake — unless the player asked for no motion.
707
+ *
708
+ * `settings.reduceMotion` is an accessibility switch, and camera movement the
709
+ * player did not cause is the vestibular trigger it exists for. Checked HERE
710
+ * rather than in `update` so a shake requested while it is on leaves nothing
711
+ * queued to play the moment it is turned off.
712
+ *
713
+ * It deliberately does NOT touch gameplay timing (`hitStop`): freezing time
714
+ * for 80 ms is feedback, not motion, and removing it would change what the
715
+ * game is rather than how it moves.
716
+ */
688
717
  shake(magnitude: number, seconds?: number): void;
689
718
  override update(dt: number): void;
690
719
  }
package/dist/gameplay.js CHANGED
@@ -1,2 +1,2 @@
1
- import { C as FollowCamera, D as Health, E as DamageOnContact, O as Collector, S as restartScene, T as phaseOf, _ as hitStop, a as Wander, b as GameFlow, c as Projectile, d as PathFollow, f as Oscillate, g as Cooldown, h as CameraShake, i as WaveSpawner, k as Chase, l as Pickup, m as Lifetime, n as registerGameplayBehaviors, o as Spawner, p as MoveTo, r as ZombieAI, s as ScoreKeeper, t as GAMEPLAY_BEHAVIORS, u as Patrol, v as screenFlash, w as DayNight, x as goToScene, y as Interactable } from "./gameplay-By8mslMc.js";
1
+ import { C as FollowCamera, D as Health, E as DamageOnContact, O as Collector, S as restartScene, T as phaseOf, _ as hitStop, a as Wander, b as GameFlow, c as Projectile, d as PathFollow, f as Oscillate, g as Cooldown, h as CameraShake, i as WaveSpawner, k as Chase, l as Pickup, m as Lifetime, n as registerGameplayBehaviors, o as Spawner, p as MoveTo, r as ZombieAI, s as ScoreKeeper, t as GAMEPLAY_BEHAVIORS, u as Patrol, v as screenFlash, w as DayNight, x as goToScene, y as Interactable } from "./gameplay-BQOeAid6.js";
2
2
  export { CameraShake, Chase, Collector, Cooldown, DamageOnContact, DayNight, FollowCamera, GAMEPLAY_BEHAVIORS, GameFlow, Health, Interactable, Lifetime, MoveTo, Oscillate, PathFollow, Patrol, Pickup, Projectile, ScoreKeeper, Spawner, Wander, WaveSpawner, ZombieAI, goToScene, hitStop, phaseOf, registerGameplayBehaviors, restartScene, screenFlash };
package/dist/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- import { $ as suggestLocale, A as SettingsValues, At as LogLevel, B as captureBehaviors, C as EngineStats, Ct as PlayMusicOptions, Dt as NodeLifecycle, E as DeviceHints, Et as Node, F as BehaviorState, G as OrderGroup, H as SaveStore, I as RestoreReport, J as resolveOrderGroups, K as OrderGroupTable, L as SaveSlot, M as readDeviceHints, Mt as Signal, N as suggestQuality, Nt as SignalListener, O as QualityTier, Ot as SceneTree, P as Scene, Q as T_PREFIX, R as SaveSlots, S as Scheduler, St as MusicTrack, T as RendererStats, Tt as BusName, U as createSaveStore, V as restoreBehaviors, W as ORDER_GROUP_BASE, X as LocaleTables, Y as BASE_LOCALE, Z as Localization, _ as mergeStaticSignals, _t as SfxWave, a as clearBehaviors, at as Voice, b as Engine, bt as MusicBackend, c as registeredBehaviors, ct as ROLLOFF_MODELS, d as PropSchema, dt as Vec3, et as translationKey, f as clearRegistry, ft as spatialGain, g as getNodeType, gt as SfxParams, h as getNodeSignals, ht as SFX_PRESET_NAMES, i as behaviorSignals, it as isAudioContextAvailable, j as qualityEnvironment, jt as LogManager, k as Settings, kt as LogEntry, l as NodeCtor, lt as RolloffModel, m as getNodeSchema, mt as SFX_PRESETS, n as BehaviorCtor, nt as SfxEngine, o as getBehavior, ot as VoicePreset, p as createNode, pt as spatialPan, q as effectiveOrder, r as behaviorSchema, rt as SfxPlayOptions, s as registerBehavior, st as Listener, t as Behavior, tt as InputMap, u as PropDef, ut as SpatialParams, v as registerNode, vt as SynthOptions, w as GameStats, wt as AudioBuses, x as EngineOptions, xt as MusicManager, y as registeredTypes, yt as synthSfx, z as behaviorsWithoutSave } from "./behavior-TA8nySqb.js";
1
+ import { $ as suggestLocale, A as SettingsValues, At as LogLevel, B as captureBehaviors, C as EngineStats, Ct as PlayMusicOptions, Dt as NodeLifecycle, E as DeviceHints, Et as Node, F as BehaviorState, G as OrderGroup, H as SaveStore, I as RestoreReport, J as resolveOrderGroups, K as OrderGroupTable, L as SaveSlot, M as readDeviceHints, Mt as Signal, N as suggestQuality, Nt as SignalListener, O as QualityTier, Ot as SceneTree, P as Scene, Q as T_PREFIX, R as SaveSlots, S as Scheduler, St as MusicTrack, T as RendererStats, Tt as BusName, U as createSaveStore, V as restoreBehaviors, W as ORDER_GROUP_BASE, X as LocaleTables, Y as BASE_LOCALE, Z as Localization, _ as mergeStaticSignals, _t as SfxWave, a as clearBehaviors, at as Voice, b as Engine, bt as MusicBackend, c as registeredBehaviors, ct as ROLLOFF_MODELS, d as PropSchema, dt as Vec3, et as translationKey, f as clearRegistry, ft as spatialGain, g as getNodeType, gt as SfxParams, h as getNodeSignals, ht as SFX_PRESET_NAMES, i as behaviorSignals, it as isAudioContextAvailable, j as qualityEnvironment, jt as LogManager, k as Settings, kt as LogEntry, l as NodeCtor, lt as RolloffModel, m as getNodeSchema, mt as SFX_PRESETS, n as BehaviorCtor, nt as SfxEngine, o as getBehavior, ot as VoicePreset, p as createNode, pt as spatialPan, q as effectiveOrder, r as behaviorSchema, rt as SfxPlayOptions, s as registerBehavior, st as Listener, t as Behavior, tt as InputMap, u as PropDef, ut as SpatialParams, v as registerNode, vt as SynthOptions, w as GameStats, wt as AudioBuses, x as EngineOptions, xt as MusicManager, y as registeredTypes, yt as synthSfx, z as behaviorsWithoutSave } from "./behavior-62q0HWBO.js";
2
2
  import { a as Rng, c as JsonValue, d as jsonKind, i as SceneJson, l as jsonClone, n as NodeJson, o as JsonKind, r as SCENE_FORMAT, s as JsonObject, t as ConnectionJson, u as jsonEquals } from "./schema-CFeioQRE.js";
3
- import { n as loadScene, t as LoadSceneOptions } from "./loader-CvSDKC3v.js";
3
+ import { n as loadScene, t as LoadSceneOptions } from "./loader-CeyU_bm1.js";
4
4
  import { n as ParticleSimConfig, r as ParticleView, t as ParticleSim } from "./particle-sim-BzJ1yxoE.js";
5
- import { a as AudioElementLike, i as gridFromRows, n as PathGrid, o as AudioPlayer, r as findPath, t as FindPathOptions } from "./pathfinding-_UlKUoox.js";
6
- import { a as startRecording, c as IncantoErrorDetails, i as replay, l as auditScene, n as ReplayEvent, o as IncantoError, r as ReplayJson, s as IncantoErrorCode, t as Recorder } from "./replay-DN-kG4va.js";
5
+ import { a as AudioElementLike, i as gridFromRows, n as PathGrid, o as AudioPlayer, r as findPath, t as FindPathOptions } from "./pathfinding-C49JSNNq.js";
6
+ import { a as startRecording, c as IncantoErrorDetails, i as replay, l as auditScene, n as ReplayEvent, o as IncantoError, r as ReplayJson, s as IncantoErrorCode, t as Recorder } from "./replay-CAphXMyM.js";
7
7
 
8
8
  //#region src/core/audio/crossfade.d.ts
9
9
  /**
package/dist/index.js CHANGED
@@ -1,177 +1,13 @@
1
1
  import { C as getBehavior, E as Signal, S as clearBehaviors, T as registeredBehaviors, _ as InputMap, a as SCENE_FORMAT, b as behaviorSchema, c as SceneTree, d as resolveConstants, f as Node, g as resolveOrderGroups, h as effectiveOrder, i as serializeNode, l as CONST_REF_KEY, m as ORDER_GROUP_BASE, n as loadScene, o as computeViewport, p as parseNodePath, r as Scene, s as resolveViewport, u as isConstRef, w as registerBehavior, x as behaviorSignals, y as Behavior } from "./loader-r49nDwB4.js";
2
- import { A as T_PREFIX, B as synthSfx, C as behaviorsWithoutSave, D as LogManager, E as createSaveStore, F as ROLLOFF_MODELS, G as AudioBuses, H as WebAudioMusicBackend, I as spatialGain, L as spatialPan, M as translationKey, N as SfxEngine, O as BASE_LOCALE, P as isAudioContextAvailable, R as SFX_PRESETS, S as SaveSlots, T as restoreBehaviors, U as crossfadeGains, V as MusicManager, W as fadeGain, a as UiSlider, b as readDeviceHints, c as UiButton, d as UiBanner, f as UiBar, g as Settings, h as Engine, i as UiSelect, j as suggestLocale, k as Localization, l as UiDialogue, m as AudioPlayer, n as UiImage, o as UiToggle, p as UiText, r as UiPanel, s as Timer, t as registerCoreNodes, u as HudLayer, v as qualityEnvironment, w as captureBehaviors, x as suggestQuality, z as SFX_PRESET_NAMES } from "./register-DuTlY56W.js";
2
+ import { A as T_PREFIX, B as synthSfx, C as behaviorsWithoutSave, D as LogManager, E as createSaveStore, F as ROLLOFF_MODELS, G as AudioBuses, H as WebAudioMusicBackend, I as spatialGain, L as spatialPan, M as translationKey, N as SfxEngine, O as BASE_LOCALE, P as isAudioContextAvailable, R as SFX_PRESETS, S as SaveSlots, T as restoreBehaviors, U as crossfadeGains, V as MusicManager, W as fadeGain, a as UiSlider, b as readDeviceHints, c as UiButton, d as UiBanner, f as UiBar, g as Settings, h as Engine, i as UiSelect, j as suggestLocale, k as Localization, l as UiDialogue, m as AudioPlayer, n as UiImage, o as UiToggle, p as UiText, r as UiPanel, s as Timer, t as registerCoreNodes, u as HudLayer, v as qualityEnvironment, w as captureBehaviors, x as suggestQuality, z as SFX_PRESET_NAMES } from "./register-BSXV8T9F.js";
3
3
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
4
4
  import { t as Rng } from "./rng-DP-SR7eg.js";
5
- import { n as startRecording, r as auditScene, t as replay } from "./replay-DcWg4LT5.js";
5
+ import { n as startRecording, r as auditScene, t as replay } from "./replay-DilbZgQI.js";
6
6
  import { n as jsonEquals, r as jsonKind, t as jsonClone } from "./json-BLk7H2Qa.js";
7
7
  import { a as getNodeSignals, c as mergeStaticSignals, i as getNodeSchema, l as registerNode, n as clearRegistry, o as getNodeType, r as createNode, u as registeredTypes } from "./registry-IyWCGe4q.js";
8
8
  import { t as createNoise2D } from "./noise-CGUMx44x.js";
9
9
  import { i as applyParticlePreset, n as PARTICLE_PRESETS, r as PARTICLE_PRESET_NAMES, t as ParticleSim } from "./particle-sim-Bw7hB93B.js";
10
+ import { a as findPath, i as preloadUrls, n as newUid, o as gridFromRows, r as assetUrls, t as VERSION } from "./src-CwYxzZKl.js";
10
11
  import { i as resolveRendering, n as attachTouchControls, r as joystickVector, t as TouchControls } from "./touch-BoNg_MnF.js";
11
12
  import { t as duplicateNode } from "./duplicate-CRtihGmC.js";
12
- //#region src/core/pathfinding.ts
13
- /**
14
- * A* from `from` to `to` (inclusive cell coords). Returns the cell path
15
- * INCLUDING both endpoints, or null when unreachable. Straight steps cost
16
- * 1, diagonals √2; the heuristic is octile (admissible for both modes).
17
- */
18
- function findPath(grid, from, to, opts) {
19
- const [sx, sy] = from;
20
- const [tx, ty] = to;
21
- const { width, height } = grid;
22
- const inBounds = (x, y) => x >= 0 && y >= 0 && x < width && y < height;
23
- if (!inBounds(sx, sy) || !inBounds(tx, ty)) return null;
24
- if (grid.solid(sx, sy) || grid.solid(tx, ty)) return null;
25
- if (sx === tx && sy === ty) return [[sx, sy]];
26
- const diagonal = opts?.diagonal ?? true;
27
- const maxExpansions = opts?.maxExpansions ?? 2e4;
28
- const idx = (x, y) => y * width + x;
29
- const gScore = new Float64Array(width * height).fill(Number.POSITIVE_INFINITY);
30
- const cameFrom = new Int32Array(width * height).fill(-1);
31
- const closed = new Uint8Array(width * height);
32
- const heap = [];
33
- const fScore = new Float64Array(width * height).fill(Number.POSITIVE_INFINITY);
34
- const push = (n) => {
35
- heap.push(n);
36
- let i = heap.length - 1;
37
- while (i > 0) {
38
- const p = i - 1 >> 1;
39
- if (fScore[heap[p]] <= fScore[heap[i]]) break;
40
- const t = heap[p];
41
- heap[p] = heap[i];
42
- heap[i] = t;
43
- i = p;
44
- }
45
- };
46
- const pop = () => {
47
- const top = heap[0];
48
- const last = heap.pop();
49
- if (heap.length > 0) {
50
- heap[0] = last;
51
- let i = 0;
52
- for (;;) {
53
- const l = i * 2 + 1;
54
- const r = l + 1;
55
- let m = i;
56
- if (l < heap.length && fScore[heap[l]] < fScore[heap[m]]) m = l;
57
- if (r < heap.length && fScore[heap[r]] < fScore[heap[m]]) m = r;
58
- if (m === i) break;
59
- const t = heap[m];
60
- heap[m] = heap[i];
61
- heap[i] = t;
62
- i = m;
63
- }
64
- }
65
- return top;
66
- };
67
- const SQRT2 = Math.SQRT2;
68
- const octile = (x, y) => {
69
- const dx = Math.abs(x - tx);
70
- const dy = Math.abs(y - ty);
71
- return dx > dy ? dx - dy + SQRT2 * dy : dy - dx + SQRT2 * dx;
72
- };
73
- const start = idx(sx, sy);
74
- gScore[start] = 0;
75
- fScore[start] = octile(sx, sy);
76
- push(start);
77
- let expansions = 0;
78
- while (heap.length > 0) {
79
- if (++expansions > maxExpansions) return null;
80
- const current = pop();
81
- if (closed[current]) continue;
82
- closed[current] = 1;
83
- const cx = current % width;
84
- const cy = current / width | 0;
85
- if (cx === tx && cy === ty) {
86
- const path = [];
87
- let n = current;
88
- while (n !== -1) {
89
- path.push([n % width, n / width | 0]);
90
- n = cameFrom[n];
91
- }
92
- path.reverse();
93
- return path;
94
- }
95
- for (let dy = -1; dy <= 1; dy++) for (let dx = -1; dx <= 1; dx++) {
96
- if (dx === 0 && dy === 0) continue;
97
- const isDiag = dx !== 0 && dy !== 0;
98
- if (isDiag && !diagonal) continue;
99
- const nx = cx + dx;
100
- const ny = cy + dy;
101
- if (!inBounds(nx, ny) || grid.solid(nx, ny)) continue;
102
- if (isDiag && (grid.solid(cx + dx, cy) || grid.solid(cx, cy + dy))) continue;
103
- const n = idx(nx, ny);
104
- if (closed[n]) continue;
105
- const tentative = gScore[current] + (isDiag ? SQRT2 : 1);
106
- if (tentative < gScore[n]) {
107
- gScore[n] = tentative;
108
- fScore[n] = tentative + octile(nx, ny);
109
- cameFrom[n] = current;
110
- push(n);
111
- }
112
- }
113
- }
114
- return null;
115
- }
116
- /** Build a PathGrid from row-strings ('#' = solid) — tests and tile games. */
117
- function gridFromRows(rows) {
118
- const height = rows.length;
119
- return {
120
- width: rows[0]?.length ?? 0,
121
- height,
122
- solid: (x, y) => (rows[y]?.[x] ?? "#") === "#"
123
- };
124
- }
125
- //#endregion
126
- //#region src/core/preload.ts
127
- /**
128
- * Warm the HTTP cache for a list of asset urls with a progress callback —
129
- * the loading-bar pattern every template ships. Failures only warn and still
130
- * count toward progress (a broken url must not block the game), but they are
131
- * reported in the result so callers can tell success from 100%-with-holes.
132
- */
133
- async function preloadUrls(urls, onProgress) {
134
- let loaded = 0;
135
- const failed = [];
136
- const total = urls.length;
137
- if (total === 0) {
138
- onProgress?.(0, 0);
139
- return { failed };
140
- }
141
- await Promise.all(urls.map(async (url) => {
142
- try {
143
- await (await fetch(url)).arrayBuffer();
144
- } catch (error) {
145
- failed.push(url);
146
- console.warn(`[incanto] preload failed for ${url}:`, error);
147
- }
148
- loaded += 1;
149
- onProgress?.(loaded, total);
150
- }));
151
- return { failed };
152
- }
153
- /** Every url found in a scene's `assets` block (preload helper). */
154
- function assetUrls(assets) {
155
- return Object.values(assets ?? {}).map((a) => a.url).filter((u) => typeof u === "string" && !u.startsWith("data:"));
156
- }
157
- //#endregion
158
- //#region src/core/uid-gen.ts
159
- /**
160
- * The ONE way to mint a node uid: crypto-strength, `n_` + 16 base36 chars
161
- * (~82 bits). Hand-written uids are forbidden by convention — they break the
162
- * collision-strength contract and read as fakes next to generated ones.
163
- */
164
- function newUid() {
165
- const bytes = new Uint8Array(16);
166
- if (globalThis.crypto?.getRandomValues) globalThis.crypto.getRandomValues(bytes);
167
- else for (let i = 0; i < bytes.length; i++) bytes[i] = Math.floor(Math.random() * 256);
168
- let id = "";
169
- for (const b of bytes) id += (b % 36).toString(36);
170
- return `n_${id.slice(0, 16)}`;
171
- }
172
- //#endregion
173
- //#region src/index.ts
174
- /** Engine version. Kept in sync with package.json by the release pipeline. */
175
- const VERSION = "0.41.0";
176
- //#endregion
177
13
  export { AudioBuses, AudioPlayer, BASE_LOCALE, Behavior, CONST_REF_KEY, Engine, HudLayer, IncantoError, InputMap, Localization, LogManager, MusicManager, Node, ORDER_GROUP_BASE, PARTICLE_PRESETS, PARTICLE_PRESET_NAMES, ParticleSim, ROLLOFF_MODELS, Rng, SCENE_FORMAT, SFX_PRESETS, SFX_PRESET_NAMES, SaveSlots, Scene, SceneTree, Settings, SfxEngine, Signal, T_PREFIX, Timer, TouchControls, UiBanner, UiBar, UiButton, UiDialogue, UiImage, UiPanel, UiSelect, UiSlider, UiText, UiToggle, VERSION, WebAudioMusicBackend, applyParticlePreset, assetUrls, attachTouchControls, auditScene, behaviorSchema, behaviorSignals, behaviorsWithoutSave, captureBehaviors, clearBehaviors, clearRegistry, computeViewport, createNode, createNoise2D, createSaveStore, crossfadeGains, duplicateNode, effectiveOrder, fadeGain, findPath, getBehavior, getNodeSchema, getNodeSignals, getNodeType, gridFromRows, isAudioContextAvailable, isConstRef, joystickVector, jsonClone, jsonEquals, jsonKind, loadScene, mergeStaticSignals, newUid, parseNodePath, preloadUrls, qualityEnvironment, readDeviceHints, registerBehavior, registerCoreNodes, registerNode, registeredBehaviors, registeredTypes, replay, resolveConstants, resolveOrderGroups, resolveRendering, resolveViewport, restoreBehaviors, serializeNode, spatialGain, spatialPan, startRecording, suggestLocale, suggestQuality, synthSfx, translationKey };
@@ -1,4 +1,4 @@
1
- import { P as Scene, b as Engine } from "./behavior-TA8nySqb.js";
1
+ import { P as Scene, b as Engine } from "./behavior-62q0HWBO.js";
2
2
  import { i as SceneJson } from "./schema-CFeioQRE.js";
3
3
 
4
4
  //#region src/core/scene/loader.d.ts
package/dist/net.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Et as Node, Mt as Signal, b as Engine, d as PropSchema } from "./behavior-TA8nySqb.js";
1
+ import { Et as Node, Mt as Signal, b as Engine, d as PropSchema } from "./behavior-62q0HWBO.js";
2
2
  import { c as JsonValue, i as SceneJson, s as JsonObject } from "./schema-CFeioQRE.js";
3
3
 
4
4
  //#region src/net/types.d.ts
package/dist/net.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { n as loadScene } from "./loader-r49nDwB4.js";
2
- import { h as Engine } from "./register-DuTlY56W.js";
2
+ import { h as Engine } from "./register-BSXV8T9F.js";
3
3
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
4
4
  import { t as jsonClone } from "./json-BLk7H2Qa.js";
5
5
  import { n as createAgent8Server } from "./agent8-CvsfVskX.js";
6
- import { i as applySyncPatch, n as NetworkSpawner, r as NetworkManager, t as registerNodesNet } from "./register-DnycZ91Q.js";
6
+ import { i as applySyncPatch, n as NetworkSpawner, r as NetworkManager, t as registerNodesNet } from "./register-D651it1J.js";
7
7
  //#region src/net/loopback.ts
8
8
  /**
9
9
  * In-memory multiplayer hub implementing the SAME kernel contract as
@@ -1,4 +1,4 @@
1
- import { Et as Node, d as PropSchema, st as Listener } from "./behavior-TA8nySqb.js";
1
+ import { Et as Node, d as PropSchema, st as Listener } from "./behavior-62q0HWBO.js";
2
2
 
3
3
  //#region src/core/nodes/audio-player.d.ts
4
4
  /**
@@ -1,7 +1,7 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
2
  import { v as diagnose } from "./loader-r49nDwB4.js";
3
3
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
4
- import { _ as RigidBody2D, b as validateCollider2D, g as PhysicsBody2D, h as CharacterBody2D, m as Area2D, p as Joint2D, y as Node2D } from "./register-MSZvnHkp.js";
4
+ import { _ as RigidBody2D, b as validateCollider2D, g as PhysicsBody2D, h as CharacterBody2D, m as Area2D, p as Joint2D, y as Node2D } from "./register-R2JTnIMw.js";
5
5
  import { n as registerDebugSource } from "./debug-draw-BM3DsvtT.js";
6
6
  //#region src/2d/physics/physics-2d.ts
7
7
  var physics_2d_exports = /* @__PURE__ */ __exportAll({
@@ -1,9 +1,9 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
2
  import { v as diagnose } from "./loader-r49nDwB4.js";
3
3
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
4
- import { F as PhysicsBody3D, I as RigidBody3D, N as Area3D, P as CharacterBody3D, R as Node3D, z as validateCollider3D } from "./gameplay-By8mslMc.js";
4
+ import { F as PhysicsBody3D, I as RigidBody3D, N as Area3D, P as CharacterBody3D, R as Node3D, z as validateCollider3D } from "./gameplay-BQOeAid6.js";
5
5
  import { n as registerDebugSource } from "./debug-draw-BM3DsvtT.js";
6
- import { A as Terrain3D, F as InstancedMesh3D, L as buildMeshGeometry, P as Joint3D } from "./environment-presets-pGTHb_V4.js";
6
+ import { A as Terrain3D, F as InstancedMesh3D, L as buildMeshGeometry, P as Joint3D } from "./environment-presets-Ds5kXLoF.js";
7
7
  import { Euler, Matrix4, Quaternion, Vector3 } from "three";
8
8
  //#region src/3d/physics/collider-lines.ts
9
9
  /**
package/dist/react.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { P as Scene, b as Engine, n as BehaviorCtor } from "./behavior-TA8nySqb.js";
1
+ import { P as Scene, b as Engine, n as BehaviorCtor } from "./behavior-62q0HWBO.js";
2
2
  import { c as JsonValue } from "./schema-CFeioQRE.js";
3
3
  import { CSSProperties, ReactNode } from "react";
4
4
 
package/dist/react.js CHANGED
@@ -156,7 +156,7 @@ function IncantoCanvas(props) {
156
156
  pointer: latest.pointer,
157
157
  ...keyboard !== void 0 ? { keyboard } : {}
158
158
  };
159
- const next = await (_gameFactory ?? (mode === "3d" ? async (o) => (await import("./create-game-u7TDbvln.js").then((n) => n.n)).createGame3D(o) : async (o) => (await import("./create-game-CZpENyin.js").then((n) => n.n)).createGame2D(o)))(opts);
159
+ const next = await (_gameFactory ?? (mode === "3d" ? async (o) => (await import("./create-game-B3vBWgVD.js").then((n) => n.n)).createGame3D(o) : async (o) => (await import("./create-game-DFRbG0Bj.js").then((n) => n.n)).createGame2D(o)))(opts);
160
160
  if (disposed) {
161
161
  next.dispose();
162
162
  return;
@@ -204,7 +204,9 @@ var WebAudioMusicBackend = class {
204
204
  * frame to advance fades (the engine wires this to `updated`).
205
205
  *
206
206
  * ```ts
207
- * engine.music.play('incanto/assets/audio/theme.mp3'); // loop, full
207
+ * // NOTE: the bundle ships SFX only — no music track. A background track is
208
+ * // your game's own file under public/, or a URL.
209
+ * engine.music.play('/audio/theme.mp3'); // loop, full
208
210
  * engine.music.crossfadeTo('boss.mp3', 3); // 3s equal-power swap
209
211
  * engine.music.stop(2); // fade out over 2s
210
212
  * engine.music.setVolume(0.5); // per-manager volume
@@ -1,5 +1,5 @@
1
1
  import { E as Signal, f as Node, t as buildNodeJson } from "./loader-r49nDwB4.js";
2
- import { t as registerCoreNodes } from "./register-DuTlY56W.js";
2
+ import { t as registerCoreNodes } from "./register-BSXV8T9F.js";
3
3
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
4
4
  import { n as jsonEquals, t as jsonClone } from "./json-BLk7H2Qa.js";
5
5
  import { l as registerNode } from "./registry-IyWCGe4q.js";
@@ -1,5 +1,5 @@
1
1
  import { f as Node, h as effectiveOrder } from "./loader-r49nDwB4.js";
2
- import { t as registerCoreNodes } from "./register-DuTlY56W.js";
2
+ import { t as registerCoreNodes } from "./register-BSXV8T9F.js";
3
3
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
4
4
  import { t as Rng } from "./rng-DP-SR7eg.js";
5
5
  import { i as getNodeSchema, l as registerNode } from "./registry-IyWCGe4q.js";
@@ -1,4 +1,4 @@
1
- import { b as Engine } from "./behavior-TA8nySqb.js";
1
+ import { b as Engine } from "./behavior-62q0HWBO.js";
2
2
  import { s as JsonObject } from "./schema-CFeioQRE.js";
3
3
 
4
4
  //#region src/core/audit.d.ts
@@ -1,4 +1,4 @@
1
- import { M as translationKey } from "./register-DuTlY56W.js";
1
+ import { M as translationKey } from "./register-BSXV8T9F.js";
2
2
  //#region src/core/audit.ts
3
3
  const ANIMATED_TYPES = new Set([
4
4
  "Particles2D",
@@ -0,0 +1,166 @@
1
+ //#region src/core/pathfinding.ts
2
+ /**
3
+ * A* from `from` to `to` (inclusive cell coords). Returns the cell path
4
+ * INCLUDING both endpoints, or null when unreachable. Straight steps cost
5
+ * 1, diagonals √2; the heuristic is octile (admissible for both modes).
6
+ */
7
+ function findPath(grid, from, to, opts) {
8
+ const [sx, sy] = from;
9
+ const [tx, ty] = to;
10
+ const { width, height } = grid;
11
+ const inBounds = (x, y) => x >= 0 && y >= 0 && x < width && y < height;
12
+ if (!inBounds(sx, sy) || !inBounds(tx, ty)) return null;
13
+ if (grid.solid(sx, sy) || grid.solid(tx, ty)) return null;
14
+ if (sx === tx && sy === ty) return [[sx, sy]];
15
+ const diagonal = opts?.diagonal ?? true;
16
+ const maxExpansions = opts?.maxExpansions ?? 2e4;
17
+ const idx = (x, y) => y * width + x;
18
+ const gScore = new Float64Array(width * height).fill(Number.POSITIVE_INFINITY);
19
+ const cameFrom = new Int32Array(width * height).fill(-1);
20
+ const closed = new Uint8Array(width * height);
21
+ const heap = [];
22
+ const fScore = new Float64Array(width * height).fill(Number.POSITIVE_INFINITY);
23
+ const push = (n) => {
24
+ heap.push(n);
25
+ let i = heap.length - 1;
26
+ while (i > 0) {
27
+ const p = i - 1 >> 1;
28
+ if (fScore[heap[p]] <= fScore[heap[i]]) break;
29
+ const t = heap[p];
30
+ heap[p] = heap[i];
31
+ heap[i] = t;
32
+ i = p;
33
+ }
34
+ };
35
+ const pop = () => {
36
+ const top = heap[0];
37
+ const last = heap.pop();
38
+ if (heap.length > 0) {
39
+ heap[0] = last;
40
+ let i = 0;
41
+ for (;;) {
42
+ const l = i * 2 + 1;
43
+ const r = l + 1;
44
+ let m = i;
45
+ if (l < heap.length && fScore[heap[l]] < fScore[heap[m]]) m = l;
46
+ if (r < heap.length && fScore[heap[r]] < fScore[heap[m]]) m = r;
47
+ if (m === i) break;
48
+ const t = heap[m];
49
+ heap[m] = heap[i];
50
+ heap[i] = t;
51
+ i = m;
52
+ }
53
+ }
54
+ return top;
55
+ };
56
+ const SQRT2 = Math.SQRT2;
57
+ const octile = (x, y) => {
58
+ const dx = Math.abs(x - tx);
59
+ const dy = Math.abs(y - ty);
60
+ return dx > dy ? dx - dy + SQRT2 * dy : dy - dx + SQRT2 * dx;
61
+ };
62
+ const start = idx(sx, sy);
63
+ gScore[start] = 0;
64
+ fScore[start] = octile(sx, sy);
65
+ push(start);
66
+ let expansions = 0;
67
+ while (heap.length > 0) {
68
+ if (++expansions > maxExpansions) return null;
69
+ const current = pop();
70
+ if (closed[current]) continue;
71
+ closed[current] = 1;
72
+ const cx = current % width;
73
+ const cy = current / width | 0;
74
+ if (cx === tx && cy === ty) {
75
+ const path = [];
76
+ let n = current;
77
+ while (n !== -1) {
78
+ path.push([n % width, n / width | 0]);
79
+ n = cameFrom[n];
80
+ }
81
+ path.reverse();
82
+ return path;
83
+ }
84
+ for (let dy = -1; dy <= 1; dy++) for (let dx = -1; dx <= 1; dx++) {
85
+ if (dx === 0 && dy === 0) continue;
86
+ const isDiag = dx !== 0 && dy !== 0;
87
+ if (isDiag && !diagonal) continue;
88
+ const nx = cx + dx;
89
+ const ny = cy + dy;
90
+ if (!inBounds(nx, ny) || grid.solid(nx, ny)) continue;
91
+ if (isDiag && (grid.solid(cx + dx, cy) || grid.solid(cx, cy + dy))) continue;
92
+ const n = idx(nx, ny);
93
+ if (closed[n]) continue;
94
+ const tentative = gScore[current] + (isDiag ? SQRT2 : 1);
95
+ if (tentative < gScore[n]) {
96
+ gScore[n] = tentative;
97
+ fScore[n] = tentative + octile(nx, ny);
98
+ cameFrom[n] = current;
99
+ push(n);
100
+ }
101
+ }
102
+ }
103
+ return null;
104
+ }
105
+ /** Build a PathGrid from row-strings ('#' = solid) — tests and tile games. */
106
+ function gridFromRows(rows) {
107
+ const height = rows.length;
108
+ return {
109
+ width: rows[0]?.length ?? 0,
110
+ height,
111
+ solid: (x, y) => (rows[y]?.[x] ?? "#") === "#"
112
+ };
113
+ }
114
+ //#endregion
115
+ //#region src/core/preload.ts
116
+ /**
117
+ * Warm the HTTP cache for a list of asset urls with a progress callback —
118
+ * the loading-bar pattern every template ships. Failures only warn and still
119
+ * count toward progress (a broken url must not block the game), but they are
120
+ * reported in the result so callers can tell success from 100%-with-holes.
121
+ */
122
+ async function preloadUrls(urls, onProgress) {
123
+ let loaded = 0;
124
+ const failed = [];
125
+ const total = urls.length;
126
+ if (total === 0) {
127
+ onProgress?.(0, 0);
128
+ return { failed };
129
+ }
130
+ await Promise.all(urls.map(async (url) => {
131
+ try {
132
+ await (await fetch(url)).arrayBuffer();
133
+ } catch (error) {
134
+ failed.push(url);
135
+ console.warn(`[incanto] preload failed for ${url}:`, error);
136
+ }
137
+ loaded += 1;
138
+ onProgress?.(loaded, total);
139
+ }));
140
+ return { failed };
141
+ }
142
+ /** Every url found in a scene's `assets` block (preload helper). */
143
+ function assetUrls(assets) {
144
+ return Object.values(assets ?? {}).map((a) => a.url).filter((u) => typeof u === "string" && !u.startsWith("data:"));
145
+ }
146
+ //#endregion
147
+ //#region src/core/uid-gen.ts
148
+ /**
149
+ * The ONE way to mint a node uid: crypto-strength, `n_` + 16 base36 chars
150
+ * (~82 bits). Hand-written uids are forbidden by convention — they break the
151
+ * collision-strength contract and read as fakes next to generated ones.
152
+ */
153
+ function newUid() {
154
+ const bytes = new Uint8Array(16);
155
+ if (globalThis.crypto?.getRandomValues) globalThis.crypto.getRandomValues(bytes);
156
+ else for (let i = 0; i < bytes.length; i++) bytes[i] = Math.floor(Math.random() * 256);
157
+ let id = "";
158
+ for (const b of bytes) id += (b % 36).toString(36);
159
+ return `n_${id.slice(0, 16)}`;
160
+ }
161
+ //#endregion
162
+ //#region src/index.ts
163
+ /** Engine version. Kept in sync with package.json by the release pipeline. */
164
+ const VERSION = "0.43.0";
165
+ //#endregion
166
+ export { findPath as a, preloadUrls as i, newUid as n, gridFromRows as o, assetUrls as r, VERSION as t };