incanto 0.34.0 → 0.35.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 (58) hide show
  1. package/dist/2d.d.ts +5 -5
  2. package/dist/2d.js +3 -3
  3. package/dist/3d.d.ts +122 -14
  4. package/dist/3d.js +5 -5
  5. package/dist/{audit-C4kmDK0o.js → audit-C5oZGAru.js} +37 -0
  6. package/dist/{behavior-CtPScDMo.d.ts → behavior-CGSCWOHB.d.ts} +310 -22
  7. package/dist/{create-game-CFNqoqOX.js → create-game-B0Wfhi2-.js} +6 -5
  8. package/dist/{create-game-QTfghzwM.js → create-game-BUD89Kqh.js} +20 -6
  9. package/dist/debug.d.ts +1 -1
  10. package/dist/{duplicate-DlOvknDO.js → duplicate-C716f-97.js} +1 -1
  11. package/dist/{editor-switch-BJb-CWfA.d.ts → editor-switch-DyXEtH36.d.ts} +1 -1
  12. package/dist/editor.js +1739 -1312
  13. package/dist/env.d.ts +1 -1
  14. package/dist/{environment-presets-B6WiUsaO.js → environment-presets-TAGvmM3z.js} +240 -74
  15. package/dist/{errors-1dXlIwoR.d.ts → errors-BY2kL0hv.d.ts} +1 -1
  16. package/dist/{gameplay-BxFtmfSe.js → gameplay-BvhcQbfJ.js} +75 -11
  17. package/dist/gameplay.d.ts +12 -5
  18. package/dist/gameplay.js +1 -1
  19. package/dist/index.d.ts +22 -7
  20. package/dist/index.js +6 -6
  21. package/dist/{loader-DmXfj6Uv.d.ts → loader-CUcj00M8.d.ts} +19 -2
  22. package/dist/{loader-BwR0DxeM.js → loader-Mig5fY4n.js} +97 -18
  23. package/dist/net.d.ts +2 -2
  24. package/dist/net.js +3 -3
  25. package/dist/{particle-sim-CwJ5rI_P.d.ts → particle-sim-B-vZBF5R.d.ts} +1 -1
  26. package/dist/{pathfinding-DRCe89SI.d.ts → pathfinding-DgOo2KNF.d.ts} +1 -1
  27. package/dist/{physics-2d-BuyAwsW5.js → physics-2d-DqAclql-.js} +22 -9
  28. package/dist/{physics-3d-C8Kn_Nyb.js → physics-3d-DxBH4sIF.js} +27 -11
  29. package/dist/react.d.ts +2 -2
  30. package/dist/react.js +1 -1
  31. package/dist/{register-G3edsjJ2.js → register-BFLg0-_i.js} +490 -12
  32. package/dist/{register-OodmuUMK.js → register-BjbPMA5B.js} +2 -2
  33. package/dist/{register-BLPC10Mj.js → register-DSmIRAf7.js} +11 -5
  34. package/dist/{schema-CcoWb32N.d.ts → schema-3ywbdlrv.d.ts} +10 -0
  35. package/dist/{test-C4B5znap.js → test-9EokzbRd.js} +10 -10
  36. package/dist/test.d.ts +4 -4
  37. package/dist/test.js +2 -2
  38. package/dist/vite.js +1 -1
  39. package/editor/assets/{agent8-BTODHtdM.js → agent8-D3_GWeuh.js} +1 -1
  40. package/editor/assets/{debug-CJEz4EwC.js → debug-CX0LDd1r.js} +1 -1
  41. package/editor/assets/index-CMlKFT0C.js +10773 -0
  42. package/editor/index.html +1 -1
  43. package/package.json +1 -1
  44. package/schemas/scene.schema.json +109 -0
  45. package/skills/incanto-assets.md +21 -0
  46. package/skills/incanto-behaviors-and-scripts.md +16 -0
  47. package/skills/incanto-editor.md +17 -1
  48. package/skills/incanto-gameplay-behaviors.md +6 -0
  49. package/skills/incanto-hud.md +16 -0
  50. package/skills/incanto-localization.md +132 -0
  51. package/skills/incanto-node-reference.md +33 -18
  52. package/skills/incanto-save-slots.md +152 -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/PROJECT/Context.md +16 -0
  56. package/templates-app/village-quest-3d/package.json +1 -1
  57. package/templates-app/village-quest-3d/src/village.scene.json +25 -3
  58. package/editor/assets/index-BYCso8Bf.js +0 -10696
@@ -1,5 +1,5 @@
1
- import { at as Node, l as PropSchema, n as BehaviorCtor, t as Behavior, v as Engine } from "./behavior-CtPScDMo.js";
2
- import { c as JsonValue, s as JsonObject } from "./schema-CcoWb32N.js";
1
+ import { b as Engine, bt as Node, d as PropSchema, n as BehaviorCtor, t as Behavior } from "./behavior-CGSCWOHB.js";
2
+ import { c as JsonValue, s as JsonObject } from "./schema-3ywbdlrv.js";
3
3
 
4
4
  //#region src/gameplay/chase.d.ts
5
5
  /**
@@ -51,6 +51,9 @@ declare class Collector extends Behavior {
51
51
  override onReady(): void;
52
52
  /** Add `value` to the tally and emit `totalChanged`. */
