incanto 0.40.1 → 0.42.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 (46) hide show
  1. package/bin/incanto-frame.mjs +144 -0
  2. package/dist/2d.d.ts +2 -2
  3. package/dist/2d.js +3 -3
  4. package/dist/3d.d.ts +76 -4
  5. package/dist/3d.js +5 -5
  6. package/dist/{behavior-TA8nySqb.d.ts → behavior-62q0HWBO.d.ts} +3 -1
  7. package/dist/{create-game-B7c5icho.js → create-game-9KPppR0L.js} +4 -4
  8. package/dist/{create-game-p-O66Y_U.js → create-game-DItIRyXH.js} +158 -5
  9. package/dist/debug.d.ts +36 -2
  10. package/dist/debug.js +183 -2
  11. package/dist/{environment-presets-CIZXGKnS.js → environment-presets-Vl5xBsXp.js} +2 -2
  12. package/dist/{gameplay-By8mslMc.js → gameplay-DRi9524r.js} +13 -0
  13. package/dist/gameplay.d.ts +13 -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-CxIqR1cW.js → physics-2d-BmgXBNDB.js} +1 -1
  22. package/dist/{physics-3d-CgIHivyL.js → physics-3d-C2G604O1.js} +2 -2
  23. package/dist/react.d.ts +1 -1
  24. package/dist/react.js +1 -1
  25. package/dist/{register-DMFJI-c_.js → register-BSXV8T9F.js} +5 -1
  26. package/dist/{register-D5v8iqjZ.js → register-D651it1J.js} +1 -1
  27. package/dist/{register-CgMO5JBY.js → register-R2JTnIMw.js} +1 -1
  28. package/dist/{replay-DN-kG4va.d.ts → replay-CAphXMyM.d.ts} +1 -1
  29. package/dist/{replay-D_QY5Xm3.js → replay-DilbZgQI.js} +1 -1
  30. package/dist/src-cU57Uwdw.js +166 -0
  31. package/dist/{test-DUrGse_6.js → test-DuOD1DO8.js} +22 -11
  32. package/dist/test.d.ts +3 -3
  33. package/dist/test.js +2 -2
  34. package/dist/vite.d.ts +61 -1
  35. package/dist/vite.js +149 -2
  36. package/editor/assets/{agent8-CFq3LMrx.js → agent8-Csw0T7jh.js} +1 -1
  37. package/editor/assets/debug-C3qeBD4X.js +3 -0
  38. package/editor/assets/{index-CRwy6mq9.js → index-B1rUkWxB.js} +50 -50
  39. package/editor/index.html +1 -1
  40. package/package.json +3 -2
  41. package/skills/incanto-audio.md +6 -2
  42. package/skills/incanto-verifying-your-game.md +60 -2
  43. package/templates-app/beacon-isle-3d/package.json +1 -1
  44. package/templates-app/tps-3d/package.json +1 -1
  45. package/templates-app/village-quest-3d/package.json +1 -1
  46. package/editor/assets/debug-C2aomumK.js +0 -3
