incanto 0.53.0 → 0.54.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 (55) hide show
  1. package/bin/incanto-check.mjs +56 -1
  2. package/dist/2d.d.ts +46 -3
  3. package/dist/2d.js +3 -3
  4. package/dist/3d.d.ts +105 -6
  5. package/dist/3d.js +10 -6
  6. package/dist/{behavior-Do0Da56m.d.ts → behavior-CyQoSu4n.d.ts} +114 -10
  7. package/dist/{create-game-9D87XaiX.js → create-game-BRt6XKmP.js} +10 -6
  8. package/dist/{create-game-CDJ1lVqK.js → create-game-Czzp6ZuE.js} +10 -6
  9. package/dist/debug.d.ts +1 -1
  10. package/dist/debug.js +1 -1
  11. package/dist/{environment-presets-SkGanr2s.js → environment-presets-BQ_QsIBY.js} +222 -11
  12. package/dist/{gameplay-CRYw_Q-T.js → gameplay-CZ2yq37J.js} +30 -21
  13. package/dist/gameplay.d.ts +8 -3
  14. package/dist/gameplay.js +1 -1
  15. package/dist/index.d.ts +6 -6
  16. package/dist/index.js +5 -5
  17. package/dist/{loader-Cu_7kJDy.d.ts → loader-DhI1jFW_.d.ts} +1 -1
  18. package/dist/net.d.ts +2 -2
  19. package/dist/net.js +1 -1
  20. package/dist/{particle-sim-BzJ1yxoE.d.ts → particle-sim-C5OfBbmU.d.ts} +11 -0
  21. package/dist/{pathfinding-HNGFqGUZ.d.ts → pathfinding-CXGCpRQe.d.ts} +1 -1
  22. package/dist/{physics-2d-BhZZ-HAp.js → physics-2d-CfWAggJ1.js} +1 -1
  23. package/dist/{physics-3d-DmyO2oaN.js → physics-3d-Bes-GIuR.js} +2 -2
  24. package/dist/react.d.ts +1 -1
  25. package/dist/react.js +1 -1
  26. package/dist/{register-DROK2l7J.js → register-en63AEZO.js} +163 -4
  27. package/dist/{register-BTomIiYG.js → register-p48lHE2o.js} +130 -13
  28. package/dist/{replay-9Fy6C10F.d.ts → replay-ePMz26jw.d.ts} +1 -1
  29. package/dist/{replay-DYNUL4BU.js → replay-t1pP0gQg.js} +1 -1
  30. package/dist/{split-screen-DNmcX1Pz.d.ts → split-screen-BsdOHbzP.d.ts} +1 -1
  31. package/dist/{split-screen-CaF9hO7g.js → split-screen-CSb_uZ6W.js} +1 -1
  32. package/dist/{sprite-animation-D_p28jwU.js → sprite-animation-7qvUxF6Z.js} +19 -0
  33. package/dist/{src-C9xyZW7M.js → src-vNPQeQ1W.js} +1 -1
  34. package/dist/{teardown-Bw2aeyGI.js → teardown-C7uVSJvx.js} +1 -1
  35. package/dist/{test-CJRsciFk.js → test-Tnf1nQl3.js} +24 -12
  36. package/dist/test.d.ts +4 -4
  37. package/dist/test.js +2 -2
  38. package/dist/vite.js +2 -2
  39. package/editor/assets/{agent8-PlFHzJsh.js → agent8-BWaW-D85.js} +1 -1
  40. package/editor/assets/{debug-CebV7CDW.js → debug-DLF8rUtr.js} +2 -2
  41. package/editor/assets/{index-CdbsY31G.js → index-BYfiwbQx.js} +91 -91
  42. package/editor/index.html +1 -1
  43. package/package.json +1 -1
  44. package/schemas/scene.schema.json +8 -0
  45. package/skills/incanto-assets.md +45 -2
  46. package/skills/incanto-building-2d-games.md +35 -0
  47. package/skills/incanto-building-3d-games.md +3 -1
  48. package/skills/incanto-environment.md +3 -1
  49. package/skills/incanto-gameplay-behaviors.md +51 -4
  50. package/skills/incanto-node-reference.md +2 -0
  51. package/skills/incanto-performance.md +47 -0
  52. package/skills/incanto-verifying-your-game.md +31 -0
  53. package/templates-app/beacon-isle-3d/package.json +1 -1
  54. package/templates-app/tps-3d/package.json +1 -1
  55. package/templates-app/village-quest-3d/package.json +1 -1
@@ -1,10 +1,10 @@
1
- import { t as registerCoreNodes } from "./register-BTomIiYG.js";
1
+ import { t as registerCoreNodes } from "./register-p48lHE2o.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-C7u42TID.js";
5
5
  import { t as createNoise2D } from "./noise-CGUMx44x.js";
6
- import { i as ParticleSim, n as resolveFrames, o as PARTICLE_PRESET_NAMES, r as validateAnimationAliases, s as applyParticlePreset, t as resolveAnimation } from "./sprite-animation-D_p28jwU.js";
7
- import { F as CharacterBody3D, I as PhysicsBody3D, L as RigidBody3D, N as WaterCutout3D, P as Area3D, R as StaticBody3D, U as colliderFootDrop, j as Water3D, z as Node3D } from "./gameplay-CRYw_Q-T.js";
6
+ import { i as ParticleSim, n as resolveFrames, o as PARTICLE_PRESET_NAMES, r as validateAnimationAliases, s as applyParticlePreset, t as resolveAnimation } from "./sprite-animation-7qvUxF6Z.js";
7
+ import { F as CharacterBody3D, I as PhysicsBody3D, L as RigidBody3D, N as WaterCutout3D, P as Area3D, R as StaticBody3D, U as colliderFootDrop, j as Water3D, z as Node3D } from "./gameplay-CZ2yq37J.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";
@@ -104,6 +104,25 @@ var TextureCache3D = class {
104
104
  return master ? master.clone() : null;
105
105
  }
106
106
  /**
107
+ * File a failure this store did not load itself.
108
+ *
109
+ * `Tree3D` keeps its own cache on purpose (it decodes off-thread and its
110
+ * page-session lifetime is load-bearing for the editor), which left its bark
111
+ * and leaf textures outside the one place `assetErrors()` reads. A 404'd leaf
112
+ * texture is a forest of bare branches and no message anywhere.
113
+ */
114
+ noteError(url, error) {
115
+ const key = `external|${url}`;
116
+ if (this.entries.has(key)) return;
117
+ const message = error instanceof Error ? error.message : String(error);
118
+ this.entries.set(key, {
119
+ url,
120
+ texture: null,
121
+ error: message
122
+ });
123
+ this.diagnostics?.warn(`texture failed to load: ${url} — ${message}`);
124
+ }
125
+ /**
107
126
  * What 404'd, by URL — reported raw, never by cache key, because an agent
108
127
  * greps this against the URL it wrote.
109
128
  */