53
53
  collect(value: number): void;
54
+ /** How many have been picked up so far. */
55
+ override serialize(): JsonValue;
56
+ override deserialize(data: JsonValue): void;
54
57
  }
55
58
  //#endregion
56
59
  //#region src/gameplay/health.d.ts
@@ -96,6 +99,9 @@ declare class Health extends Behavior {
96
99
  kill(): void;
97
100
  private setCurrent;
98
101
  private die;
102
+ /** Current HP and whether death already fired — `max` comes back from JSON. */
103
+ override serialize(): JsonValue;
104
+ override deserialize(data: JsonValue): void;
99
105
  }
100
106
  //#endregion
101
107
  //#region src/gameplay/damage-on-contact.d.ts
@@ -377,6 +383,9 @@ declare class ScoreKeeper extends Behavior {
377
383
  setScore(value: number): void;
378
384
  /** Lose one life (no-op when lives disabled or already lost). Emits `lifeLost`, then `lost` at 0. */
379
385
  loseLife(): void;
386
+ /** The run's progress. `scoreToWin` is balance and comes back from JSON. */
387
+ override serialize(): JsonValue;
388
+ override deserialize(data: JsonValue): void;
380
389
  }
381
390
  //#endregion
382
391
  //#region src/gameplay/spawner.d.ts