package/dist/debug.js CHANGED
@@ -192,6 +192,9 @@ function detailText(value, json) {
192
192
  function isPlainObject(value) {
193
193
  return typeof value === "object" && value !== null && !Array.isArray(value);
194
194
  }
195
+ /** The slider's ceiling. Beyond this the number box is the tool — a 0..8 slider
196
+ * makes the 0.1-to-1 range, where slow-motion debugging actually lives, unusable. */
197
+ const TIME_SLIDER_MAX = 2;
195
198
  /** Attach the overlay; null when no DOM is available (headless). */
196
199
  function attachDebugOverlay(engine, opts = {}) {
197
200
  const doc = opts.doc ?? (typeof document !== "undefined" ? document : null);
@@ -235,6 +238,15 @@ var DebugOverlay = class {
235
238
  editing = 0;
236
239
  /** Which bulky values are unfolded, per node ('transforms', 'collider.vertices'). */
237
240
  detailOpen = /* @__PURE__ */ new WeakMap();
241
+ /**
242
+ * The scale to come back to when a pause is lifted.
243
+ *
244
+ * Captured only from a RUNNING scale: pausing an already-frozen game (a
245
+ * hit-stop, a previous pause) must not record 0 as the thing to restore, or
246
+ * Resume does nothing and the game looks hung.
247
+ */
248
+ resumeScale = 1;
249
+ timeEls = null;
238
250
  /** Pointer inside the inspector — it stops refreshing under your hand. */
239
251
  hovering = false;
240
252
  constructor(engine, container, doc, statsSource, actions = []) {
@@ -282,6 +294,7 @@ var DebugOverlay = class {
282
294
  this.renderExplorer();
283
295
  this.renderStats();
284
296
  if (this.editing === 0 && !this.hovering) this.renderInspector();
297
+ this.syncTime();
285
298
  }));
286
299
  }
287
300
  isOpen(id) {
@@ -314,11 +327,18 @@ var DebugOverlay = class {
314
327
  this.openStatsChip();
315
328
  return;
316
329
  }
317
- if (this.panels.has(id)) return;
330
+ if (this.panels.has(id)) {
331
+ if (id === "explorer") this.renderExplorer();
332
+ if (id === "inspector") this.renderInspector();
333
+ if (id === "logs") this.renderLogs();
334
+ if (id === "time") this.renderTime();
335
+ return;
336
+ }
318
337
  const panel = new FloatingPanel(this.doc, this.container, {
319
338
  explorer: "Explorer",
320
339
  inspector: "Inspector",
321
- logs: "Logs"
340
+ logs: "Logs",
341
+ time: "Time"
322
342
  }[id], {
323
343
  explorer: {
324
344
  x: 12,
@@ -337,6 +357,12 @@ var DebugOverlay = class {
337
357
  y: 380,
338
358
  w: 532,
339
359
  h: 200
360
+ },
361
+ time: {
362
+ x: 556,
363
+ y: 44,
364
+ w: 260,
365
+ h: 150
340
366
  }
341
367
  }[id]);
342
368
  panel.onClose = () => this.close(id);
@@ -352,6 +378,7 @@ var DebugOverlay = class {
352
378
  if (id === "explorer") this.renderExplorer();
353
379
  if (id === "inspector") this.renderInspector();
354
380
  if (id === "logs") this.renderLogs();
381
+ if (id === "time") this.renderTime();
355
382
  }
356
383
  close(id) {
357
384
  if (id === "inspector") this.hovering = false;
@@ -453,6 +480,7 @@ var DebugOverlay = class {
453
480
  ["explorer", "Explorer"],
454
481
  ["inspector", "Inspector"],
455
482
  ["logs", "Logs"],
483
+ ["time", "Time"],
456
484
  ["stats", "Stats"],
457
485
  ["colliders", "Colliders"]
458
486
  ]) {
@@ -849,6 +877,148 @@ var DebugOverlay = class {
849
877
  if (this.logRows.length > MAX_LOG_ROWS) this.logRows.splice(0, this.logRows.length - MAX_LOG_ROWS);
850
878
  this.renderLogs();
851
879
  }
880
+ /**
881
+ * Set game time, defensively. `timeScale` multiplies every dt in the engine,
882
+ * so a NaN from a text field would poison physics, timers and animation in
883
+ * one frame, and a negative would run the simulation backwards through code
884
+ * that has never been asked to.
885
+ */
886
+ setTimeScale(next) {
887
+ if (!Number.isFinite(next)) return;
888
+ this.engine.timeScale = Math.max(0, next);
889
+ this.syncTime();
890
+ }
891
+ /** Freeze or unfreeze game time. Rendering keeps running — this is a pause,
892
+ * not a stop: the camera still moves and the overlay stays interactive. */
893
+ setPaused(paused) {
894
+ if (paused) {
895
+ if (this.engine.timeScale > 0) this.resumeScale = this.engine.timeScale;
896
+ this.engine.timeScale = 0;
897
+ } else this.engine.timeScale = this.resumeScale > 0 ? this.resumeScale : 1;
898
+ this.syncTime();
899
+ }
900
+ get paused() {
901
+ return this.engine.timeScale === 0;
902
+ }
903
+ /**
904
+ * Advance exactly one frame, pausing first.
905
+ *
906
+ * Stepping a RUNNING game is meaningless — the next real frame lands a
907
+ * millisecond later and swamps the step — so this pauses and then steps,
908
+ * which is also the only way the button is useful twice in a row.
909
+ */
910
+ nextFrame() {
911
+ if (!this.paused) this.setPaused(true);
912
+ this.engine.step();
913
+ this.syncTime();
914
+ }
915
+ /** Push the engine's current time state into the panel, without rebuilding it. */
916
+ syncTime() {
917
+ const els = this.timeEls;
918
+ if (!els) return;
919
+ const scale = this.engine.timeScale;
920
+ const text = String(Math.round(scale * 1e3) / 1e3);
921
+ if (this.editing === 0) {
922
+ els.slider.value = String(Math.min(scale, TIME_SLIDER_MAX));
923
+ els.box.value = text;
924
+ }
925
+ els.readout.textContent = `timeScale ${text}${this.paused ? " · paused" : ""}`;
926
+ els.pause.textContent = this.paused ? "▶ Resume" : "⏸ Pause";
927
+ }
928
+ renderTime() {
929
+ const panel = this.panels.get("time");
930
+ if (!panel) return;
931
+ clear(panel.body);
932
+ this.timeEls = null;
933
+ const readout = this.doc.createElement("div");
934
+ applyStyle(readout, {
935
+ marginBottom: "6px",
936
+ opacity: "0.85"
937
+ });
938
+ panel.body.appendChild(readout);
939
+ const slider = this.doc.createElement("input");
940
+ slider.type = "range";
941
+ slider.min = "0";
942
+ slider.max = String(TIME_SLIDER_MAX);
943
+ slider.step = "0.05";
944
+ slider.value = String(Math.min(this.engine.timeScale, TIME_SLIDER_MAX));
945
+ applyStyle(slider, {
946
+ width: "100%",
947
+ marginBottom: "6px"
948
+ });
949
+ this.trackEditing(slider);
950
+ slider.addEventListener("input", () => this.setTimeScale(Number(slider.value)));
951
+ panel.body.appendChild(slider);
952
+ const row = this.doc.createElement("div");
953
+ applyStyle(row, {
954
+ display: "flex",
955
+ gap: "6px",
956
+ alignItems: "center",
957
+ marginBottom: "8px"
958
+ });
959
+ const box = this.doc.createElement("input");
960
+ box.type = "number";
961
+ box.min = "0";
962
+ box.step = "0.05";
963
+ box.value = String(this.engine.timeScale);
964
+ applyStyle(box, inputStyle("72px"));
965
+ this.trackEditing(box);
966
+ box.addEventListener("change", () => {
967
+ const next = Number(box.value);
968
+ if (Number.isFinite(next)) this.setTimeScale(next);
969
+ this.syncTimeAfterEdit();
970
+ });
971
+ row.appendChild(box);
972
+ for (const preset of [
973
+ .25,
974
+ .5,
975
+ 1,
976
+ 2
977
+ ]) {
978
+ const chip = this.doc.createElement("div");
979
+ chip.textContent = `${preset}×`;
980
+ applyStyle(chip, {
981
+ cursor: "pointer",
982
+ opacity: "0.7",
983
+ padding: "2px 4px"
984
+ });
985
+ chip.addEventListener("click", () => this.setTimeScale(preset));
986
+ row.appendChild(chip);
987
+ }
988
+ panel.body.appendChild(row);
989
+ const buttons = this.doc.createElement("div");
990
+ applyStyle(buttons, {
991
+ display: "flex",
992
+ gap: "8px"
993
+ });
994
+ const pause = this.doc.createElement("div");
995
+ applyStyle(pause, buttonStyle());
996
+ pause.addEventListener("click", () => this.setPaused(!this.paused));
997
+ buttons.appendChild(pause);
998
+ const step = this.doc.createElement("div");
999
+ step.textContent = "⏭ Next frame";
1000
+ step.title = "Pauses, then advances one fixed step";
1001
+ applyStyle(step, buttonStyle());
1002
+ step.addEventListener("click", () => this.nextFrame());
1003
+ buttons.appendChild(step);
1004
+ panel.body.appendChild(buttons);
1005
+ this.timeEls = {
1006
+ slider,
1007
+ box,
1008
+ readout,
1009
+ pause
1010
+ };
1011
+ this.syncTime();
1012
+ }
1013
+ /** After a committed edit the field must show what the ENGINE took — a
1014
+ * rejected 'abc' or a clamped -2 has to be visible, not left on screen. */
1015
+ syncTimeAfterEdit() {
1016
+ const els = this.timeEls;
1017
+ if (!els) return;
1018
+ const scale = this.engine.timeScale;
1019
+ els.slider.value = String(Math.min(scale, TIME_SLIDER_MAX));
1020
+ els.box.value = String(Math.round(scale * 1e3) / 1e3);
1021
+ }
852
1022
  renderLogs() {
853
1023
  const panel = this.panels.get("logs");
854
1024
  if (!panel) return;
@@ -924,6 +1094,17 @@ function stringify(part) {
924
1094
  return String(part);
925
1095
  }
926
1096
  }
1097
+ /** A clickable chip that reads as a button in the overlay's flat style. */
1098
+ function buttonStyle() {
1099
+ return {
1100
+ background: "rgba(255,255,255,0.08)",
1101
+ border: "1px solid rgba(255,255,255,0.2)",
1102
+ borderRadius: "4px",
1103
+ padding: "4px 8px",
1104
+ cursor: "pointer",
1105
+ userSelect: "none"
1106
+ };
1107
+ }
927
1108
  function inputStyle(width) {
928
1109
  return {
929
1110
  width,
@@ -1,10 +1,10 @@
1
- import { t as registerCoreNodes } from "./register-DMFJI-c_.js";
1
+ import { t as registerCoreNodes } from "./register-BSXV8T9F.js";
2
2
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
3
3
  import { t as Rng } from "./rng-DP-SR7eg.js";
4
4
  import { i as getNodeSchema, l as registerNode } from "./registry-IyWCGe4q.js";
5
5
  import { t as createNoise2D } from "./noise-CGUMx44x.js";
6
6
  import { i as applyParticlePreset, r as PARTICLE_PRESET_NAMES, t as ParticleSim } from "./particle-sim-Bw7hB93B.js";
7
- import { A as Water3D, F as PhysicsBody3D, H as colliderFootDrop, I as RigidBody3D, L as StaticBody3D, M as WaterCutout3D, N as Area3D, P as CharacterBody3D, R as Node3D } from "./gameplay-By8mslMc.js";
7
+ import { A as Water3D, F as PhysicsBody3D, H as colliderFootDrop, I as RigidBody3D, L as StaticBody3D, M as WaterCutout3D, N as Area3D, P as CharacterBody3D, R as Node3D } from "./gameplay-DRi9524r.js";
8
8
  import { n as splatWeights, t as buildHeightmap } from "./heightmap-CRK0M4jT.js";
9
9
  import { AdditiveBlending, AnimationClip, AnimationMixer, Box3, BoxGeometry, BufferAttribute, BufferGeometry, CanvasTexture, CapsuleGeometry, ClampToEdgeWrapping, Color, ConeGeometry, CylinderGeometry, DataTexture, DirectionalLight, DoubleSide, DynamicDrawUsage, Euler, Group, IcosahedronGeometry, ImageBitmapLoader, InstancedBufferAttribute, InstancedMesh, LinearFilter, LinearMipmapLinearFilter, LoopOnce, LoopRepeat, Matrix4, Mesh, MeshBasicMaterial, MeshDepthMaterial, MeshPhysicalMaterial, MeshStandardMaterial, NearestFilter, NoBlending, NoColorSpace, NormalBlending, PerspectiveCamera, PlaneGeometry, PointLight, Points, PointsMaterial, Quaternion, QuaternionKeyframeTrack, RGBADepthPacking, RGBAFormat, RepeatWrapping, SRGBColorSpace, ShaderChunk, ShaderMaterial, SphereGeometry, Texture, TextureLoader, UniformsLib, UniformsUtils, Vector3, Vector4, VectorKeyframeTrack } from "three";
10
10
  import { clone } from "three/addons/utils/SkeletonUtils.js";
@@ -5166,7 +5166,20 @@ var CameraShake = class extends Behavior {
5166
5166
  magnitude = 0;
5167
5167
  t = 0;
5168
5168
  prev = [];
5169
+ /**
5170
+ * Start a shake — unless the player asked for no motion.
5171
+ *
5172
+ * `settings.reduceMotion` is an accessibility switch, and camera movement the
5173
+ * player did not cause is the vestibular trigger it exists for. Checked HERE
5174
+ * rather than in `update` so a shake requested while it is on leaves nothing
5175
+ * queued to play the moment it is turned off.
5176
+ *
5177
+ * It deliberately does NOT touch gameplay timing (`hitStop`): freezing time
5178
+ * for 80 ms is feedback, not motion, and removing it would change what the
5179
+ * game is rather than how it moves.
5180
+ */
5169
5181
  shake(magnitude, seconds) {
5182
+ if (this.engine?.settings.get("reduceMotion") === true) return;
5170
5183
  this.magnitude = Math.max(this.magnitude, magnitude);
5171
5184
  this.t = seconds ?? this.falloff;
5172
5185
  }
@@ -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
@@ -685,6 +685,18 @@ declare class CameraShake extends Behavior {
685
685
  private magnitude;
686
686
  private t;
687
687
  private prev;
688
+ /**
689
+ * Start a shake — unless the player asked for no motion.
690
+ *
691
+ * `settings.reduceMotion` is an accessibility switch, and camera movement the
692
+ * player did not cause is the vestibular trigger it exists for. Checked HERE
693
+ * rather than in `update` so a shake requested while it is on leaves nothing
694
+ * queued to play the moment it is turned off.
695
+ *
696
+ * It deliberately does NOT touch gameplay timing (`hitStop`): freezing time
697
+ * for 80 ms is feedback, not motion, and removing it would change what the
698
+ * game is rather than how it moves.
699
+ */
688
700
  shake(magnitude: number, seconds?: number): void;
689
701
  override update(dt: number): void;
690
702
  }
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-DRi9524r.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-DMFJI-c_.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-D_QY5Xm3.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-cU57Uwdw.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.40.1";
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-DMFJI-c_.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-D5v8iqjZ.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-CgMO5JBY.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-DRi9524r.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-CIZXGKnS.js";
6
+ import { A as Terrain3D, F as InstancedMesh3D, L as buildMeshGeometry, P as Joint3D } from "./environment-presets-Vl5xBsXp.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-p-O66Y_U.js").then((n) => n.n)).createGame3D(o) : async (o) => (await import("./create-game-B7c5icho.js").then((n) => n.n)).createGame2D(o)))(opts);
159
+ const next = await (_gameFactory ?? (mode === "3d" ? async (o) => (await import("./create-game-DItIRyXH.js").then((n) => n.n)).createGame3D(o) : async (o) => (await import("./create-game-9KPppR0L.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
@@ -1980,6 +1982,8 @@ var Engine = class {
1980
1982
  step() {
1981
1983
  const scene = this._scene;
1982
1984
  if (!scene) return;
1985
+ this._time += this.fixedStep;
1986
+ this._unscaledTime += this.fixedStep;
1983
1987
  scene.tree.fixedUpdate(this.fixedStep);
1984
1988
  this.fixedUpdated.emit(this.fixedStep);
1985
1989
  scene.tree.update(this.fixedStep);
@@ -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-DMFJI-c_.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-DMFJI-c_.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-DMFJI-c_.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",