@@ -5806,6 +5825,25 @@ var LoftMesh3D = class extends Node3D {
5806
5825
  m.castShadow = this.castShadow;
5807
5826
  m.receiveShadow = this.receiveShadow;
5808
5827
  }
5828
+ /**
5829
+ * Hand the GPU back.
5830
+ *
5831
+ * Every other node that builds geometry disposes it here; these four did not,
5832
+ * so a scene swap left their buffers and materials resident and a game with
5833
+ * level transitions climbed until the tab died — with `stats().geometries` the
5834
+ * only witness, and only if someone was watching it.
5835
+ */
5836
+ free() {
5837
+ const object = this._builtObject3D;
5838
+ if (object) object.traverse((child) => {
5839
+ const mesh = child;
5840
+ mesh.geometry?.dispose();
5841
+ const material = mesh.material;
5842
+ const list = Array.isArray(material) ? material : material ? [material] : [];
5843
+ for (const m of list) m.dispose?.();
5844
+ });
5845
+ super.free();
5846
+ }
5809
5847
  };
5810
5848
  function validateSections(sections, name) {
5811
5849
  const fail = (message) => {
@@ -6764,6 +6802,8 @@ var Particles3D = class Particles3D extends Node3D {
6764
6802
  options: ["custom", ...PARTICLE_PRESET_NAMES]
6765
6803
  },
6766
6804
  emitting: { default: true },
6805
+ /** Particles keep the place they were born instead of following the node. */
6806
+ worldSpace: { default: false },
6767
6807
  rate: { default: 40 },
6768
6808
  burst: { default: 0 },
6769
6809
  lifetime: { default: [.6, 1.2] },
@@ -6789,6 +6829,18 @@ var Particles3D = class Particles3D extends Node3D {
6789
6829
  };
6790
6830
  preset = "custom";
6791
6831
  emitting = true;
6832
+ /**
6833
+ * Where a particle LIVES once it is born.
6834
+ *
6835
+ * `false` (default) — the emitter's local space: the plume moves with the
6836
+ * node, which is what a torch on a moving platform wants.
6837
+ *
6838
+ * `true` — the world: a particle keeps the place it was born, so dust is left
6839
+ * BEHIND a running player instead of glued to it, and moving a one-shot to
6840
+ * the next explosion (the documented `p.position = …; p.replay()` recipe)
6841
+ * stops dragging the previous one across the level.
6842
+ */
6843
+ worldSpace = false;
6792
6844
  rate = 40;
6793
6845
  burst = 0;
6794
6846
  lifetime = [.6, 1.2];
@@ -6855,12 +6907,38 @@ var Particles3D = class Particles3D extends Node3D {
6855
6907
  }
6856
6908
  return this.sim;
6857
6909
  }
6910
+ /** Particles alive this frame — an emitter that is running, told from one
6911
+ * that is not. `framing()` says where it IS; this says whether it is going. */
6912
+ get aliveCount() {
6913
+ return this.sim?.count ?? 0;
6914
+ }
6915
+ /** A snapshot of the live particles — positions are in whichever space
6916
+ * `worldSpace` selects, relative to the emitter. */
6917
+ particles() {
6918
+ const out = [];
6919
+ this.sim?.forEach((p) => {
6920
+ out.push({ ...p });
6921
+ });
6922
+ return out;
6923
+ }
6924
+ /** The emitter's world position last frame, for world-space counter-motion. */
6925
+ lastWorld = null;
6926
+ /** Last frame's `emitting`, so turning one ON is a reportable event. */
6927
+ wasEmitting = false;
6858
6928
  update(dt) {
6859
6929
  const sim = this._ensureSim();
6930
+ this.holdWorldSpace(sim);
6860
6931
  sim.setRate(this.emitting ? this.rate : 0);
6861
- if (this.burst > 0 && !this.bursted) {
6932
+ if (this.burst > 0 && !this.bursted && (this.emitting || this.armedByReplay)) {
6862
6933
  this.bursted = true;
6934
+ this.armedByReplay = false;
6863
6935
  sim.burst(this.burst);
6936
+ this.report("burst", this.burst);
6937
+ }
6938
+ const on = this.emitting && this.rate > 0;
6939
+ if (on !== this.wasEmitting) {
6940
+ this.wasEmitting = on;
6941
+ if (on) this.report("emit", this.rate);
6864
6942
  }
6865
6943
  sim.update(dt);
6866
6944
  if ((!this.emitting || this.rate === 0) && this.bursted && sim.done && !this.finishedEmitted) {
@@ -6868,11 +6946,52 @@ var Particles3D = class Particles3D extends Node3D {
6868
6946
  this.emit("finished");
6869
6947
  }
6870
6948
  }
6949
+ /**
6950
+ * Undo the emitter's own motion on everything already alive.
6951
+ *
6952
+ * Ancestor-summed like the physics helpers, so it follows a moving PARENT
6953
+ * (dust under a player) as well as the node's own position.
6954
+ */
6955
+ holdWorldSpace(sim) {
6956
+ if (!this.worldSpace) {
6957
+ this.lastWorld = null;
6958
+ return;
6959
+ }
6960
+ let x = 0;
6961
+ let y = 0;
6962
+ let z = 0;
6963
+ for (let n = this; n; n = n.parent) if (n instanceof Node3D) {
6964
+ x += n.position[0] ?? 0;
6965
+ y += n.position[1] ?? 0;
6966
+ z += n.position[2] ?? 0;
6967
+ }
6968
+ const prev = this.lastWorld;
6969
+ if (prev) sim.translateAll(prev[0] - x, prev[1] - y, prev[2] - z);
6970
+ this.lastWorld = [
6971
+ x,
6972
+ y,
6973
+ z
6974
+ ];
6975
+ }
6976
+ /** Tell the engine an effect fired — the visual half of the audio record. */
6977
+ report(kind, amount) {
6978
+ const engine = this.tree?.engine;
6979
+ engine?.effects.record({
6980
+ kind,
6981
+ name: this.preset,
6982
+ from: this.getPath(),
6983
+ amount,
6984
+ at: engine.time
6985
+ });
6986
+ }
6871
6987
  /** Restart a one-shot (re-burst + allow finished to fire again). */
6872
6988
  replay() {
6873
6989
  this.bursted = false;
6874
6990
  this.finishedEmitted = false;
6991
+ this.armedByReplay = true;
6875
6992
  }
6993
+ /** `replay()` asked for this burst, so `emitting: false` does not hold it. */
6994
+ armedByReplay = false;
6876
6995
  points = null;
6877
6996
  positions = null;
6878
6997
  colors = null;
@@ -6958,6 +7077,25 @@ var Particles3D = class Particles3D extends Node3D {
6958
7077
  geometry.getAttribute("position").needsUpdate = true;
6959
7078
  geometry.getAttribute("color").needsUpdate = true;
6960
7079
  }
7080
+ /**
7081
+ * Hand the GPU back.
7082
+ *
7083
+ * Every other node that builds geometry disposes it here; these four did not,
7084
+ * so a scene swap left their buffers and materials resident and a game with
7085
+ * level transitions climbed until the tab died — with `stats().geometries` the
7086
+ * only witness, and only if someone was watching it.
7087
+ */
7088
+ free() {
7089
+ const object = this._builtObject3D;
7090
+ if (object) object.traverse((child) => {
7091
+ const mesh = child;
7092
+ mesh.geometry?.dispose();
7093
+ const material = mesh.material;
7094
+ const list = Array.isArray(material) ? material : material ? [material] : [];
7095
+ for (const m of list) m.dispose?.();
7096
+ });
7097
+ super.free();
7098
+ }
6961
7099
  };
6962
7100
  const colorScratchA = new Color();
6963
7101
  const colorScratchB = new Color();
@@ -9008,8 +9146,21 @@ var Trail3D = class extends Node3D {
9008
9146
  group.add(this.mesh);
9009
9147
  return group;
9010
9148
  }
9149
+ /** Last frame's `emitting`, so laying ribbon is a reportable event. */
9150
+ wasEmitting = false;
9011
9151
  update(dt) {
9012
9152
  this.clock += dt;
9153
+ if (this.emitting !== this.wasEmitting) {
9154
+ this.wasEmitting = this.emitting;
9155
+ const engine = this.tree?.engine;
9156
+ if (this.emitting && engine) engine.effects.record({
9157
+ kind: "trail",
9158
+ name: this.color,
9159
+ from: this.getPath(),
9160
+ amount: this.seconds,
9161
+ at: engine.time
9162
+ });
9163
+ }
9013
9164
  while (this.points.length > 0 && this.clock - this.points[0].t > this.seconds) this.points.shift();
9014
9165
  }
9015
9166
  _onRender3D(ctx) {
@@ -9066,10 +9217,38 @@ var Trail3D = class extends Node3D {
9066
9217
  }
9067
9218
  geometry.setDrawRange(0, (n - 1) * 6);
9068
9219
  }
9069
- /** Test hook: recorded point count. */
9220
+ /**
9221
+ * How many points the ribbon is made of.
9222
+ *
9223
+ * ZERO with no renderer, and that is not a bug: the ribbon is laid in the
9224
+ * render sync, because each point needs the node's real world transform (a
9225
+ * sword arc is a ROTATING parent, which an ancestor position sum cannot see).
9226
+ * Assert it from a browser check; read a headless 0 as "nothing rendered"
9227
+ * rather than "my trail is broken", and use the `trail` effect record for the
9228
+ * wiring question.
9229
+ */
9070
9230
  get pointCount() {
9071
9231
  return this.points.length;
9072
9232
  }
9233
+ /**
9234
+ * Hand the GPU back.
9235
+ *
9236
+ * Every other node that builds geometry disposes it here; these four did not,
9237
+ * so a scene swap left their buffers and materials resident and a game with
9238
+ * level transitions climbed until the tab died — with `stats().geometries` the
9239
+ * only witness, and only if someone was watching it.
9240
+ */
9241
+ free() {
9242
+ const object = this._builtObject3D;
9243
+ if (object) object.traverse((child) => {
9244
+ const mesh = child;
9245
+ mesh.geometry?.dispose();
9246
+ const material = mesh.material;
9247
+ const list = Array.isArray(material) ? material : material ? [material] : [];
9248
+ for (const m of list) m.dispose?.();
9249
+ });
9250
+ super.free();
9251
+ }
9073
9252
  };
9074
9253
  const worldScratch = new Vector3();
9075
9254
  const dirScratch = new Vector3();
@@ -10863,10 +11042,23 @@ var Tree3D = class Tree3D extends Node3D {
10863
11042
  this.leafFadeUniform.end.value = this.leafFadeEnd;
10864
11043
  }
10865
11044
  }