@@ -606,9 +615,7 @@ declare function goToScene(engine: Engine, sceneJson: JsonObject, opts?: {
606
615
  */
607
616
  declare class GameFlow extends Behavior {
608
617
  static readonly signals: readonly string[];
609
- static readonly props: Record<string, {
610
- default: string | boolean;
611
- }>;
618
+ static readonly props: PropSchema;
612
619
  /** Input action that restarts from gameover/won (declare it in `input{}`). */
613
620
  restartAction: string;
614
621
  /** Freeze `engine.timeScale` on gameover/won (banner UI keeps rendering). */
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-BxFtmfSe.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-BvhcQbfJ.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 MusicBackend, A as suggestQuality, B as Listener, C as RendererStats, D as SettingsValues, E as Settings, F as SfxEngine, G as spatialGain, H as RolloffModel, I as SfxPlayOptions, J as SFX_PRESET_NAMES, K as spatialPan, L as isAudioContextAvailable, M as createSaveStore, N as Scene, O as qualityEnvironment, P as InputMap, Q as synthSfx, R as Voice, S as GameStats, T as QualityTier, U as SpatialParams, V as ROLLOFF_MODELS, W as Vec3, X as SfxWave, Y as SfxParams, Z as SynthOptions, _ as registeredTypes, a as registerBehavior, at as Node, b as Scheduler, c as PropDef, ct as LogEntry, d as createNode, dt as Signal, et as MusicManager, f as getNodeSchema, ft as SignalListener, g as registerNode, h as mergeStaticSignals, i as getBehavior, it as BusName, j as SaveStore, k as readDeviceHints, l as PropSchema, lt as LogLevel, m as getNodeType, n as BehaviorCtor, nt as PlayMusicOptions, o as registeredBehaviors, ot as NodeLifecycle, p as getNodeSignals, q as SFX_PRESETS, r as clearBehaviors, rt as AudioBuses, s as NodeCtor, st as SceneTree, t as Behavior, tt as MusicTrack, u as clearRegistry, ut as LogManager, v as Engine, w as DeviceHints, x as EngineStats, y as EngineOptions, z as VoicePreset } from "./behavior-CtPScDMo.js";
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-CcoWb32N.js";
3
- import { n as loadScene, t as LoadSceneOptions } from "./loader-DmXfj6Uv.js";
4
- import { n as ParticleSimConfig, r as ParticleView, t as ParticleSim } from "./particle-sim-CwJ5rI_P.js";
5
- import { a as AudioElementLike, i as gridFromRows, n as PathGrid, o as AudioPlayer, r as findPath, t as FindPathOptions } from "./pathfinding-DRCe89SI.js";
6
- import { i as auditScene, n as IncantoErrorCode, r as IncantoErrorDetails, t as IncantoError } from "./errors-1dXlIwoR.js";
1
+ import { $ as Voice, A as qualityEnvironment, B as restoreBehaviors, C as EngineStats, Ct as LogEntry, D as QualityTier, Dt as SignalListener, E as DeviceHints, Et as Signal, F as RestoreReport, G as Localization, H as createSaveStore, I as SaveSlot, J as translationKey, K as T_PREFIX, L as SaveSlots, M as suggestQuality, N as Scene, O as Settings, P as BehaviorState, Q as isAudioContextAvailable, R as behaviorsWithoutSave, S as Scheduler, St as SceneTree, T as RendererStats, Tt as LogManager, U as BASE_LOCALE, V as SaveStore, W as LocaleTables, X as SfxEngine, Y as InputMap, Z as SfxPlayOptions, _ as mergeStaticSignals, _t as PlayMusicOptions, a as clearBehaviors, at as Vec3, b as Engine, bt as Node, c as registeredBehaviors, ct as SFX_PRESETS, d as PropSchema, dt as SfxWave, et as VoicePreset, f as clearRegistry, ft as SynthOptions, g as getNodeType, gt as MusicTrack, h as getNodeSignals, ht as MusicManager, i as behaviorSignals, it as SpatialParams, j as readDeviceHints, k as SettingsValues, l as NodeCtor, lt as SFX_PRESET_NAMES, m as getNodeSchema, mt as MusicBackend, n as BehaviorCtor, nt as ROLLOFF_MODELS, o as getBehavior, ot as spatialGain, p as createNode, pt as synthSfx, q as suggestLocale, r as behaviorSchema, rt as RolloffModel, s as registerBehavior, st as spatialPan, t as Behavior, tt as Listener, u as PropDef, ut as SfxParams, v as registerNode, vt as AudioBuses, w as GameStats, wt as LogLevel, x as EngineOptions, xt as NodeLifecycle, y as registeredTypes, yt as BusName, z as captureBehaviors } from "./behavior-CGSCWOHB.js";
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-3ywbdlrv.js";
3
+ import { n as loadScene, t as LoadSceneOptions } from "./loader-CUcj00M8.js";
4
+ import { n as ParticleSimConfig, r as ParticleView, t as ParticleSim } from "./particle-sim-B-vZBF5R.js";
5
+ import { a as AudioElementLike, i as gridFromRows, n as PathGrid, o as AudioPlayer, r as findPath, t as FindPathOptions } from "./pathfinding-DgOo2KNF.js";
6
+ import { i as auditScene, n as IncantoErrorCode, r as IncantoErrorDetails, t as IncantoError } from "./errors-BY2kL0hv.js";
7
7
 
8
8
  //#region src/core/audio/crossfade.d.ts
9
9
  /**
@@ -204,6 +204,15 @@ declare abstract class HudWidgetBase extends Node {
204
204
  override onExitTree(): void;
205
205
  override update(_dt: number): void;
206
206
  protected _sync(): void;
207
+ /**
208
+ * Resolve a text prop through the engine's locale.
209
+ *
210
+ * Called every frame from `_sync`, which is what makes switching language
211
+ * repaint with nothing to invalidate: widgets already re-read their props, so
212
+ * the new string simply appears on the next frame. A plain string costs one
213
+ * `startsWith` and is returned untouched.
214
+ */
215
+ protected _t(value: string): string;
207
216
  }
208
217
  /** A text line (score, timer, hints). Set `.text` from behaviors. */
209
218
  declare class UiText extends HudWidgetBase {
@@ -231,6 +240,8 @@ declare class UiBar extends HudWidgetBase {
231
240
  background: string;
232
241
  label: string;
233
242
  private fill;
243
+ private tag;
244
+ private lastLabel;
234
245
  private lastRatio;
235
246
  /** Current fill ratio 0..1 (what the bar shows). */
236
247
  get ratio(): number;
@@ -469,9 +480,13 @@ declare class UiSelect extends HudWidgetBase {
469
480
  /** Left/right (and confirm) walk the list — a select has no other gesture. */
470
481
  override _activate(dir: number): void;
471
482
  private select;
483
+ private labelEl;
484
+ private lastLabel;
472
485
  private lastOptions;
473
486
  private last;
474
487
  protected _build(): HTMLElement;
488
+ /** Override point: what an option READS as, when it differs from its value. */
489
+ protected _optionLabel(value: string): string;
475
490
  private fillOptions;
476
491
  protected override _sync(): void;
477
492
  }
@@ -758,4 +773,4 @@ declare function computeViewport(canvasW: number, canvasH: number, viewport: {
758
773
  /** Engine version. Kept in sync with package.json by the release pipeline. */
759
774
  declare const VERSION: string;
760
775
  //#endregion
761
- export { AudioBuses, type AudioElementLike, AudioPlayer, Behavior, type BehaviorCtor, type BusName, CONST_REF_KEY, type ComputedViewport, type ConnectionJson, type CrossfadeGains, type DebugLineSource, type DeviceHints, Engine, type EngineOptions, type EngineStats, type FindPathOptions, type GameStats, type HudAnchor, HudLayer, IncantoError, type IncantoErrorCode, type IncantoErrorDetails, InputMap, type JsonKind, type JsonObject, type JsonValue, type Listener, type LoadSceneOptions, type LogEntry, type LogLevel, LogManager, type MusicBackend, MusicManager, type MusicTrack, Node, type NodeCtor, type NodeJson, type NodeLifecycle, ORDER_GROUP_BASE, type OrderGroup, 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 RendererStats, type ReplayEvent, type ReplayJson, type ResolvedRendering, type ResolvedViewport, Rng, type RolloffModel, SCENE_FORMAT, SFX_PRESETS, SFX_PRESET_NAMES, 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, Timer, TouchControls, type TouchControlsOptions, UiBanner, UiBar, UiButton, UiDialogue, UiImage, UiPanel, UiSelect, UiSlider, UiText, UiToggle, VERSION, type Vec3, type ViewportFit, type Voice, type VoicePreset, WebAudioMusicBackend, applyParticlePreset, assetUrls, attachTouchControls, auditScene, 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, resolveRendering, resolveViewport, serializeNode, spatialGain, spatialPan, startRecording, suggestQuality, synthSfx };
776
+ export { 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, Engine, type EngineOptions, type EngineStats, type FindPathOptions, type GameStats, type HudAnchor, HudLayer, IncantoError, type IncantoErrorCode, type IncantoErrorDetails, InputMap, type JsonKind, type JsonObject, type JsonValue, type Listener, 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, 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 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, UiImage, UiPanel, UiSelect, UiSlider, UiText, UiToggle, 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, 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, resolveRendering, resolveViewport, restoreBehaviors, serializeNode, spatialGain, spatialPan, startRecording, suggestLocale, suggestQuality, synthSfx, translationKey };
package/dist/index.js CHANGED
@@ -1,12 +1,12 @@
1
- import { _ as clearBehaviors, a as SCENE_FORMAT, b as registeredBehaviors, c as SceneTree, d as resolveConstants, f as Node, g as Behavior, h as InputMap, i as serializeNode, l as CONST_REF_KEY, n as loadScene, o as computeViewport, p as parseNodePath, r as Scene, s as resolveViewport, u as isConstRef, v as getBehavior, x as Signal, y as registerBehavior } from "./loader-BwR0DxeM.js";
2
- import { A as MusicManager, C as isAudioContextAvailable, D as SFX_PRESETS, E as spatialPan, M as crossfadeGains, N as fadeGain, O as SFX_PRESET_NAMES, P as AudioBuses, S as SfxEngine, T as spatialGain, _ as qualityEnvironment, a as UiSlider, b as createSaveStore, c as UiButton, d as UiBanner, f as UiBar, g as Settings, h as Engine, i as UiSelect, j as WebAudioMusicBackend, k as synthSfx, 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 readDeviceHints, w as ROLLOFF_MODELS, x as LogManager, y as suggestQuality } from "./register-G3edsjJ2.js";
1
+ import { C as Signal, S as registeredBehaviors, _ as behaviorSchema, a as SCENE_FORMAT, b as getBehavior, c as SceneTree, d as resolveConstants, f as Node, g as Behavior, i as serializeNode, l as CONST_REF_KEY, m as InputMap, n as loadScene, o as computeViewport, p as parseNodePath, r as Scene, s as resolveViewport, u as isConstRef, v as behaviorSignals, x as registerBehavior, y as clearBehaviors } from "./loader-Mig5fY4n.js";
2
+ import { A as translationKey, B as WebAudioMusicBackend, C as restoreBehaviors, D as Localization, E as BASE_LOCALE, F as spatialPan, H as fadeGain, I as SFX_PRESETS, L as SFX_PRESET_NAMES, M as isAudioContextAvailable, N as ROLLOFF_MODELS, O as T_PREFIX, P as spatialGain, R as synthSfx, S as captureBehaviors, T as LogManager, U as AudioBuses, V as crossfadeGains, _ as qualityEnvironment, a as UiSlider, b as SaveSlots, c as UiButton, d as UiBanner, f as UiBar, g as Settings, h as Engine, i as UiSelect, j as SfxEngine, k as suggestLocale, 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 readDeviceHints, w as createSaveStore, x as behaviorsWithoutSave, y as suggestQuality, z as MusicManager } from "./register-BFLg0-_i.js";
3
3
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
4
4
  import { t as Rng } from "./rng-DP-SR7eg.js";
5
- import { t as auditScene } from "./audit-C4kmDK0o.js";
5
+ import { t as auditScene } from "./audit-C5oZGAru.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-CJdGpT2V.js";
8
8
  import { t as createNoise2D } from "./noise-CGUMx44x.js";
9
- import { n as ORDER_GROUP_BASE, r as effectiveOrder, t as duplicateNode } from "./duplicate-DlOvknDO.js";
9
+ import { n as ORDER_GROUP_BASE, r as effectiveOrder, t as duplicateNode } from "./duplicate-C716f-97.js";
10
10
  import { i as applyParticlePreset, n as PARTICLE_PRESETS, r as PARTICLE_PRESET_NAMES, t as ParticleSim } from "./particle-sim-Bw7hB93B.js";
11
11
  import { i as resolveRendering, n as attachTouchControls, r as joystickVector, t as TouchControls } from "./touch-BoNg_MnF.js";
12
12
  //#region src/core/pathfinding.ts
@@ -242,6 +242,6 @@ function newUid() {
242
242
  //#endregion
243
243
  //#region src/index.ts
244
244
  /** Engine version. Kept in sync with package.json by the release pipeline. */
245
- const VERSION = "0.34.0";
245
+ const VERSION = "0.35.0";
246
246
  //#endregion
247
- export { AudioBuses, AudioPlayer, Behavior, CONST_REF_KEY, Engine, HudLayer, IncantoError, InputMap, LogManager, MusicManager, Node, ORDER_GROUP_BASE, PARTICLE_PRESETS, PARTICLE_PRESET_NAMES, ParticleSim, ROLLOFF_MODELS, Rng, SCENE_FORMAT, SFX_PRESETS, SFX_PRESET_NAMES, Scene, SceneTree, Settings, SfxEngine, Signal, Timer, TouchControls, UiBanner, UiBar, UiButton, UiDialogue, UiImage, UiPanel, UiSelect, UiSlider, UiText, UiToggle, VERSION, WebAudioMusicBackend, applyParticlePreset, assetUrls, attachTouchControls, auditScene, 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, resolveRendering, resolveViewport, serializeNode, spatialGain, spatialPan, startRecording, suggestQuality, synthSfx };
247
+ 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, resolveRendering, resolveViewport, restoreBehaviors, serializeNode, spatialGain, spatialPan, startRecording, suggestLocale, suggestQuality, synthSfx, translationKey };
@@ -1,5 +1,5 @@
1
- import { N as Scene, v as Engine } from "./behavior-CtPScDMo.js";
2
- import { i as SceneJson } from "./schema-CcoWb32N.js";
1
+ import { N as Scene, b as Engine } from "./behavior-CGSCWOHB.js";
2
+ import { i as SceneJson } from "./schema-3ywbdlrv.js";
3
3
 
4
4
  //#region src/core/scene/loader.d.ts
5
5
  interface LoadSceneOptions {
@@ -20,6 +20,23 @@ interface LoadSceneOptions {
20
20
  */
21
21
  stubMissingBehaviors?: boolean;
22
22
  /**
23
+ * Editor mode: EVERY script becomes a no-op stub, registered or not.
24
+ *
25
+ * The editor must not run the game while you edit it — a behavior's `onReady`
26
+ * moving the player, or `update` walking it off the terrain, is not something
27
+ * an edit view should do. It used to get that by deleting `script` outright,
28
+ * which also meant deleting `connections` (a behavior handler cannot resolve
29
+ * on a node with no behavior) — so the editor validated a scene the game
30
+ * would reject, and reported green.
31
+ *
32
+ * Stubbing instead keeps connections loadable and hard-validated: endpoints
33
+ * must still resolve, so DANGLING_CONNECTION fires in the editor exactly
34
+ * where it fires in the game. Handlers are accepted (they live in the game's
35
+ * TypeScript, which the editor does not load) and props are not applied.
36
+ * Games should NOT set this.
37
+ */
38
+ stubAllBehaviors?: boolean;
39
+ /**
23
40
  * Attach the engine BEFORE onEnterTree/onReady fire, so behaviors can use
24
41
  * `this.engine` / `this.rng` / `this.log` in onReady. `createGame` does
25
42
  * this for you; pair it with `engine.setScene(scene)` afterwards.
@@ -103,6 +103,23 @@ function getBehavior(name) {
103
103
  }
104
104
  return ctor;
105
105
  }
106
+ /**
107
+ * A registered behavior's merged prop schema, `{}` for one nobody registered.
108
+ *
109
+ * The node side has had `getNodeSchema` for a long time; a behavior's props were
110
+ * reachable only by whoever already held the class, which a tool reading scene
111
+ * JSON never does. Deliberately non-throwing: a tool inspecting a scene that
112
+ * names a behavior from a game it did not load should degrade, not blow up.
113
+ */
114
+ function behaviorSchema(name) {
115
+ const ctor = behaviors.get(name);
116
+ return ctor ? mergeStaticProps(ctor) : {};
117
+ }
118
+ /** A registered behavior's signals (own + inherited), `[]` for an unknown one. */
119
+ function behaviorSignals(name) {
120
+ const ctor = behaviors.get(name);
121
+ return ctor ? mergeStaticSignals(ctor) : [];
122
+ }
106
123
  function registeredBehaviors() {
107
124
  return [...behaviors.keys()];
108
125
  }
@@ -118,6 +135,14 @@ function createBehavior(name, props) {
118
135
  return behavior;
119
136
  }
120
137
  //#endregion
138
+ //#region src/core/diagnostics.ts
139
+ function diagnose(engine, level, ...parts) {
140
+ engine?.log[level](...parts);
141
+ if (level === "error") console.error(...parts);
142
+ else if (level === "warn") console.warn(...parts);
143
+ else console.info(...parts);
144
+ }
145
+ //#endregion
121
146
  //#region src/core/input.ts
122
147
  /** Keys typed into editable elements belong to the page UI, not the game. */
123
148
  function isEditableTarget(target) {
@@ -572,14 +597,6 @@ function parseInputDecls(decls) {
572
597
  return out;
573
598
  }
574
599
  //#endregion
575
- //#region src/core/diagnostics.ts
576
- function diagnose(engine, level, ...parts) {
577
- engine?.log[level](...parts);
578
- if (level === "error") console.error(...parts);
579
- else if (level === "warn") console.warn(...parts);
580
- else console.info(...parts);
581
- }
582
- //#endregion
583
600
  //#region src/core/node-path.ts
584
601
  function parseNodePath(path) {
585
602
  if (path === "") throw new IncantoError("BAD_NODE_PATH", "Node path must not be empty.");
@@ -657,10 +674,7 @@ var Node = class {
657
674
  const old = this._name;
658
675
  if (this._parent) this._name = uniqueSiblingName(value, this._parent._children, this);
659
676
  else this._name = value;
660
- if (this._tree && this._name !== old) {
661
- this._tree._unindexName(this, old);
662
- this._tree._indexName(this);
663
- }
677
+ if (this._tree && this._name !== old) this._tree._reindexName(this, old);
664
678
  }
665
679
  get parent() {
666
680
  return this._parent;
@@ -1055,7 +1069,7 @@ function resolveConstants(value, constants) {
1055
1069
  *
1056
1070
  * Headless by design — tests step it manually; the render loop (M2) calls it.
1057
1071
  */
1058
- var SceneTree = class {
1072
+ var SceneTree = class SceneTree {
1059
1073
  _root = null;
1060
1074
  _engine = null;
1061
1075
  freeQueue = /* @__PURE__ */ new Set();
@@ -1071,6 +1085,47 @@ var SceneTree = class {
1071
1085
  * change shape?" check for per-step body gathers and similar caches.
1072
1086
  */
1073
1087
  _structureVersion = 0;
1088
+ /**
1089
+ * WHAT changed, not just that something did.
1090
+ *
1091
+ * The version alone forces every consumer into a full-tree re-walk, and the
1092
+ * physics step is gated on it: one bullet spawned per frame means
1093
+ * `collectBodies` + `collectJoints` + `syncScatters` walk the entire scene at
1094
+ * 60 Hz. A bullet-hell or a survivors-like does nothing else all game.
1095
+ *
1096
+ * This log lets a consumer replay just the attaches and detaches it has not
1097
+ * seen. It is bounded — a scene load appends thousands of entries and nobody
1098
+ * benefits from replaying those — so a consumer that falls behind
1099
+ * `_structureLogBase` takes the full walk, exactly as before. Correctness does
1100
+ * not depend on the log; only the fast path does.
1101
+ */
1102
+ _structureLog = [];
1103
+ /** The `_structureVersion` the first entry in the log corresponds to. */
1104
+ _structureLogBase = 0;
1105
+ static STRUCTURE_LOG_MAX = 2048;
1106
+ /**
1107
+ * Attaches and detaches since `sinceVersion`, or null when the log no longer
1108
+ * reaches back that far (take the full walk).
1109
+ *
1110
+ * The returned array is the live buffer's tail — do not hold it across frames.
1111
+ */
1112
+ _structureSince(sinceVersion) {
1113
+ if (sinceVersion > this._structureVersion) return null;
1114
+ if (sinceVersion < this._structureLogBase) return null;
1115
+ return this._structureLog.slice(sinceVersion - this._structureLogBase);
1116
+ }
1117
+ _logStructure(node, added) {
1118
+ this._structureVersion++;
1119
+ this._structureLog.push({
1120
+ node,
1121
+ added
1122
+ });
1123
+ const overflow = this._structureLog.length - SceneTree.STRUCTURE_LOG_MAX;
1124
+ if (overflow > 0) {
1125
+ this._structureLog.splice(0, overflow);
1126
+ this._structureLogBase += overflow;
1127
+ }
1128
+ }
1074
1129
  _frameId = 0;
1075
1130
  /**
1076
1131
  * Monotonic update-frame counter — bumped once per `update()`. Per-frame
@@ -1082,7 +1137,7 @@ var SceneTree = class {
1082
1137
  }
1083
1138
  /** @internal Node attach hook. */
1084
1139
  _indexName(node) {
1085
- this._structureVersion++;
1140
+ this._logStructure(node, true);
1086
1141
  let set = this._nameIndex.get(node.name);
1087
1142
  if (!set) {
1088
1143
  set = /* @__PURE__ */ new Set();
@@ -1090,9 +1145,29 @@ var SceneTree = class {
1090
1145
  }
1091
1146
  set.add(node);
1092
1147
  }
1093
- /** @internal Node detach/rename hook (`name` = the entry to remove). */
1148
+ /**
1149
+ * @internal A RENAME: same node, same place, different key in the name index.
1150
+ *
1151
+ * Deliberately not logged as a detach + attach. It is not a structural change,
1152
+ * and treating it as one would make the physics step tear down and rebuild the
1153
+ * node's Rapier body for a name edit.
1154
+ */
1155
+ _reindexName(node, oldName) {
1156
+ const set = this._nameIndex.get(oldName);
1157
+ if (set) {
1158
+ set.delete(node);
1159
+ if (set.size === 0) this._nameIndex.delete(oldName);
1160
+ }
1161
+ let next = this._nameIndex.get(node.name);
1162
+ if (!next) {
1163
+ next = /* @__PURE__ */ new Set();
1164
+ this._nameIndex.set(node.name, next);
1165
+ }
1166
+ next.add(node);
1167
+ }
1168
+ /** @internal Node detach hook (`name` = the entry to remove). */
1094
1169
  _unindexName(node, name = node.name) {
1095
- this._structureVersion++;
1170
+ this._logStructure(node, false);
1096
1171
  const set = this._nameIndex.get(name);
1097
1172
  if (set) {
1098
1173
  set.delete(node);
@@ -1291,6 +1366,8 @@ var Scene = class {
1291
1366
  constants;
1292
1367
  /** Input action declarations (loaded into `Engine.input` on setScene). */
1293
1368
  input;
1369
+ /** Translations by locale (merged into `Engine.locale` on setScene). */
1370
+ strings;
1294
1371
  /** Physics config (gravity etc. — interpreted by the physics modules). */
1295
1372
  physics;
1296
1373
  /** Multiplayer config (room etc. — interpreted by incanto/net). */
@@ -1309,6 +1386,7 @@ var Scene = class {
1309
1386
  this.assets = source.assets ? jsonClone(source.assets) : void 0;
1310
1387
  this.constants = source.constants ? jsonClone(source.constants) : void 0;
1311
1388
  this.input = source.input ? jsonClone(source.input) : void 0;
1389
+ this.strings = source.strings ? jsonClone(source.strings) : void 0;
1312
1390
  this.physics = source.physics ? jsonClone(source.physics) : void 0;
1313
1391
  this.multiplayer = source.multiplayer ? jsonClone(source.multiplayer) : void 0;
1314
1392
  this.environment = source.environment ? jsonClone(source.environment) : void 0;
@@ -1325,6 +1403,7 @@ var Scene = class {
1325
1403
  ...this.assets ? { assets: jsonClone(this.assets) } : {},
1326
1404
  ...this.constants ? { constants: jsonClone(this.constants) } : {},
1327
1405
  ...this.input ? { input: jsonClone(this.input) } : {},
1406
+ ...this.strings ? { strings: jsonClone(this.strings) } : {},
1328
1407
  ...this.physics ? { physics: jsonClone(this.physics) } : {},
1329
1408
  ...this.multiplayer ? { multiplayer: jsonClone(this.multiplayer) } : {},
1330
1409
  ...this.environment ? { environment: jsonClone(this.environment) } : {},
@@ -1448,7 +1527,7 @@ function applyCommon(node, nj, opts) {
1448
1527
  node.script = jsonClone(nj.script);
1449
1528
  const name = nj.script.name;
1450
1529
  if (typeof name !== "string" || name === "") throw new IncantoError("BAD_FORMAT", `Node '${nj.name}': "script" needs a non-empty "name" (a registered behavior).`);
1451
- const behavior = opts?.stubMissingBehaviors && !hasBehavior(name) ? new class extends StubBehavior {}() : createBehavior(name, nj.script.props);
1530
+ const behavior = opts?.stubAllBehaviors || opts?.stubMissingBehaviors && !hasBehavior(name) ? new class extends StubBehavior {}() : createBehavior(name, nj.script.props);
1452
1531
  behavior.node = node;
1453
1532
  node.behavior = behavior;
1454
1533
  for (const s of mergeStaticSignals(behavior.constructor)) node.declareSignal(s);
@@ -1520,4 +1599,4 @@ function validateUniqueUids(root) {
1520
1599
  walk(root);
1521
1600
  }
1522
1601
  //#endregion
1523
- export { clearBehaviors as _, SCENE_FORMAT as a, registeredBehaviors as b, SceneTree as c, resolveConstants as d, Node as f, Behavior as g, InputMap as h, serializeNode as i, CONST_REF_KEY as l, diagnose as m, loadScene as n, computeViewport as o, parseNodePath as p, Scene as r, resolveViewport as s, buildNodeJson as t, isConstRef as u, getBehavior as v, Signal as x, registerBehavior as y };
1602
+ export { Signal as C, registeredBehaviors as S, behaviorSchema as _, SCENE_FORMAT as a, getBehavior as b, SceneTree as c, resolveConstants as d, Node as f, Behavior as g, diagnose as h, serializeNode as i, CONST_REF_KEY as l, InputMap as m, loadScene as n, computeViewport as o, parseNodePath as p, Scene as r, resolveViewport as s, buildNodeJson as t, isConstRef as u, behaviorSignals as v, registerBehavior as x, clearBehaviors as y };
package/dist/net.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { at as Node, dt as Signal, l as PropSchema, v as Engine } from "./behavior-CtPScDMo.js";
2
- import { c as JsonValue, i as SceneJson, s as JsonObject } from "./schema-CcoWb32N.js";
1
+ import { Et as Signal, b as Engine, bt as Node, d as PropSchema } from "./behavior-CGSCWOHB.js";
2
+ import { c as JsonValue, i as SceneJson, s as JsonObject } from "./schema-3ywbdlrv.js";
3
3
 
4
4
  //#region src/net/types.d.ts
5
5
  type Unsubscribe = () => void;
package/dist/net.js CHANGED
@@ -1,9 +1,9 @@
1
- import { n as loadScene } from "./loader-BwR0DxeM.js";
2
- import { h as Engine } from "./register-G3edsjJ2.js";
1
+ import { n as loadScene } from "./loader-Mig5fY4n.js";
2
+ import { h as Engine } from "./register-BFLg0-_i.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-OodmuUMK.js";
6
+ import { i as applySyncPatch, n as NetworkSpawner, r as NetworkManager, t as registerNodesNet } from "./register-BjbPMA5B.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 { a as Rng } from "./schema-CcoWb32N.js";
1
+ import { a as Rng } from "./schema-3ywbdlrv.js";
2
2
 
3
3
  //#region src/core/particle-sim.d.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { B as Listener, at as Node, l as PropSchema } from "./behavior-CtPScDMo.js";
1
+ import { bt as Node, d as PropSchema, tt as Listener } from "./behavior-CGSCWOHB.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
- import { m as diagnose } from "./loader-BwR0DxeM.js";
2
+ import { h as diagnose } from "./loader-Mig5fY4n.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-BLPC10Mj.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-DSmIRAf7.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({
@@ -334,16 +334,29 @@ var Physics2D = class {
334
334
  const scene = this.engine.scene;
335
335
  if (!scene) return;
336
336
  const version = scene.tree._structureVersion;
337
- const structureChanged = version !== this.lastStructure || scene.root !== this.lastRoot;
337
+ const sameRoot = scene.root === this.lastRoot;
338
+ const structureChanged = version !== this.lastStructure || !sameRoot;
338
339
  if (structureChanged) {
340
+ const delta = sameRoot ? scene.tree._structureSince(this.lastStructure) : null;
339
341
  this.lastStructure = version;
340
342
  this.lastRoot = scene.root;
341
- this.bodySet.clear();
342
- collectBodies(scene.root, this.bodySet);
343
- }
344
- if (structureChanged) {
345
- this.jointSet.clear();
346
- collectJoints(scene.root, this.jointSet);
343
+ if (delta) {
344
+ let joints = false;
345
+ for (const { node, added } of delta) {
346
+ if (node instanceof PhysicsBody2D) if (added) this.bodySet.add(node);
347
+ else this.bodySet.delete(node);
348
+ if (node instanceof Joint2D) joints = true;
349
+ }
350
+ if (joints) {
351
+ this.jointSet.clear();
352
+ collectJoints(scene.root, this.jointSet);
353
+ }
354
+ } else {
355
+ this.bodySet.clear();
356
+ collectBodies(scene.root, this.bodySet);
357
+ this.jointSet.clear();
358
+ collectJoints(scene.root, this.jointSet);
359
+ }
347
360
  }
348
361
  const seen = this.bodySet;
349
362
  for (const node of seen) {
@@ -1,9 +1,9 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
- import { m as diagnose } from "./loader-BwR0DxeM.js";
2
+ import { h as diagnose } from "./loader-Mig5fY4n.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-BxFtmfSe.js";
4
+ import { F as PhysicsBody3D, I as RigidBody3D, N as Area3D, P as CharacterBody3D, R as Node3D, z as validateCollider3D } from "./gameplay-BvhcQbfJ.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-B6WiUsaO.js";
6
+ import { A as Terrain3D, F as InstancedMesh3D, L as buildMeshGeometry, P as Joint3D } from "./environment-presets-TAGvmM3z.js";
7
7
  import { Euler, Matrix4, Quaternion, Vector3 } from "three";
8
8
  //#region src/3d/physics/collider-lines.ts
9
9
  /**
@@ -528,17 +528,33 @@ var Physics3D = class {
528
528
  const scene = this.engine.scene;
529
529
  if (!scene) return;
530
530
  const version = scene.tree._structureVersion;
531
- const structureChanged = version !== this.lastStructure || scene.root !== this.lastRoot;
531
+ const sameRoot = scene.root === this.lastRoot;
532
+ const structureChanged = version !== this.lastStructure || !sameRoot;
532
533
  if (structureChanged) {
534
+ const delta = sameRoot ? scene.tree._structureSince(this.lastStructure) : null;
533
535
  this.lastStructure = version;
534
536
  this.lastRoot = scene.root;
535
- this.bodySet.clear();
536
- collectBodies(scene.root, this.bodySet);
537
- }
538
- if (structureChanged) {
539
- this.jointSet.clear();
540
- collectJoints(scene.root, this.jointSet);
541
- this.syncScatters(scene.root);
537
+ if (delta) {
538
+ let joints = false;
539
+ let scatters = false;
540
+ for (const { node, added } of delta) {
541
+ if (node instanceof PhysicsBody3D) if (added) this.bodySet.add(node);
542
+ else this.bodySet.delete(node);
543
+ if (node instanceof Joint3D) joints = true;
544
+ if (node instanceof InstancedMesh3D) scatters = true;
545
+ }
546
+ if (joints) {
547
+ this.jointSet.clear();
548
+ collectJoints(scene.root, this.jointSet);
549
+ }
550
+ if (scatters) this.syncScatters(scene.root);
551
+ } else {
552
+ this.bodySet.clear();
553
+ collectBodies(scene.root, this.bodySet);
554
+ this.jointSet.clear();
555
+ collectJoints(scene.root, this.jointSet);
556
+ this.syncScatters(scene.root);
557
+ }
542
558
  }
543
559
  const seen = this.bodySet;
544
560
  for (const node of seen) {
package/dist/react.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { N as Scene, n as BehaviorCtor, v as Engine } from "./behavior-CtPScDMo.js";
2
- import { c as JsonValue } from "./schema-CcoWb32N.js";
1
+ import { N as Scene, b as Engine, n as BehaviorCtor } from "./behavior-CGSCWOHB.js";
2
+ import { c as JsonValue } from "./schema-3ywbdlrv.js";
3
3
  import { CSSProperties, ReactNode } from "react";
4
4
 
5
5
  //#region src/react/index.d.ts
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-QTfghzwM.js").then((n) => n.n)).createGame3D(o) : async (o) => (await import("./create-game-CFNqoqOX.js").then((n) => n.n)).createGame2D(o)))(opts);
159
+ const next = await (_gameFactory ?? (mode === "3d" ? async (o) => (await import("./create-game-BUD89Kqh.js").then((n) => n.n)).createGame3D(o) : async (o) => (await import("./create-game-B0Wfhi2-.js").then((n) => n.n)).createGame2D(o)))(opts);
160
160
  if (disposed) {
161
161
  next.dispose();
162
162
  return;