10866
- _syncObject3D() {
10867
- super._syncObject3D();
11045
+ /** The scene's asset store, so a 404'd bark or leaf reaches assetErrors(). */
11046
+ assets = null;
11047
+ _syncObject3D(alpha = 1, assets = null) {
11048
+ super._syncObject3D(alpha, assets);
11049
+ this.assets = assets;
10868
11050
  this.rebuildIfNeeded();
10869
11051
  }
11052
+ /**
11053
+ * File a texture failure where the ladder reads it.
11054
+ *
11055
+ * This node keeps its own cache on purpose, and that left its textures
11056
+ * outside the one place `assetErrors()` looks — so a leaf URL that 404s was a
11057
+ * forest of bare branches with no message anywhere.
11058
+ */
11059
+ noteTextureError = (url, error) => {
11060
+ this.assets?.textures.noteError(url, error);
11061
+ };
10870
11062
  /** Re-grow only when a grove-shaping prop changed (the JSON IS the grove). */
10871
11063
  rebuildIfNeeded() {
10872
11064
  if (!this.grove) {
@@ -10975,7 +11167,7 @@ var Tree3D = class Tree3D extends Node3D {
10975
11167
  if (loader) material.map = loadSharedTexture(loader, url, (texture) => {
10976
11168
  texture.colorSpace = SRGBColorSpace;
10977
11169
  texture.flipY = false;
10978
- }, (texture) => greenMatte(texture));
11170
+ }, (texture) => greenMatte(texture), this.noteTextureError);
10979
11171
  wireLeafWind(material, this.swayUniform, LEAF_ENV_CUT, this.leafFadeUniform ?? void 0);
10980
11172
  leaves = new InstancedMesh(geometry.leaves, material, instances);
10981
11173
  leaves.userData.incantoWaterPassSkip = true;
@@ -11040,7 +11232,7 @@ var Tree3D = class Tree3D extends Node3D {
11040
11232
  texture.anisotropy = 4;
11041
11233
  texture.flipY = false;
11042
11234
  if (srgb) texture.colorSpace = SRGBColorSpace;
11043
- });
11235
+ }, void 0, this.noteTextureError);
11044
11236
  }
11045
11237
  /** Per-instance placement + jitter — ONE rng draw order for both paths. */
11046
11238
  scatter(rng, place) {
@@ -11098,7 +11290,7 @@ var Tree3D = class Tree3D extends Node3D {
11098
11290
  * grove color VARIATION lives on the material tint, not the shared map.)
11099
11291
  */
11100
11292
  const sharedTextures = /* @__PURE__ */ new Map();
11101
- function loadSharedTexture(loader, url, configure, onReady) {
11293
+ function loadSharedTexture(loader, url, configure, onReady, onError) {
11102
11294
  const cached = sharedTextures.get(url);
11103
11295
  if (cached) return cached;
11104
11296
  const texture = new Texture();
@@ -11107,7 +11299,7 @@ function loadSharedTexture(loader, url, configure, onReady) {
11107
11299
  texture.image = bitmap;
11108
11300
  if (onReady) onReady(texture);
11109
11301
  else texture.needsUpdate = true;
11110
- });
11302
+ }, void 0, (error) => onError?.(url, error));
11111
11303
  sharedTextures.set(url, texture);
11112
11304
  return texture;
11113
11305
  }
@@ -12169,6 +12361,25 @@ var VoxelGrid3D = class extends Node3D {
12169
12361
  this.mesh.count = i;
12170
12362
  this.mesh.instanceMatrix.needsUpdate = true;
12171
12363
  }
12364
+ /**
12365
+ * Hand the GPU back.
12366
+ *
12367
+ * Every other node that builds geometry disposes it here; these four did not,
12368
+ * so a scene swap left their buffers and materials resident and a game with
12369
+ * level transitions climbed until the tab died — with `stats().geometries` the
12370
+ * only witness, and only if someone was watching it.
12371
+ */
12372
+ free() {
12373
+ const object = this._builtObject3D;
12374
+ if (object) object.traverse((child) => {
12375
+ const mesh = child;
12376
+ mesh.geometry?.dispose();
12377
+ const material = mesh.material;
12378
+ const list = Array.isArray(material) ? material : material ? [material] : [];
12379
+ for (const m of list) m.dispose?.();
12380
+ });
12381
+ super.free();
12382
+ }
12172
12383
  };
12173
12384
  const FACE_ATTRS = [
12174
12385
  "colorFront",
@@ -5242,6 +5242,16 @@ var Cooldown = class {
5242
5242
  }
5243
5243
  };
5244
5244
  const SHAKE_FALLOFF_SECONDS = .28;
5245
+ /** File the effect with the engine, if this one is attached to a running game. */
5246
+ function recordEffect(engine, kind, name, from, amount) {
5247
+ engine?.effects.record({
5248
+ kind,
5249
+ name,
5250
+ from,
5251
+ amount,
5252
+ at: engine.time
5253
+ });
5254
+ }
5245
5255
  /**
5246
5256
  * Camera shake as a standalone behavior for cameras WITHOUT another script.
5247
5257
  * (FollowCamera has this built in — one script per node.) Composes with any
@@ -5273,6 +5283,7 @@ var CameraShake = class extends Behavior {
5273
5283
  if (this.engine?.settings.get("reduceMotion") === true) return;
5274
5284
  this.magnitude = Math.max(this.magnitude, magnitude);
5275
5285
  this.t = seconds ?? this.falloff;
5286
+ recordEffect(this.engine, "shake", "", this.node?.getPath() ?? "engine", magnitude);
5276
5287
  }
5277
5288
  update(dt) {
5278
5289
  const node = requirePosition(this);
@@ -5300,9 +5311,15 @@ var CameraShake = class extends Behavior {
5300
5311
  };
5301
5312
  /**
5302
5313
  * Full-screen color flash (damage red, pickup white). DOM overlay above
5303
- * everything; headless no-op. Repeated calls restart the fade.
5314
+ * everything; headless it draws nothing but still REPORTS, so a damage flash
5315
+ * can be checked in a run with no screen (`engine.effects.countOf('#ff0000')`).
5316
+ *
5317
+ * The engine is the first argument because it is the only juice primitive that
5318
+ * did not take one — which is also why nothing in the repo called it, and why a
5319
+ * flash was the one piece of feedback no test could see.
5304
5320
  */
5305
- function screenFlash(color = "#ffffff", opacity = .35, seconds = .25) {
5321
+ function screenFlash(engine, color = "#ffffff", opacity = .35, seconds = .25) {
5322
+ recordEffect(engine, "flash", color, "engine", opacity);
5306
5323
  if (typeof document === "undefined") return;
5307
5324
  const id = "incanto-screen-flash";
5308
5325
  let el = document.getElementById(id);
@@ -5327,36 +5344,28 @@ function screenFlash(color = "#ffffff", opacity = .35, seconds = .25) {
5327
5344
  * previous timeScale. Stacking calls extend the freeze instead of fighting.
5328
5345
  */
5329
5346
  function hitStop(engine, seconds = .08) {
5347
+ recordEffect(engine, "hitstop", "", "engine", seconds);
5330
5348
  const state = hitStops.get(engine);
5331
5349
  if (state) {
5332
- state.until = Math.max(state.until, realNow() + seconds * 1e3);
5350
+ state.until = Math.max(state.until, engine.unscaledTime + seconds);
5333
5351
  return;
5334
5352
  }
5335
5353
  const restore = engine.timeScale;
5336
5354
  const entry = {
5337
- until: realNow() + seconds * 1e3,
5338
- restore
5355
+ until: engine.unscaledTime + seconds,
5356
+ restore,
5357
+ release: () => {}
5339
5358
  };
5340
5359
  hitStops.set(engine, entry);
5341
5360
  engine.timeScale = 0;
5342
- const pump = () => {
5343
- if (realNow() >= entry.until) {
5344
- engine.timeScale = entry.restore;
5345
- hitStops.delete(engine);
5346
- return;
5347
- }
5348
- schedule(pump);
5349
- };
5350
- schedule(pump);
5361
+ entry.release = engine.updated.connect(() => {
5362
+ if (engine.unscaledTime < entry.until) return;
5363
+ engine.timeScale = entry.restore;
5364
+ entry.release();
5365
+ hitStops.delete(engine);
5366
+ });
5351
5367
  }
5352
5368
  const hitStops = /* @__PURE__ */ new Map();
5353
- function realNow() {
5354
- return typeof performance !== "undefined" ? performance.now() : Date.now();
5355
- }
5356
- function schedule(fn) {
5357
- if (typeof requestAnimationFrame === "function") requestAnimationFrame(fn);
5358
- else setTimeout(fn, 16);
5359
- }
5360
5369
  //#endregion
5361
5370
  //#region src/gameplay/lifetime.ts
5362
5371
  /**
@@ -1,4 +1,4 @@
1
- import { Dt as Node, b as Engine, d as PropSchema, n as BehaviorCtor, t as Behavior } from "./behavior-Do0Da56m.js";
1
+ import { At as Node, b as Engine, d as PropSchema, n as BehaviorCtor, t as Behavior } from "./behavior-CyQoSu4n.js";
2
2
  import { c as JsonValue, s as JsonObject } from "./schema-CFeioQRE.js";
3
3
 
4
4
  //#region src/gameplay/chase.d.ts
@@ -813,9 +813,14 @@ declare class CameraShake extends Behavior {
813
813
  }
814
814
  /**
815
815
  * Full-screen color flash (damage red, pickup white). DOM overlay above
816
- * everything; headless no-op. Repeated calls restart the fade.
816
+ * everything; headless it draws nothing but still REPORTS, so a damage flash
817
+ * can be checked in a run with no screen (`engine.effects.countOf('#ff0000')`).
818
+ *
819
+ * The engine is the first argument because it is the only juice primitive that
820
+ * did not take one — which is also why nothing in the repo called it, and why a
821
+ * flash was the one piece of feedback no test could see.
817
822
  */
818
- declare function screenFlash(color?: string, opacity?: number, seconds?: number): void;
823
+ declare function screenFlash(engine: Engine, color?: string, opacity?: number, seconds?: number): void;
819
824
  /**
820
825
  * Hit-stop: freeze game time for `seconds` of REAL time, then restore the
821
826
  * previous timeScale. Stacking calls extend the freeze instead of fighting.
package/dist/gameplay.js CHANGED
@@ -1,2 +1,2 @@
1
- import { A as Chase, C as restartScene, D as DamageOnContact, E as phaseOf, O as Health, S as goToScene, T as DayNight, _ as Cooldown, a as Wander, b as Interactable, c as SavePoint, d as Patrol, f as PathFollow, g as CameraShake, h as Lifetime, i as WaveSpawner, k as Collector, l as Projectile, m as MoveTo, n as registerGameplayBehaviors, o as Spawner, p as Oscillate, r as ZombieAI, s as ScoreKeeper, t as GAMEPLAY_BEHAVIORS, u as Pickup, v as hitStop, w as FollowCamera, x as GameFlow, y as screenFlash } from "./gameplay-CRYw_Q-T.js";
1
+ import { A as Chase, C as restartScene, D as DamageOnContact, E as phaseOf, O as Health, S as goToScene, T as DayNight, _ as Cooldown, a as Wander, b as Interactable, c as SavePoint, d as Patrol, f as PathFollow, g as CameraShake, h as Lifetime, i as WaveSpawner, k as Collector, l as Projectile, m as MoveTo, n as registerGameplayBehaviors, o as Spawner, p as Oscillate, r as ZombieAI, s as ScoreKeeper, t as GAMEPLAY_BEHAVIORS, u as Pickup, v as hitStop, w as FollowCamera, x as GameFlow, y as screenFlash } from "./gameplay-CZ2yq37J.js";
2
2
  export { CameraShake, Chase, Collector, Cooldown, DamageOnContact, DayNight, FollowCamera, GAMEPLAY_BEHAVIORS, GameFlow, Health, Interactable, Lifetime, MoveTo, Oscillate, PathFollow, Patrol, Pickup, Projectile, SavePoint, ScoreKeeper, Spawner, Wander, WaveSpawner, ZombieAI, goToScene, hitStop, phaseOf, registerGameplayBehaviors, restartScene, screenFlash };
package/dist/index.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- import { $ as T_PREFIX, A as SettingsValues, At as LogEntry, B as captureBehaviors, C as EngineStats, Ct as MusicTrack, Dt as Node, E as DeviceHints, Et as BusName, F as BehaviorState, G as ORDER_GROUP_BASE, H as savesWithoutUid, I as RestoreReport, J as effectiveOrder, K as OrderGroup, L as SaveSlot, M as readDeviceHints, Mt as LogManager, N as suggestQuality, Nt as Signal, O as QualityTier, Ot as NodeLifecycle, P as Scene, Pt as SignalListener, Q as Localization, R as SaveSlots, S as Scheduler, St as MusicManager, T as RendererStats, Tt as AudioBuses, U as SaveStore, V as restoreBehaviors, W as createSaveStore, X as BASE_LOCALE, Y as resolveOrderGroups, Z as LocaleTables, _ as mergeStaticSignals, _t as SfxParams, a as clearBehaviors, at as isAudioContextAvailable, b as Engine, bt as synthSfx, c as registeredBehaviors, ct as Listener, d as PropSchema, dt as SpatialParams, et as suggestLocale, f as clearRegistry, ft as Vec3, g as getNodeType, gt as SFX_PRESET_NAMES, h as getNodeSignals, ht as SFX_PRESETS, i as behaviorSignals, it as SfxPlayOptions, j as qualityEnvironment, jt as LogLevel, k as Settings, kt as SceneTree, l as NodeCtor, lt as ROLLOFF_MODELS, m as getNodeSchema, mt as spatialPan, n as BehaviorCtor, nt as InputMap, o as getBehavior, ot as Voice, p as createNode, pt as spatialGain, q as OrderGroupTable, r as behaviorSchema, rt as SfxEngine, s as registerBehavior, st as VoicePreset, t as Behavior, tt as translationKey, u as PropDef, ut as RolloffModel, v as registerNode, vt as SfxWave, w as GameStats, wt as PlayMusicOptions, x as EngineOptions, xt as MusicBackend, y as registeredTypes, yt as SynthOptions, z as behaviorsWithoutSave } from "./behavior-Do0Da56m.js";
1
+ import { $ as T_PREFIX, A as SettingsValues, At as Node, B as captureBehaviors, C as EngineStats, Ct as synthSfx, Dt as PlayMusicOptions, E as DeviceHints, Et as MusicTrack, F as BehaviorState, Ft as LogManager, G as ORDER_GROUP_BASE, H as savesWithoutUid, I as RestoreReport, It as Signal, J as effectiveOrder, K as OrderGroup, L as SaveSlot, Lt as SignalListener, M as readDeviceHints, Mt as SceneTree, N as suggestQuality, Nt as LogEntry, O as QualityTier, Ot as AudioBuses, P as Scene, Pt as LogLevel, Q as Localization, R as SaveSlots, S as Scheduler, St as SynthOptions, T as RendererStats, Tt as MusicManager, U as SaveStore, V as restoreBehaviors, W as createSaveStore, X as BASE_LOCALE, Y as resolveOrderGroups, Z as LocaleTables, _ as mergeStaticSignals, _t as spatialPan, a as clearBehaviors, at as EffectLog, b as Engine, bt as SfxParams, c as registeredBehaviors, ct as isAudioContextAvailable, d as PropSchema, dt as Listener, et as suggestLocale, f as clearRegistry, ft as ROLLOFF_MODELS, g as getNodeType, gt as spatialGain, h as getNodeSignals, ht as Vec3, i as behaviorSignals, it as EffectKind, j as qualityEnvironment, jt as NodeLifecycle, k as Settings, kt as BusName, l as NodeCtor, lt as Voice, m as getNodeSchema, mt as SpatialParams, n as BehaviorCtor, nt as InputMap, o as getBehavior, ot as SfxEngine, p as createNode, pt as RolloffModel, q as OrderGroupTable, r as behaviorSchema, rt as EffectEvent, s as registerBehavior, st as SfxPlayOptions, t as Behavior, tt as translationKey, u as PropDef, ut as VoicePreset, v as registerNode, vt as SFX_PRESETS, w as GameStats, wt as MusicBackend, x as EngineOptions, xt as SfxWave, y as registeredTypes, yt as SFX_PRESET_NAMES, z as behaviorsWithoutSave } from "./behavior-CyQoSu4n.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-Cu_7kJDy.js";
3
+ import { n as loadScene, t as LoadSceneOptions } from "./loader-DhI1jFW_.js";
4
4
  import { i as resolveFrames, n as AnimationEntry, r as resolveAnimation, t as AnimationDef } from "./sprite-animation-CMr6f1K2.js";
5
- import { n as ParticleSimConfig, r as ParticleView, t as ParticleSim } from "./particle-sim-BzJ1yxoE.js";
6
- import { a as AudioElementLike, i as gridFromRows, n as PathGrid, o as AudioPlayer, r as findPath, t as FindPathOptions } from "./pathfinding-HNGFqGUZ.js";
7
- 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-9Fy6C10F.js";
5
+ import { n as ParticleSimConfig, r as ParticleView, t as ParticleSim } from "./particle-sim-C5OfBbmU.js";
6
+ import { a as AudioElementLike, i as gridFromRows, n as PathGrid, o as AudioPlayer, r as findPath, t as FindPathOptions } from "./pathfinding-CXGCpRQe.js";
7
+ 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-ePMz26jw.js";
8
8
 
9
9
  //#region src/core/audio/crossfade.d.ts
10
10
  /**
@@ -1089,4 +1089,4 @@ declare function computeViewport(canvasW: number, canvasH: number, viewport: {
1089
1089
  /** Engine version. Kept in sync with package.json by the release pipeline. */
1090
1090
  declare const VERSION: string;
1091
1091
  //#endregion
1092
- export { type AnimationDef, type AnimationEntry, type AssetFailure, AudioBuses, type AudioElementLike, AudioPlayer, BASE_LOCALE, Behavior, type BehaviorCtor, type BehaviorState, type BusName, CONST_REF_KEY, type ComputedViewport, type ConnectionJson, type CrossfadeGains, type DebugLineSource, type DeviceHints, type DriveScript, type DriveStep, Engine, type EngineOptions, type EngineStats, type FindPathOptions, type GameStats, type GroupedLog, type HudAnchor, HudLayer, IncantoError, type IncantoErrorCode, type IncantoErrorDetails, InputMap, type JsonKind, type JsonObject, type JsonValue, type Listener, type LiveInput, type LiveReport, type LoadSceneOptions, type LocaleTables, Localization, type LogEntry, type LogLevel, LogManager, type MusicBackend, MusicManager, type MusicTrack, Node, type NodeCtor, type NodeJson, type NodeLifecycle, ORDER_GROUP_BASE, type OrderGroup, type OrderGroupTable, PARTICLE_PRESETS, PARTICLE_PRESET_NAMES, type ParsedNodePath, type ParticlePresetValues, ParticleSim, type ParticleSimConfig, type ParticleView, type PathGrid, type PlayMusicOptions, type PreloadResult, type PropDef, type PropSchema, type QualityTier, ROLLOFF_MODELS, type Recorder, type RefNode, type RefProblem, type RefResult, type RendererStats, type ReplayEvent, type ReplayJson, type ResolvedRendering, type ResolvedViewport, type RestoreReport, Rng, type RolloffModel, SCENE_FORMAT, SFX_PRESETS, SFX_PRESET_NAMES, type SaveSlot, SaveSlots, type SaveStore, Scene, type SceneJson, SceneTree, type Scheduler, Settings, type SettingsValues, SfxEngine, type SfxParams, type SfxPlayOptions, type SfxWave, Signal, type SignalListener, type SpatialParams, type SynthOptions, T_PREFIX, Timer, TouchControls, type TouchControlsOptions, UiBanner, UiBar, UiButton, UiDialogue, UiFrameCapSelect, UiImage, UiLanguageSelect, UiMuteToggle, UiPanel, UiQualitySelect, UiRenderScaleSelect, UiSelect, UiSlider, UiText, UiToggle, UiVolumeSlider, VERSION, type Vec3, type ViewportFit, type Voice, type VoicePreset, WebAudioMusicBackend, applyParticlePreset, assetUrls, attachTouchControls, auditScene, behaviorSchema, behaviorSignals, behaviorsWithoutSave, captureBehaviors, clearBehaviors, clearRegistry, computeViewport, createNode, createNoise2D, createSaveStore, crossfadeGains, describeRefProblem, duplicateNode, effectiveOrder, fadeGain, findPath, getBehavior, getNodeSchema, getNodeSignals, getNodeType, gridFromRows, isAudioContextAvailable, isConstRef, joystickVector, jsonClone, jsonEquals, jsonKind, loadScene, logReport, logText, mergeStaticSignals, newUid, nodeRefWarnings, parseDrive, parseNodePath, preloadUrls, qualityEnvironment, readDeviceHints, registerBehavior, registerCoreNodes, registerNode, registeredBehaviors, registeredTypes, replay, resolveAnimation, resolveConstants, resolveFrames, resolveOrderGroups, resolveRefInJson, resolveRendering, resolveViewport, restoreBehaviors, savesWithoutUid, serializeNode, spatialGain, spatialPan, startRecording, suggestLocale, suggestQuality, synthSfx, translationKey };
1092
+ export { type AnimationDef, type AnimationEntry, type AssetFailure, AudioBuses, type AudioElementLike, AudioPlayer, BASE_LOCALE, Behavior, type BehaviorCtor, type BehaviorState, type BusName, CONST_REF_KEY, type ComputedViewport, type ConnectionJson, type CrossfadeGains, type DebugLineSource, type DeviceHints, type DriveScript, type DriveStep, type EffectEvent, type EffectKind, EffectLog, Engine, type EngineOptions, type EngineStats, type FindPathOptions, type GameStats, type GroupedLog, type HudAnchor, HudLayer, IncantoError, type IncantoErrorCode, type IncantoErrorDetails, InputMap, type JsonKind, type JsonObject, type JsonValue, type Listener, type LiveInput, type LiveReport, type LoadSceneOptions, type LocaleTables, Localization, type LogEntry, type LogLevel, LogManager, type MusicBackend, MusicManager, type MusicTrack, Node, type NodeCtor, type NodeJson, type NodeLifecycle, ORDER_GROUP_BASE, type OrderGroup, type OrderGroupTable, PARTICLE_PRESETS, PARTICLE_PRESET_NAMES, type ParsedNodePath, type ParticlePresetValues, ParticleSim, type ParticleSimConfig, type ParticleView, type PathGrid, type PlayMusicOptions, type PreloadResult, type PropDef, type PropSchema, type QualityTier, ROLLOFF_MODELS, type Recorder, type RefNode, type RefProblem, type RefResult, type RendererStats, type ReplayEvent, type ReplayJson, type ResolvedRendering, type ResolvedViewport, type RestoreReport, Rng, type RolloffModel, SCENE_FORMAT, SFX_PRESETS, SFX_PRESET_NAMES, type SaveSlot, SaveSlots, type SaveStore, Scene, type SceneJson, SceneTree, type Scheduler, Settings, type SettingsValues, SfxEngine, type SfxParams, type SfxPlayOptions, type SfxWave, Signal, type SignalListener, type SpatialParams, type SynthOptions, T_PREFIX, Timer, TouchControls, type TouchControlsOptions, UiBanner, UiBar, UiButton, UiDialogue, UiFrameCapSelect, UiImage, UiLanguageSelect, UiMuteToggle, UiPanel, UiQualitySelect, UiRenderScaleSelect, UiSelect, UiSlider, UiText, UiToggle, UiVolumeSlider, VERSION, type Vec3, type ViewportFit, type Voice, type VoicePreset, WebAudioMusicBackend, applyParticlePreset, assetUrls, attachTouchControls, auditScene, behaviorSchema, behaviorSignals, behaviorsWithoutSave, captureBehaviors, clearBehaviors, clearRegistry, computeViewport, createNode, createNoise2D, createSaveStore, crossfadeGains, describeRefProblem, duplicateNode, effectiveOrder, fadeGain, findPath, getBehavior, getNodeSchema, getNodeSignals, getNodeType, gridFromRows, isAudioContextAvailable, isConstRef, joystickVector, jsonClone, jsonEquals, jsonKind, loadScene, logReport, logText, mergeStaticSignals, newUid, nodeRefWarnings, parseDrive, parseNodePath, preloadUrls, qualityEnvironment, readDeviceHints, registerBehavior, registerCoreNodes, registerNode, registeredBehaviors, registeredTypes, replay, resolveAnimation, resolveConstants, resolveFrames, resolveOrderGroups, resolveRefInJson, resolveRendering, resolveViewport, restoreBehaviors, savesWithoutUid, serializeNode, spatialGain, spatialPan, startRecording, suggestLocale, suggestQuality, synthSfx, translationKey };
package/dist/index.js CHANGED
@@ -1,14 +1,14 @@
1
1
  import { A as registerBehavior, D as behaviorSignals, E as behaviorSchema, M as Signal, O as clearBehaviors, S as InputMap, T as Behavior, _ as savesWithoutUid, a as SCENE_FORMAT, b as effectiveOrder, c as SceneTree, d as resolveConstants, f as Node, g as restoreBehaviors, h as captureBehaviors, i as serializeNode, j as registeredBehaviors, k as getBehavior, l as CONST_REF_KEY, m as behaviorsWithoutSave, n as loadScene, o as computeViewport, p as SaveSlots, r as Scene, s as resolveViewport, u as isConstRef, v as createSaveStore, w as parseNodePath, x as resolveOrderGroups, y as ORDER_GROUP_BASE } from "./loader-BTkHYrQn.js";
2
- import { A as Localization, B as SFX_PRESET_NAMES, D as suggestQuality, E as readDeviceHints, F as isAudioContextAvailable, G as fadeGain, H as MusicManager, I as ROLLOFF_MODELS, K as AudioBuses, L as spatialGain, M as suggestLocale, N as translationKey, O as LogManager, P as SfxEngine, R as spatialPan, S as Settings, U as WebAudioMusicBackend, V as synthSfx, W as crossfadeGains, _ as UiBanner, a as UiMuteToggle, b as AudioPlayer, c as UiRenderScaleSelect, d as UiToggle, f as UiVolumeSlider, g as HudLayer, h as UiDialogue, i as UiLanguageSelect, j as T_PREFIX, k as BASE_LOCALE, l as UiSelect, m as UiButton, n as UiFrameCapSelect, o as UiPanel, p as Timer, r as UiImage, s as UiQualitySelect, t as registerCoreNodes, u as UiSlider, v as UiBar, w as qualityEnvironment, x as Engine, y as UiText, z as SFX_PRESETS } from "./register-BTomIiYG.js";
2
+ import { A as BASE_LOCALE, B as SFX_PRESETS, D as suggestQuality, E as readDeviceHints, F as SfxEngine, G as crossfadeGains, H as synthSfx, I as isAudioContextAvailable, K as fadeGain, L as ROLLOFF_MODELS, M as T_PREFIX, N as suggestLocale, O as LogManager, P as translationKey, R as spatialGain, S as Settings, U as MusicManager, V as SFX_PRESET_NAMES, W as WebAudioMusicBackend, _ as UiBanner, a as UiMuteToggle, b as AudioPlayer, c as UiRenderScaleSelect, d as UiToggle, f as UiVolumeSlider, g as HudLayer, h as UiDialogue, i as UiLanguageSelect, j as Localization, k as EffectLog, l as UiSelect, m as UiButton, n as UiFrameCapSelect, o as UiPanel, p as Timer, q as AudioBuses, r as UiImage, s as UiQualitySelect, t as registerCoreNodes, u as UiSlider, v as UiBar, w as qualityEnvironment, x as Engine, y as UiText, z as spatialPan } from "./register-p48lHE2o.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 { n as jsonEquals, r as jsonKind, t as jsonClone } from "./json-BLk7H2Qa.js";
6
6
  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-C7u42TID.js";
7
- import { a as nodeRefWarnings, i as describeRefProblem, n as startRecording, o as resolveRefInJson, r as auditScene, t as replay } from "./replay-DYNUL4BU.js";
7
+ import { a as nodeRefWarnings, i as describeRefProblem, n as startRecording, o as resolveRefInJson, r as auditScene, t as replay } from "./replay-t1pP0gQg.js";
8
8
  import { n as logText, r as parseDrive, t as logReport } from "./log-report-CPFm4OXf.js";
9
9
  import { t as createNoise2D } from "./noise-CGUMx44x.js";
10
- import { a as PARTICLE_PRESETS, i as ParticleSim, n as resolveFrames, o as PARTICLE_PRESET_NAMES, s as applyParticlePreset, t as resolveAnimation } from "./sprite-animation-D_p28jwU.js";
11
- import { a as findPath, i as preloadUrls, n as newUid, o as gridFromRows, r as assetUrls, t as VERSION } from "./src-C9xyZW7M.js";
10
+ import { a as PARTICLE_PRESETS, i as ParticleSim, n as resolveFrames, o as PARTICLE_PRESET_NAMES, s as applyParticlePreset, t as resolveAnimation } from "./sprite-animation-7qvUxF6Z.js";
11
+ import { a as findPath, i as preloadUrls, n as newUid, o as gridFromRows, r as assetUrls, t as VERSION } from "./src-vNPQeQ1W.js";
12
12
  import { i as resolveRendering, n as attachTouchControls, r as joystickVector, t as TouchControls } from "./touch-BnMyy9tr.js";
13
13
  import { t as duplicateNode } from "./duplicate-MNLMAcbz.js";
14
- 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, UiFrameCapSelect, UiImage, UiLanguageSelect, UiMuteToggle, UiPanel, UiQualitySelect, UiRenderScaleSelect, UiSelect, UiSlider, UiText, UiToggle, UiVolumeSlider, VERSION, WebAudioMusicBackend, applyParticlePreset, assetUrls, attachTouchControls, auditScene, behaviorSchema, behaviorSignals, behaviorsWithoutSave, captureBehaviors, clearBehaviors, clearRegistry, computeViewport, createNode, createNoise2D, createSaveStore, crossfadeGains, describeRefProblem, duplicateNode, effectiveOrder, fadeGain, findPath, getBehavior, getNodeSchema, getNodeSignals, getNodeType, gridFromRows, isAudioContextAvailable, isConstRef, joystickVector, jsonClone, jsonEquals, jsonKind, loadScene, logReport, logText, mergeStaticSignals, newUid, nodeRefWarnings, parseDrive, parseNodePath, preloadUrls, qualityEnvironment, readDeviceHints, registerBehavior, registerCoreNodes, registerNode, registeredBehaviors, registeredTypes, replay, resolveAnimation, resolveConstants, resolveFrames, resolveOrderGroups, resolveRefInJson, resolveRendering, resolveViewport, restoreBehaviors, savesWithoutUid, serializeNode, spatialGain, spatialPan, startRecording, suggestLocale, suggestQuality, synthSfx, translationKey };
14
+ export { AudioBuses, AudioPlayer, BASE_LOCALE, Behavior, CONST_REF_KEY, EffectLog, 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, UiFrameCapSelect, UiImage, UiLanguageSelect, UiMuteToggle, UiPanel, UiQualitySelect, UiRenderScaleSelect, UiSelect, UiSlider, UiText, UiToggle, UiVolumeSlider, VERSION, WebAudioMusicBackend, applyParticlePreset, assetUrls, attachTouchControls, auditScene, behaviorSchema, behaviorSignals, behaviorsWithoutSave, captureBehaviors, clearBehaviors, clearRegistry, computeViewport, createNode, createNoise2D, createSaveStore, crossfadeGains, describeRefProblem, duplicateNode, effectiveOrder, fadeGain, findPath, getBehavior, getNodeSchema, getNodeSignals, getNodeType, gridFromRows, isAudioContextAvailable, isConstRef, joystickVector, jsonClone, jsonEquals, jsonKind, loadScene, logReport, logText, mergeStaticSignals, newUid, nodeRefWarnings, parseDrive, parseNodePath, preloadUrls, qualityEnvironment, readDeviceHints, registerBehavior, registerCoreNodes, registerNode, registeredBehaviors, registeredTypes, replay, resolveAnimation, resolveConstants, resolveFrames, resolveOrderGroups, resolveRefInJson, resolveRendering, resolveViewport, restoreBehaviors, savesWithoutUid, serializeNode, spatialGain, spatialPan, startRecording, suggestLocale, suggestQuality, synthSfx, translationKey };
@@ -1,4 +1,4 @@
1
- import { P as Scene, b as Engine } from "./behavior-Do0Da56m.js";
1
+ import { P as Scene, b as Engine } from "./behavior-CyQoSu4n.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,5 +1,5 @@
1
- import { Dt as Node, d as PropSchema } from "./behavior-Do0Da56m.js";
2
- import { a as NetworkManagerOptions, c as LocalGameServer, d as ServerClass, f as createLocalGameServer, g as Unsubscribe, h as NetworkTransport, i as NetworkManager, l as LocalGameServerOptions, m as LoopbackTransport, n as SplitScreenPlayer, o as applySyncPatch, p as LoopbackHub, r as createSplitScreen, s as CollectionOptions, t as SplitScreenOptions, u as LocalGameServerTransport } from "./split-screen-DNmcX1Pz.js";
1
+ import { At as Node, d as PropSchema } from "./behavior-CyQoSu4n.js";
2
+ import { a as NetworkManagerOptions, c as LocalGameServer, d as ServerClass, f as createLocalGameServer, g as Unsubscribe, h as NetworkTransport, i as NetworkManager, l as LocalGameServerOptions, m as LoopbackTransport, n as SplitScreenPlayer, o as applySyncPatch, p as LoopbackHub, r as createSplitScreen, s as CollectionOptions, t as SplitScreenOptions, u as LocalGameServerTransport } from "./split-screen-BsdOHbzP.js";
3
3
 
4
4
  //#region src/net/agent8.d.ts
5
5
  /**
package/dist/net.js CHANGED
@@ -1,3 +1,3 @@
1
1
  import { n as createAgent8Server } from "./agent8-CvsfVskX.js";
2
- import { a as applySyncPatch, c as createLocalGameServer, i as NetworkManager, l as LoopbackHub, n as registerNodesNet, o as LocalGameServer, r as NetworkSpawner, s as LocalGameServerTransport, t as createSplitScreen, u as LoopbackTransport } from "./split-screen-CaF9hO7g.js";
2
+ import { a as applySyncPatch, c as createLocalGameServer, i as NetworkManager, l as LoopbackHub, n as registerNodesNet, o as LocalGameServer, r as NetworkSpawner, s as LocalGameServerTransport, t as createSplitScreen, u as LoopbackTransport } from "./split-screen-CSb_uZ6W.js";
3
3
  export { LocalGameServer, LocalGameServerTransport, LoopbackHub, LoopbackTransport, NetworkManager, NetworkSpawner, applySyncPatch, createAgent8Server, createLocalGameServer, createSplitScreen, registerNodesNet };
@@ -49,6 +49,17 @@ declare class ParticleSim {
49
49
  private spawnAccumulator;
50
50
  private everSpawned;
51
51
  constructor(config: ParticleSimConfig, rng: Rng);
52
+ /**
53
+ * Shift every live particle — how world-space emission is done.
54
+ *
55
+ * The pool is in the emitter's LOCAL space, so a moving emitter drags its
56
+ * whole plume with it: dust glued to a running player instead of left behind,
57
+ * and the documented "move the emitter and replay" recipe for a one-shot
58
+ * teleporting the previous explosion across the level. Counter-translating by
59
+ * the emitter's own motion each frame leaves the particles where they were
60
+ * born, exactly, and costs one pass over the live ones.
61
+ */
62
+ translateAll(dx: number, dy: number, dz: number): void;
52
63
  /** Change the emission rate LIVE (particles/sec; 0 pauses emission) — lets an
53
64
  * emitter toggle on/off at runtime (drift smoke, throttle flames). */
54
65
  setRate(rate: number): void;
@@ -1,4 +1,4 @@
1
- import { Dt as Node, ct as Listener, d as PropSchema } from "./behavior-Do0Da56m.js";
1
+ import { At as Node, d as PropSchema, dt as Listener } from "./behavior-CyQoSu4n.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 { C as diagnose } from "./loader-BTkHYrQn.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-DROK2l7J.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-en63AEZO.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 { C as diagnose } from "./loader-BTkHYrQn.js";
3
3
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
4
- import { B as validateCollider3D, F as CharacterBody3D, I as PhysicsBody3D, L as RigidBody3D, P as Area3D, z as Node3D } from "./gameplay-CRYw_Q-T.js";
4
+ import { B as validateCollider3D, F as CharacterBody3D, I as PhysicsBody3D, L as RigidBody3D, P as Area3D, z as Node3D } from "./gameplay-CZ2yq37J.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-SkGanr2s.js";
6
+ import { A as Terrain3D, F as InstancedMesh3D, L as buildMeshGeometry, P as Joint3D } from "./environment-presets-BQ_QsIBY.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-Do0Da56m.js";
1
+ import { P as Scene, b as Engine, n as BehaviorCtor } from "./behavior-CyQoSu4n.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-CDJ1lVqK.js").then((n) => n.n)).createGame3D(o) : async (o) => (await import("./create-game-9D87XaiX.js").then((n) => n.n)).createGame2D(o)))(opts);
159
+ const next = await (_gameFactory ?? (mode === "3d" ? async (o) => (await import("./create-game-Czzp6ZuE.js").then((n) => n.n)).createGame3D(o) : async (o) => (await import("./create-game-BRt6XKmP.js").then((n) => n.n)).createGame2D(o)))(opts);
160
160
  if (disposed) {
161
161
  next.dispose();
162
162
  return;