incanto 0.49.0 → 0.51.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 (48) hide show
  1. package/bin/incanto-play.mjs +42 -17
  2. package/dist/2d.d.ts +3 -2
  3. package/dist/2d.js +3 -3
  4. package/dist/3d.d.ts +4 -3
  5. package/dist/3d.js +4 -4
  6. package/dist/{behavior-62q0HWBO.d.ts → behavior-rZNfzVbH.d.ts} +37 -3
  7. package/dist/{create-game-DFBjMetZ.js → create-game-BNaOC3Px.js} +5 -5
  8. package/dist/{create-game-BgV6UbVA.js → create-game-CkzKZ4v5.js} +5 -5
  9. package/dist/debug.d.ts +1 -1
  10. package/dist/{duplicate-CI9WF_bg.js → duplicate-EybyYeyL.js} +1 -1
  11. package/dist/{environment-presets-CZOH5TY5.js → environment-presets-C08pOC6H.js} +2 -2
  12. package/dist/{gameplay-02Btmmjn.js → gameplay-bStgtZBV.js} +175 -6
  13. package/dist/gameplay.d.ts +79 -2
  14. package/dist/gameplay.js +2 -2
  15. package/dist/index.d.ts +14 -5
  16. package/dist/index.js +6 -6
  17. package/dist/{loader-DwazzlQb.js → loader-B2asghWa.js} +350 -1
  18. package/dist/{loader-CeyU_bm1.d.ts → loader-Dkbn56KC.d.ts} +1 -1
  19. package/dist/net.d.ts +1 -1
  20. package/dist/net.js +3 -3
  21. package/dist/{pathfinding-C49JSNNq.d.ts → pathfinding-Bz34pvQD.d.ts} +1 -1
  22. package/dist/{physics-2d-vyCBfACH.js → physics-2d-Ccq2L9R0.js} +8 -3
  23. package/dist/{physics-3d-DpRqw8Mz.js → physics-3d-B2c5KNYh.js} +13 -8
  24. package/dist/react.d.ts +1 -1
  25. package/dist/react.js +1 -1
  26. package/dist/{register-BNPZYJmd.js → register-B_BaaGx-.js} +2 -2
  27. package/dist/{register-uvaZj1KX.js → register-CGq-Hee8.js} +19 -265
  28. package/dist/{register-CB11yp21.js → register-CLVhzWcI.js} +2 -2
  29. package/dist/{replay-C0XJIsO7.js → replay-BgKxGcXH.js} +42 -3
  30. package/dist/{replay-CAphXMyM.d.ts → replay-DIP2_as4.d.ts} +6 -2
  31. package/dist/{src-DF4gCsqO.js → src-C3UwzYXl.js} +1 -1
  32. package/dist/{test-DRna_BQU.js → test-D2gRpk5V.js} +48 -21
  33. package/dist/test.d.ts +3 -3
  34. package/dist/test.js +2 -2
  35. package/dist/vite.js +2 -2
  36. package/editor/assets/{agent8-DCW4TgDt.js → agent8-DbX_msaO.js} +1 -1
  37. package/editor/assets/{debug-RC6qts6S.js → debug-C-kMxdl1.js} +1 -1
  38. package/editor/assets/{index-5dEIhvsf.js → index-CUc1U7wm.js} +91 -91
  39. package/editor/index.html +1 -1
  40. package/package.json +1 -1
  41. package/skills/incanto-building-2d-games.md +6 -1
  42. package/skills/incanto-gameplay-behaviors.md +11 -0
  43. package/skills/incanto-node-reference.md +13 -0
  44. package/skills/incanto-playtesting.md +13 -2
  45. package/skills/incanto-save-slots.md +134 -12
  46. package/templates-app/beacon-isle-3d/package.json +1 -1
  47. package/templates-app/tps-3d/package.json +1 -1
  48. package/templates-app/village-quest-3d/package.json +1 -1
@@ -62,8 +62,11 @@ wheel/capture/describe/framing/logs/quit), JSON-line responses on stdout.`);
62
62
  }
63
63
 
64
64
  const out = (obj) => process.stdout.write(`${JSON.stringify(obj)}\n`);
65
- const fail = (cmd, e) =>
65
+ let failures = 0;
66
+ const fail = (cmd, e) => {
67
+ failures += 1;
66
68
  out({ ok: false, cmd, error: { code: e?.code ?? 'ERROR', message: e?.message ?? String(e) } });
69
+ };
67
70
 
68
71
  const { createPlaySession } = await import(pathToFileURL(join(PKG, 'dist', 'test.js')).href);
69
72
  const incanto = await import(pathToFileURL(join(PKG, 'dist', 'index.js')).href);
@@ -204,21 +207,43 @@ function handle(line) {
204
207
  }
205
208
 
206
209
  if (args.commands) {
207
- for (const line of readFileSync(resolve(args.commands), 'utf-8').split('\n')) {
208
- if (!handle(line)) process.exit(0);
210
+ const text = readFileSync(resolve(args.commands), 'utf-8');
211
+ // A REPLAY, not a command script. `incanto-playtest` writes its failing runs
212
+ // as ReplayJson and the docs point this flag at them; reading one as text
213
+ // rejected every line as UNKNOWN_COMMAND and still exited 0, so the tool's
214
+ // whole payoff — "the failure comes back as a file you can watch" — silently
215
+ // did nothing.
216
+ const recording = text.trimStart().startsWith('{') ? JSON.parse(text) : null;
217
+ if (recording?.replay) {
218
+ incanto.replay(session.engine, recording);
219
+ out({
220
+ ok: true,
221
+ cmd: 'replay',
222
+ frames: recording.ticks.length,
223
+ events: recording.events.length,
224
+ });
225
+ out({ ok: true, cmd: 'describe', text: session.describe() });
226
+ session.dispose();
227
+ process.exitCode = 0;
228
+ } else {
229
+ for (const line of text.split('\n')) {
230
+ if (!handle(line)) break;
231
+ }
232
+ session.dispose();
233
+ // A script whose every line was rejected must not report success: that is
234
+ // exactly how a replay pointed at the wrong reader stayed invisible.
235
+ process.exitCode = failures > 0 ? 1 : 0;
209
236
  }
210
- session.dispose();
211
- process.exit(0);
212
- }
213
-
214
- const rl = createInterface({ input: process.stdin });
215
- rl.on('line', (line) => {
216
- if (!handle(line)) {
217
- rl.close();
237
+ } else {
238
+ const rl = createInterface({ input: process.stdin });
239
+ rl.on('line', (line) => {
240
+ if (!handle(line)) {
241
+ rl.close();
242
+ process.exit(0);
243
+ }
244
+ });
245
+ rl.on('close', () => {
246
+ session.dispose();
218
247
  process.exit(0);
219
- }
220
- });
221
- rl.on('close', () => {
222
- session.dispose();
223
- process.exit(0);
224
- });
248
+ });
249
+ }
package/dist/2d.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { Et as Node, P as Scene$1, S as Scheduler, T as RendererStats, b as Engine, d as PropSchema, n as BehaviorCtor, w as GameStats } from "./behavior-62q0HWBO.js";
1
+ import { Dt as Node, P as Scene$1, S as Scheduler, T as RendererStats, b as Engine, d as PropSchema, n as BehaviorCtor, w as GameStats } from "./behavior-rZNfzVbH.js";
2
2
  import { n as DiagnosticSink, t as EditorSwitchOptions } from "./editor-switch-DAvWQeld.js";
3
3
  import { i as SceneJson, s as JsonObject } from "./schema-CFeioQRE.js";
4
- import { t as LoadSceneOptions } from "./loader-CeyU_bm1.js";
4
+ import { t as LoadSceneOptions } from "./loader-Dkbn56KC.js";
5
5
  import { n as AnimationEntry } from "./sprite-animation-CMr6f1K2.js";
6
6
  import { t as ParticleSim } from "./particle-sim-BzJ1yxoE.js";
7
7
  import { Group, Mesh, Object3D, Scene, Texture } from "three";
@@ -206,6 +206,7 @@ declare class Physics2D {
206
206
  private readonly world;
207
207
  private readonly events;
208
208
  private readonly kcc;
209
+ private readonly disconnectScene;
209
210
  private readonly disconnect;
210
211
  private lastDt;
211
212
  private readonly optsGravity;
package/dist/2d.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
2
- import { a as AssetStore2D, i as syncTree2D, r as Renderer2D, t as createGame2D } from "./create-game-BgV6UbVA.js";
3
- import { _ as RigidBody2D, a as parseCells, c as ColorRect2D, d as AnimatedSprite2D, f as Sprite2D, g as PhysicsBody2D, h as CharacterBody2D, i as mergeSolidRects, l as CharacterController2D, m as Area2D, n as UILayer, o as Particles2D, p as Joint2D, r as TileMap2D, s as Label, t as registerNodes2D, u as Camera2D, v as StaticBody2D, y as Node2D } from "./register-BNPZYJmd.js";
4
- import { n as enablePhysics2D, t as Physics2D } from "./physics-2d-vyCBfACH.js";
2
+ import { a as AssetStore2D, i as syncTree2D, r as Renderer2D, t as createGame2D } from "./create-game-CkzKZ4v5.js";
3
+ import { _ as RigidBody2D, a as parseCells, c as ColorRect2D, d as AnimatedSprite2D, f as Sprite2D, g as PhysicsBody2D, h as CharacterBody2D, i as mergeSolidRects, l as CharacterController2D, m as Area2D, n as UILayer, o as Particles2D, p as Joint2D, r as TileMap2D, s as Label, t as registerNodes2D, u as Camera2D, v as StaticBody2D, y as Node2D } from "./register-B_BaaGx-.js";
4
+ import { n as enablePhysics2D, t as Physics2D } from "./physics-2d-Ccq2L9R0.js";
5
5
  //#region src/2d/library-sprite.ts
6
6
  /**
7
7
  * What a `CharacterController2D`/`3D` will ask a skin to play, and the clip in
package/dist/3d.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- import { D as QualityCaps, Et as Node, P as Scene$1, S as Scheduler, T as RendererStats, b as Engine, d as PropSchema, n as BehaviorCtor, w as GameStats } from "./behavior-62q0HWBO.js";
1
+ import { D as QualityCaps, Dt as Node, P as Scene$1, S as Scheduler, T as RendererStats, b as Engine, d as PropSchema, n as BehaviorCtor, w as GameStats } from "./behavior-rZNfzVbH.js";
2
2
  import { n as DiagnosticSink, t as EditorSwitchOptions } from "./editor-switch-DAvWQeld.js";
3
3
  import { i as SceneJson$1, s as JsonObject } from "./schema-CFeioQRE.js";
4
- import { t as LoadSceneOptions } from "./loader-CeyU_bm1.js";
4
+ import { t as LoadSceneOptions } from "./loader-Dkbn56KC.js";
5
5
  import { t as ParticleSim } from "./particle-sim-BzJ1yxoE.js";
6
6
  import { a as GridCell, c as diffText, d as frameText, i as FrameStatsOptions, l as frameSignature, n as FrameSignature, o as SIGNATURE_GRID, r as FrameStats, s as diffSignatures, t as FrameDiff, u as frameStats } from "./frame-report-DZ70IY26.js";
7
- import { n as PathGrid, s as SpatialPose } from "./pathfinding-C49JSNNq.js";
7
+ import { n as PathGrid, s as SpatialPose } from "./pathfinding-Bz34pvQD.js";
8
8
  import { AnimationClip, AnimationMixer, BufferGeometry, Color, DirectionalLight, Group, InstancedMesh, Mesh, MeshPhysicalMaterial, Object3D, PerspectiveCamera, Scene, ShaderMaterial, Texture, Vector3, WebGLRenderer } from "three";
9
9
  import { VRM } from "@pixiv/three-vrm";
10
10
  import { Sky } from "three/examples/jsm/objects/Sky.js";
@@ -439,6 +439,7 @@ declare class Physics3D {
439
439
  private readonly world;
440
440
  private readonly events;
441
441
  private readonly kcc;
442
+ private readonly disconnectScene;
442
443
  private readonly disconnect;
443
444
  private lastDt;
444
445
  private readonly optsGravity;
package/dist/3d.js CHANGED
@@ -1,9 +1,9 @@
1
- import { A as Water3D, F as PhysicsBody3D, I as RigidBody3D, L as StaticBody3D, M as WaterCutout3D, N as Area3D, P as CharacterBody3D, R as Node3D, V as WATER_MAX_RIPPLES, j as WATER_CUTOUT_MAX } from "./gameplay-02Btmmjn.js";
2
- import { A as Terrain3D, B as keyboardIntensity, C as resolveFlowerDensity, D as BoneLookAt3D, E as Camera3D, F as InstancedMesh3D, G as acquireTexture, H as rigPose, I as MeshInstance3D, M as TERRAIN_THEMES, N as terrainThemeLayers, O as BoneAttachment3D, P as Joint3D, R as QUARTER_PITCH, S as Flowers3D, T as CharacterController3D, U as TextureCache3D, V as movementState, W as acquireOwnTexture, _ as LoftMesh3D, a as Tree3D, b as Foliage3D, c as buildRiverRings, d as riverCarveChannels, f as riverStepFor, g as ModelInstance3D, h as Particles3D, i as VoxelGrid3D, j as DEFAULT_TERRAIN_TEXTURE_BASE, k as Billboard3D, l as findRiverCoverageGaps, m as traceDownhillPath, n as registerNodes3D, o as Trail3D, p as smoothCourse, r as VOXEL_PALETTE, s as River3D, u as projectToRiver, v as DirectionalLight3D, w as FLOWER_VARIETIES, x as DENSITY_PRESETS, y as OmniLight3D, z as cameraRelative } from "./environment-presets-CZOH5TY5.js";
3
- import { a as Environment3D, c as parseEnvironment3D, d as AssetStore3D, i as syncTree, l as sunDirectionFromElevationAzimuth, o as setEnvironment3D, r as Renderer3D, s as horizonColorFromSky, t as createGame3D, u as sunDirectionFromSky } from "./create-game-DFBjMetZ.js";
1
+ import { F as CharacterBody3D, H as WATER_MAX_RIPPLES, I as PhysicsBody3D, L as RigidBody3D, M as WATER_CUTOUT_MAX, N as WaterCutout3D, P as Area3D, R as StaticBody3D, j as Water3D, z as Node3D } from "./gameplay-bStgtZBV.js";
2
+ import { A as Terrain3D, B as keyboardIntensity, C as resolveFlowerDensity, D as BoneLookAt3D, E as Camera3D, F as InstancedMesh3D, G as acquireTexture, H as rigPose, I as MeshInstance3D, M as TERRAIN_THEMES, N as terrainThemeLayers, O as BoneAttachment3D, P as Joint3D, R as QUARTER_PITCH, S as Flowers3D, T as CharacterController3D, U as TextureCache3D, V as movementState, W as acquireOwnTexture, _ as LoftMesh3D, a as Tree3D, b as Foliage3D, c as buildRiverRings, d as riverCarveChannels, f as riverStepFor, g as ModelInstance3D, h as Particles3D, i as VoxelGrid3D, j as DEFAULT_TERRAIN_TEXTURE_BASE, k as Billboard3D, l as findRiverCoverageGaps, m as traceDownhillPath, n as registerNodes3D, o as Trail3D, p as smoothCourse, r as VOXEL_PALETTE, s as River3D, u as projectToRiver, v as DirectionalLight3D, w as FLOWER_VARIETIES, x as DENSITY_PRESETS, y as OmniLight3D, z as cameraRelative } from "./environment-presets-C08pOC6H.js";
3
+ import { a as Environment3D, c as parseEnvironment3D, d as AssetStore3D, i as syncTree, l as sunDirectionFromElevationAzimuth, o as setEnvironment3D, r as Renderer3D, s as horizonColorFromSky, t as createGame3D, u as sunDirectionFromSky } from "./create-game-BNaOC3Px.js";
4
4
  import { a as frameSignature, n as diffSignatures, o as frameStats, r as diffText, s as frameText, t as SIGNATURE_GRID } from "./frame-report-njybhZon.js";
5
5
  import { n as splatWeights, t as buildHeightmap } from "./heightmap-CRK0M4jT.js";
6
- import { n as enablePhysics3D, t as Physics3D } from "./physics-3d-DpRqw8Mz.js";
6
+ import { n as enablePhysics3D, t as Physics3D } from "./physics-3d-B2c5KNYh.js";
7
7
  //#region src/3d/model-verdict.ts
8
8
  /** Mixamo exports every bone as `mixamorigX`; the retargeter binds by that name. */
9
9
  const MIXAMO = /^mixamorig[:_]?/i;
@@ -151,6 +151,14 @@ declare class SceneTree {
151
151
  _queueFree(node: Node): void;
152
152
  /** @internal Called by Node.free(): a freed root must not be re-drivable. */
153
153
  _detachRoot(node: Node): void;
154
+ /**
155
+ * @internal Is this node already on its way out?
156
+ *
157
+ * A save taken from a `collected` handler runs BEFORE the deferred free, so
158
+ * the pickup that triggered the checkpoint is still in the tree — and came
159
+ * back on the next run while its score did not.
160
+ */
161
+ _isQueuedFree(node: Node): boolean;
154
162
  private flushFreeQueue;
155
163
  }
156
164
  //#endregion
@@ -968,6 +976,18 @@ interface RestoreReport {
968
976
  skipped: string[];
969
977
  /** How many behaviors actually took their state back. */
970
978
  restored: number;
979
+ /**
980
+ * How much state this tree HAD to give back — every behavior in it with a
981
+ * `serialize`, addressable or not.
982
+ *
983
+ * `restored: 0` is ambiguous on its own and the ambiguity is expensive: a
984
+ * hand-written scene with no uids saves `{}`, and the documented check
985
+ * (`if (report.missing.length)`) passes on it while the whole run is gone.
986
+ * `restored: 0, expected: 2` cannot be read as success.
987
+ */
988
+ expected: number;
989
+ /** Authored nodes the save says the run consumed, and that were freed again. */
990
+ freed: number;
971
991
  }
972
992
  /**
973
993
  * Every behavior in the tree that has something to say, by uid.
@@ -980,9 +1000,10 @@ interface RestoreReport {
980
1000
  * cannot be given back. The editor assigns a uid to every node it touches; a
981
1001
  * hand-written scene may not have.
982
1002
  */
983
- declare function captureBehaviors(root: Node): {
1003
+ declare function captureBehaviors(root: Node, source?: JsonValue): {
984
1004
  state: BehaviorState; /** Paths of nodes that had state to save and no uid to save it under. */
985
- unaddressable: string[];
1005
+ unaddressable: string[]; /** Behaviors in this tree that have something to save — addressable or not. */
1006
+ saveable: number;
986
1007
  };
987
1008
  /**
988
1009
  * Hand each behavior its state back.
@@ -1032,6 +1053,19 @@ declare class SaveSlots {
1032
1053
  * and the caller decides.
1033
1054
  */
1034
1055
  declare function behaviorsWithoutSave(root: Node): string[];
1056
+ /**
1057
+ * Behaviors that DO save, on nodes with no uid to save them under — the state
1058
+ * that goes nowhere.
1059
+ *
1060
+ * `incanto-check` warns about the built-ins it can recognise from the JSON
1061
+ * (Health, ScoreKeeper, Collector), but a scene file cannot be asked whether
1062
+ * YOUR behavior has a `serialize`. This can: it walks the live tree, so a quest
1063
+ * log or a wallet of your own is named too.
1064
+ *
1065
+ * The pair to read before shipping: `behaviorsWithoutSave` is "did you forget
1066
+ * to write serialize", this is "did you forget the uid that makes it count".
1067
+ */
1068
+ declare function savesWithoutUid(root: Node): string[];
1035
1069
  //#endregion
1036
1070
  //#region src/core/scene/scene.d.ts
1037
1071
  /**
@@ -1682,4 +1716,4 @@ declare function registeredBehaviors(): string[];
1682
1716
  /** Test isolation helper. */
1683
1717
  declare function clearBehaviors(): void;
1684
1718
  //#endregion
1685
- export { suggestLocale as $, SettingsValues as A, LogLevel as At, captureBehaviors as B, EngineStats as C, PlayMusicOptions as Ct, QualityCaps as D, NodeLifecycle as Dt, DeviceHints as E, Node as Et, BehaviorState as F, OrderGroup as G, SaveStore as H, RestoreReport as I, resolveOrderGroups as J, OrderGroupTable as K, SaveSlot as L, readDeviceHints as M, Signal as Mt, suggestQuality as N, SignalListener as Nt, QualityTier as O, SceneTree as Ot, Scene as P, T_PREFIX as Q, SaveSlots as R, Scheduler as S, MusicTrack as St, RendererStats as T, BusName as Tt, createSaveStore as U, restoreBehaviors as V, ORDER_GROUP_BASE as W, LocaleTables as X, BASE_LOCALE as Y, Localization as Z, mergeStaticSignals as _, SfxWave as _t, clearBehaviors as a, Voice as at, Engine as b, MusicBackend as bt, registeredBehaviors as c, ROLLOFF_MODELS as ct, PropSchema as d, Vec3 as dt, translationKey as et, clearRegistry as f, spatialGain as ft, getNodeType as g, SfxParams as gt, getNodeSignals as h, SFX_PRESET_NAMES as ht, behaviorSignals as i, isAudioContextAvailable as it, qualityEnvironment as j, LogManager as jt, Settings as k, LogEntry as kt, NodeCtor as l, RolloffModel as lt, getNodeSchema as m, SFX_PRESETS as mt, BehaviorCtor as n, SfxEngine as nt, getBehavior as o, VoicePreset as ot, createNode as p, spatialPan as pt, effectiveOrder as q, behaviorSchema as r, SfxPlayOptions as rt, registerBehavior as s, Listener as st, Behavior as t, InputMap as tt, PropDef as u, SpatialParams as ut, registerNode as v, SynthOptions as vt, GameStats as w, AudioBuses as wt, EngineOptions as x, MusicManager as xt, registeredTypes as y, synthSfx as yt, behaviorsWithoutSave as z };
1719
+ export { T_PREFIX as $, SettingsValues as A, LogEntry as At, captureBehaviors as B, EngineStats as C, MusicTrack as Ct, QualityCaps as D, Node as Dt, DeviceHints as E, BusName as Et, BehaviorState as F, ORDER_GROUP_BASE as G, savesWithoutUid as H, RestoreReport as I, effectiveOrder as J, OrderGroup as K, SaveSlot as L, readDeviceHints as M, LogManager as Mt, suggestQuality as N, Signal as Nt, QualityTier as O, NodeLifecycle as Ot, Scene as P, SignalListener as Pt, Localization as Q, SaveSlots as R, Scheduler as S, MusicManager as St, RendererStats as T, AudioBuses as Tt, SaveStore as U, restoreBehaviors as V, createSaveStore as W, BASE_LOCALE as X, resolveOrderGroups as Y, LocaleTables as Z, mergeStaticSignals as _, SfxParams as _t, clearBehaviors as a, isAudioContextAvailable as at, Engine as b, synthSfx as bt, registeredBehaviors as c, Listener as ct, PropSchema as d, SpatialParams as dt, suggestLocale as et, clearRegistry as f, Vec3 as ft, getNodeType as g, SFX_PRESET_NAMES as gt, getNodeSignals as h, SFX_PRESETS as ht, behaviorSignals as i, SfxPlayOptions as it, qualityEnvironment as j, LogLevel as jt, Settings as k, SceneTree as kt, NodeCtor as l, ROLLOFF_MODELS as lt, getNodeSchema as m, spatialPan as mt, BehaviorCtor as n, InputMap as nt, getBehavior as o, Voice as ot, createNode as p, spatialGain as pt, OrderGroupTable as q, behaviorSchema as r, SfxEngine as rt, registerBehavior as s, VoicePreset as st, Behavior as t, translationKey as tt, PropDef as u, RolloffModel as ut, registerNode as v, SfxWave as vt, GameStats as w, PlayMusicOptions as wt, EngineOptions as x, MusicBackend as xt, registeredTypes as y, SynthOptions as yt, behaviorsWithoutSave as z };
@@ -1,15 +1,15 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
- import { n as loadScene, v as diagnose, w as registerBehavior } from "./loader-DwazzlQb.js";
3
- import { _ as qualityCaps, h as Engine, m as AudioPlayer, y as qualityRendering } from "./register-uvaZj1KX.js";
2
+ import { A as registerBehavior, n as loadScene, w as diagnose } from "./loader-B2asghWa.js";
3
+ import { _ as qualityCaps, h as Engine, m as AudioPlayer, y as qualityRendering } from "./register-CGq-Hee8.js";
4
4
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
5
5
  import { r as parseDrive, t as logReport } from "./log-report-lxrQY9cH.js";
6
6
  import { i as resolveRendering, n as attachTouchControls } from "./touch-BoNg_MnF.js";
7
7
  import { a as openBundledEditor, i as devServerLibrary, n as pauseWhenHidden, o as poseFromRenderer, r as crossFade, t as teardown } from "./teardown-BKTCzLek.js";
8
- import { B as createCausticsQuad, F as PhysicsBody3D, R as Node3D, n as registerGameplayBehaviors } from "./gameplay-02Btmmjn.js";
8
+ import { I as PhysicsBody3D, V as createCausticsQuad, n as registerGameplayBehaviors, z as Node3D } from "./gameplay-bStgtZBV.js";
9
9
  import { t as debugSources } from "./debug-draw-BM3DsvtT.js";
10
- import { E as Camera3D, U as TextureCache3D, g as ModelInstance3D, n as registerNodes3D, t as resolveEnvironmentHdri, v as DirectionalLight3D } from "./environment-presets-CZOH5TY5.js";
10
+ import { E as Camera3D, U as TextureCache3D, g as ModelInstance3D, n as registerNodes3D, t as resolveEnvironmentHdri, v as DirectionalLight3D } from "./environment-presets-C08pOC6H.js";
11
11
  import { a as frameSignature, i as frameImage, o as frameStats } from "./frame-report-njybhZon.js";
12
- import { n as enablePhysics3D } from "./physics-3d-DpRqw8Mz.js";
12
+ import { n as enablePhysics3D } from "./physics-3d-B2c5KNYh.js";
13
13
  import { ACESFilmicToneMapping, AmbientLight, Box3, BufferAttribute, BufferGeometry, Color, DepthTexture, EquirectangularReflectionMapping, FloatType, Fog, HalfFloatType, LineBasicMaterial, LineSegments, Matrix4, Mesh, PCFShadowMap, PMREMGenerator, PerspectiveCamera, PlaneGeometry, Quaternion, Raycaster, Scene, ShaderMaterial, Vector2, Vector3, WebGLRenderTarget, WebGLRenderer } from "three";
14
14
  import { VRMLoaderPlugin, VRMUtils } from "@pixiv/three-vrm";
15
15
  import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
@@ -1,13 +1,13 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
- import { n as loadScene, o as computeViewport, s as resolveViewport, v as diagnose, w as registerBehavior } from "./loader-DwazzlQb.js";
3
- import { h as Engine, m as AudioPlayer } from "./register-uvaZj1KX.js";
2
+ import { A as registerBehavior, n as loadScene, o as computeViewport, s as resolveViewport, w as diagnose } from "./loader-B2asghWa.js";
3
+ import { h as Engine, m as AudioPlayer } from "./register-CGq-Hee8.js";
4
4
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
5
5
  import { i as resolveRendering, n as attachTouchControls } from "./touch-BoNg_MnF.js";
6
6
  import { a as openBundledEditor, i as devServerLibrary, n as pauseWhenHidden, r as crossFade, t as teardown } from "./teardown-BKTCzLek.js";
7
- import { n as registerGameplayBehaviors } from "./gameplay-02Btmmjn.js";
8
- import { g as PhysicsBody2D, n as UILayer, t as registerNodes2D, u as Camera2D, y as Node2D } from "./register-BNPZYJmd.js";
7
+ import { n as registerGameplayBehaviors } from "./gameplay-bStgtZBV.js";
8
+ import { g as PhysicsBody2D, n as UILayer, t as registerNodes2D, u as Camera2D, y as Node2D } from "./register-B_BaaGx-.js";
9
9
  import { t as debugSources } from "./debug-draw-BM3DsvtT.js";
10
- import { n as enablePhysics2D } from "./physics-2d-vyCBfACH.js";
10
+ import { n as enablePhysics2D } from "./physics-2d-Ccq2L9R0.js";
11
11
  import { Box3, BufferAttribute, BufferGeometry, Color, LineBasicMaterial, LineSegments, LinearFilter, NearestFilter, OrthographicCamera, Raycaster, SRGBColorSpace, Scene, TextureLoader, Vector2, Vector3, WebGLRenderer } from "three";
12
12
  //#region src/2d/assets.ts
13
13
  /**
package/dist/debug.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { At as LogLevel, T as RendererStats, b as Engine } from "./behavior-62q0HWBO.js";
1
+ import { T as RendererStats, b as Engine, jt as LogLevel } from "./behavior-rZNfzVbH.js";
2
2
 
3
3
  //#region src/debug/panel.d.ts
4
4
  /** Minimal document surface the overlay needs (injectable for tests). */
@@ -1,4 +1,4 @@
1
- import { i as serializeNode, t as buildNodeJson } from "./loader-DwazzlQb.js";
1
+ import { i as serializeNode, t as buildNodeJson } from "./loader-B2asghWa.js";
2
2
  //#region src/core/scene/duplicate.ts
3
3
  function stripUids(json) {
4
4
  const { uid: _uid, ...rest } = json;
@@ -1,10 +1,10 @@
1
- import { t as registerCoreNodes } from "./register-uvaZj1KX.js";
1
+ import { t as registerCoreNodes } from "./register-CGq-Hee8.js";
2
2
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
3
3
  import { t as Rng } from "./rng-DP-SR7eg.js";
4
4
  import { i as getNodeSchema, l as registerNode } from "./registry-IyWCGe4q.js";
5
5
  import { t as createNoise2D } from "./noise-CGUMx44x.js";
6
6
  import { i as 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 { A as Water3D, F as PhysicsBody3D, H as colliderFootDrop, I as RigidBody3D, L as StaticBody3D, M as WaterCutout3D, N as Area3D, P as CharacterBody3D, R as Node3D } from "./gameplay-02Btmmjn.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-bStgtZBV.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";
@@ -1,7 +1,7 @@
1
- import { f as Node, h as effectiveOrder, n as loadScene, w as registerBehavior, y as Behavior } from "./loader-DwazzlQb.js";
1
+ import { A as registerBehavior, T as Behavior, f as Node, m as SaveSlots, n as loadScene, x as effectiveOrder } from "./loader-B2asghWa.js";
2
2
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
3
3
  import { t as jsonClone } from "./json-BLk7H2Qa.js";
4
- import { t as duplicateNode } from "./duplicate-CI9WF_bg.js";
4
+ import { t as duplicateNode } from "./duplicate-EybyYeyL.js";
5
5
  import { Color, CubeCamera, DepthTexture, DoubleSide, Euler, FloatType, Frustum, HalfFloatType, LinearMipmapLinearFilter, MathUtils, Matrix4, Mesh, MeshDepthMaterial, Object3D, PerspectiveCamera, Plane, PlaneGeometry, Quaternion, ShaderMaterial, Sphere, Vector2, Vector3, Vector4, WebGLCubeRenderTarget, WebGLRenderTarget } from "three";
6
6
  //#region src/3d/frustum.ts
7
7
  const scratchFrustum = new Frustum();
@@ -4252,6 +4252,20 @@ function firstWater(node) {
4252
4252
  return null;
4253
4253
  }
4254
4254
  //#endregion
4255
+ //#region src/gameplay/move-body.ts
4256
+ function moveBody(node, to, dt) {
4257
+ const body = node;
4258
+ if (typeof body.moveAndSlide !== "function" || body._physics == null || dt <= 0) {
4259
+ node.position = [...to];
4260
+ return;
4261
+ }
4262
+ const from = body.position;
4263
+ const v = [];
4264
+ for (let i = 0; i < from.length; i++) v.push(((to[i] ?? from[i] ?? 0) - (from[i] ?? 0)) / dt);
4265
+ body.velocity = v;
4266
+ body.moveAndSlide();
4267
+ }
4268
+ //#endregion
4255
4269
  //#region src/gameplay/spatial.ts
4256
4270
  /** Duck-type test: every spatial node (Node2D/Node3D) exposes `position: number[]`. */
4257
4271
  function hasPosition$1(node) {
@@ -4414,7 +4428,7 @@ var Chase = class extends Behavior {
4414
4428
  this.inRange = false;
4415
4429
  const step = Math.min(this.speed * dt, this.stopRange > 0 ? d - this.stopRange : d);
4416
4430
  const { position } = moveToward(mover.position, target.position, Math.max(0, step));
4417
- mover.position = position;
4431
+ moveBody(mover, position, dt);
4418
4432
  }
4419
4433
  };
4420
4434
  //#endregion
@@ -5675,7 +5689,7 @@ var Patrol = class extends Behavior {
5675
5689
  const target = this.pointAt(this.index);
5676
5690
  if (!target) return;
5677
5691
  const { position, reached } = moveToward(node.position, target, this.speed * dt);
5678
- node.position = position;
5692
+ moveBody(node, position, dt);
5679
5693
  if (reached) {
5680
5694
  this.emit("reachedPoint", this.index);
5681
5695
  this.advance();
@@ -5740,8 +5754,8 @@ var Pickup = class extends Behavior {
5740
5754
  const collector = findCollector(other, this.collectorGroup);
5741
5755
  if (!collector) return;
5742
5756
  this.collectedAlready = true;
5743
- this.emit("collected", this.value, collector);
5744
5757
  this.node.queueFree();
5758
+ this.emit("collected", this.value, collector);
5745
5759
  }
5746
5760
  };
5747
5761
  /**
@@ -5842,6 +5856,160 @@ var Projectile = class extends Behavior {
5842
5856
  }
5843
5857
  };
5844
5858
  //#endregion
5859
+ //#region src/gameplay/save-point.ts
5860
+ /**
5861
+ * The save, as something the scene can point a signal at.
5862
+ *
5863
+ * Every other common wiring in this engine became declarative — score to
5864
+ * screen, damage to health bar, waves, win and lose — and saving stayed the one
5865
+ * thing that needed TypeScript. The skill's own autosave example is a
5866
+ * connection to `Game.onCheckpoint`, a method the author has to go and write.
5867
+ *
5868
+ * "*When* to save is a design decision and only your game knows" is right, and
5869
+ * it is untouched here: the SCENE still says when, by choosing which signal to
5870
+ * wire — a level exit's `triggerEnter`, `won`, a key. What it gets is something
5871
+ * to wire TO.
5872
+ *
5873
+ * ```json
5874
+ * { "name": "Save", "type": "Node", "uid": "n_…",
5875
+ * "script": { "name": "SavePoint", "props": { "game": "vault", "slot": "1" } } }
5876
+ * ```
5877
+ * ```json
5878
+ * { "signal": "triggerEnter", "from": "Level/Exit", "to": "Save", "handler": "save" }
5879
+ * ```
5880
+ */
5881
+ var SavePoint = class extends Behavior {
5882
+ static props = {
5883
+ /** Namespace for this game's slots — keeps two games on one origin apart. */
5884
+ game: { default: "game" },
5885
+ /** Which slot this node writes and reads. */
5886
+ slot: { default: "1" },
5887
+ /** Shown in a load menu. */
5888
+ label: { default: "" },
5889
+ /**
5890
+ * The scene key a load should route back to. Empty = this scene's `name`.
5891
+ * The engine does not route scenes; it records what your loader will need.
5892
+ */
5893
+ scene: { default: "" },
5894
+ /** Read the slot back as soon as the scene is ready — a "Continue" boot. */
5895
+ restoreOnReady: { default: false },
5896
+ /**
5897
+ * Ask, without loading: on the first frame, emit `hasSave(label, playtime)`
5898
+ * or `noSave`. A title screen needs this and nothing else — the scene can
5899
+ * then wire `noSave → Continue.hide` and `hasSave → SlotInfo.setText`.
5900
+ */
5901
+ probeOnReady: { default: false }
5902
+ };
5903
+ static signals = [
5904
+ "saved",
5905
+ "restored",
5906
+ "noSave",
5907
+ "hasSave"
5908
+ ];
5909
+ game = "game";
5910
+ slot = "1";
5911
+ label = "";
5912
+ scene = "";
5913
+ restoreOnReady = false;
5914
+ probeOnReady = false;
5915
+ /** Seconds this run has been playing — written into the slot as `playtime`. */
5916
+ elapsed = 0;
5917
+ slots = null;
5918
+ store() {
5919
+ this.slots ??= new SaveSlots(this.game);
5920
+ return this.slots;
5921
+ }
5922
+ pendingRestore = false;
5923
+ pendingProbe = false;
5924
+ onReady() {
5925
+ this.pendingRestore = this.restoreOnReady;
5926
+ this.pendingProbe = this.probeOnReady;
5927
+ }
5928
+ update(dt) {
5929
+ if (this.pendingProbe) {
5930
+ this.pendingProbe = false;
5931
+ this.probe();
5932
+ }
5933
+ if (this.pendingRestore) {
5934
+ this.pendingRestore = false;
5935
+ this.restore();
5936
+ }
5937
+ this.elapsed += dt;
5938
+ }
5939
+ /**
5940
+ * Is there anything to continue? Emits `hasSave(label, playtime, scene)` or
5941
+ * `noSave`, and touches nothing.
5942
+ *
5943
+ * The label leads because that is what a menu shows; an unlabelled slot
5944
+ * falls back to its scene key, so `hasSave → SlotInfo.setText` always says
5945
+ * something rather than nothing.
5946
+ */
5947
+ probe() {
5948
+ const slot = this.store().read(this.slot);
5949
+ if (!slot) {
5950
+ this.emit("noSave");
5951
+ return;
5952
+ }
5953
+ this.emit("hasSave", slot.label || slot.scene, slot.playtime, slot.scene);
5954
+ }
5955
+ /** Seconds this run has been playing, including time carried over a continue. */
5956
+ get playtime() {
5957
+ return Math.round(this.elapsed);
5958
+ }
5959
+ /**
5960
+ * The scene key this slot was saved from — what a loader needs to route.
5961
+ * Null when the slot is empty.
5962
+ */
5963
+ slotScene() {
5964
+ return this.store().read(this.slot)?.scene ?? null;
5965
+ }
5966
+ /**
5967
+ * Write the run to its slot. Emits `saved(slotId)`.
5968
+ *
5969
+ * Wire it to whatever marks the moment. Extra signal arguments are ignored,
5970
+ * so `triggerEnter → save` works as-is.
5971
+ */
5972
+ save() {
5973
+ const engine = this.engine;
5974
+ this.store().write(this.slot, {
5975
+ scene: this.scene || (engine.scene?.source.name ?? ""),
5976
+ state: engine.captureState(),
5977
+ label: this.label,
5978
+ savedAt: Date.now(),
5979
+ playtime: Math.round(this.elapsed)
5980
+ });
5981
+ this.emit("saved", this.slot);
5982
+ }
5983
+ /**
5984
+ * Give this scene the slot's state back. Emits `restored(count)`, or
5985
+ * `noSave` when the slot is empty — which is what greys out a Continue button.
5986
+ *
5987
+ * Restoring does NOT route: if the slot names another scene, that is your
5988
+ * loader's business, and `slot.scene` is the key it needs.
5989
+ */
5990
+ restore() {
5991
+ const slot = this.store().read(this.slot);
5992
+ if (!slot) {
5993
+ this.emit("noSave");
5994
+ return;
5995
+ }
5996
+ const report = this.engine.restoreState(slot.state);
5997
+ this.emit("restored", report.restored);
5998
+ }
5999
+ /** Forget this slot — a "delete save" button. */
6000
+ clear() {
6001
+ this.store().remove(this.slot);
6002
+ }
6003
+ /** The run's own clock survives a save/continue cycle. */
6004
+ serialize() {
6005
+ return { playtime: Math.round(this.elapsed) };
6006
+ }
6007
+ deserialize(data) {
6008
+ const d = data;
6009
+ if (typeof d.playtime === "number" && d.playtime >= 0) this.elapsed = d.playtime;
6010
+ }
6011
+ };
6012
+ //#endregion
5845
6013
  //#region src/gameplay/score-keeper.ts
5846
6014
  /**
5847
6015
  * The game's state hub — score, lives, and win/lose detection. Put it on the
@@ -6446,6 +6614,7 @@ const GAMEPLAY_BEHAVIORS = {
6446
6614
  DamageOnContact,
6447
6615
  Interactable,
6448
6616
  Clickable,
6617
+ SavePoint,
6449
6618
  FollowCamera,
6450
6619
  Patrol,
6451
6620
  Chase,
@@ -6468,4 +6637,4 @@ function registerGameplayBehaviors(opts) {
6468
6637
  for (const [name, ctor] of Object.entries(GAMEPLAY_BEHAVIORS)) registerBehavior(name, ctor, { replace });
6469
6638
  }
6470
6639
  //#endregion
6471
- export { Water3D as A, createCausticsQuad as B, FollowCamera as C, Health as D, DamageOnContact as E, PhysicsBody3D as F, colliderFootDrop as H, RigidBody3D as I, StaticBody3D as L, WaterCutout3D as M, Area3D as N, Collector as O, CharacterBody3D as P, Node3D as R, restartScene as S, phaseOf as T, WATER_MAX_RIPPLES as V, hitStop as _, Wander as a, GameFlow as b, Projectile as c, PathFollow as d, Oscillate as f, Cooldown as g, CameraShake as h, WaveSpawner as i, WATER_CUTOUT_MAX as j, Chase as k, Pickup as l, Lifetime as m, registerGameplayBehaviors as n, Spawner as o, MoveTo as p, ZombieAI as r, ScoreKeeper as s, GAMEPLAY_BEHAVIORS as t, Patrol as u, screenFlash as v, DayNight as w, goToScene as x, Interactable as y, validateCollider3D as z };
6640
+ export { Chase as A, validateCollider3D as B, restartScene as C, DamageOnContact as D, phaseOf as E, CharacterBody3D as F, WATER_MAX_RIPPLES as H, PhysicsBody3D as I, RigidBody3D as L, WATER_CUTOUT_MAX as M, WaterCutout3D as N, Health as O, Area3D as P, StaticBody3D as R, goToScene as S, DayNight as T, colliderFootDrop as U, createCausticsQuad as V, Cooldown as _, Wander as a, Interactable as b, SavePoint as c, Patrol as d, PathFollow as f, CameraShake as g, Lifetime as h, WaveSpawner as i, Water3D as j, Collector as k, Projectile as l, MoveTo as m, registerGameplayBehaviors as n, Spawner as o, Oscillate as p, ZombieAI as r, ScoreKeeper as s, GAMEPLAY_BEHAVIORS as t, Pickup as u, hitStop as v, FollowCamera as w, GameFlow as x, screenFlash as y, Node3D as z };
@@ -1,4 +1,4 @@
1
- import { Et as Node, b as Engine, d as PropSchema, n as BehaviorCtor, t as Behavior } from "./behavior-62q0HWBO.js";
1
+ import { Dt as Node, b as Engine, d as PropSchema, n as BehaviorCtor, t as Behavior } from "./behavior-rZNfzVbH.js";
2
2
  import { c as JsonValue, s as JsonObject } from "./schema-CFeioQRE.js";
3
3
 
4
4
  //#region src/gameplay/chase.d.ts
@@ -375,6 +375,83 @@ declare class Projectile extends Behavior {
375
375
  private forwardFromRotation;
376
376
  }
377
377
  //#endregion
378
+ //#region src/gameplay/save-point.d.ts
379
+ /**
380
+ * The save, as something the scene can point a signal at.
381
+ *
382
+ * Every other common wiring in this engine became declarative — score to
383
+ * screen, damage to health bar, waves, win and lose — and saving stayed the one
384
+ * thing that needed TypeScript. The skill's own autosave example is a
385
+ * connection to `Game.onCheckpoint`, a method the author has to go and write.
386
+ *
387
+ * "*When* to save is a design decision and only your game knows" is right, and
388
+ * it is untouched here: the SCENE still says when, by choosing which signal to
389
+ * wire — a level exit's `triggerEnter`, `won`, a key. What it gets is something
390
+ * to wire TO.
391
+ *
392
+ * ```json
393
+ * { "name": "Save", "type": "Node", "uid": "n_…",
394
+ * "script": { "name": "SavePoint", "props": { "game": "vault", "slot": "1" } } }
395
+ * ```
396
+ * ```json
397
+ * { "signal": "triggerEnter", "from": "Level/Exit", "to": "Save", "handler": "save" }
398
+ * ```
399
+ */
400
+ declare class SavePoint extends Behavior {
401
+ static readonly props: PropSchema;
402
+ static readonly signals: readonly string[];
403
+ game: string;
404
+ slot: string;
405
+ label: string;
406
+ scene: string;
407
+ restoreOnReady: boolean;
408
+ probeOnReady: boolean;
409
+ /** Seconds this run has been playing — written into the slot as `playtime`. */
410
+ private elapsed;
411
+ private slots;
412
+ private store;
413
+ private pendingRestore;
414
+ private pendingProbe;
415
+ override onReady(): void;
416
+ override update(dt: number): void;
417
+ /**
418
+ * Is there anything to continue? Emits `hasSave(label, playtime, scene)` or
419
+ * `noSave`, and touches nothing.
420
+ *
421
+ * The label leads because that is what a menu shows; an unlabelled slot
422
+ * falls back to its scene key, so `hasSave → SlotInfo.setText` always says
423
+ * something rather than nothing.
424
+ */
425
+ probe(): void;
426
+ /** Seconds this run has been playing, including time carried over a continue. */
427
+ get playtime(): number;
428
+ /**
429
+ * The scene key this slot was saved from — what a loader needs to route.
430
+ * Null when the slot is empty.
431
+ */
432
+ slotScene(): string | null;
433
+ /**
434
+ * Write the run to its slot. Emits `saved(slotId)`.
435
+ *
436
+ * Wire it to whatever marks the moment. Extra signal arguments are ignored,
437
+ * so `triggerEnter → save` works as-is.
438
+ */
439
+ save(): void;
440
+ /**
441
+ * Give this scene the slot's state back. Emits `restored(count)`, or
442
+ * `noSave` when the slot is empty — which is what greys out a Continue button.
443
+ *
444
+ * Restoring does NOT route: if the slot names another scene, that is your
445
+ * loader's business, and `slot.scene` is the key it needs.
446
+ */
447
+ restore(): void;
448
+ /** Forget this slot — a "delete save" button. */
449
+ clear(): void;
450
+ /** The run's own clock survives a save/continue cycle. */
451
+ override serialize(): JsonValue;
452
+ override deserialize(data: JsonValue): void;
453
+ }
454
+ //#endregion
378
455
  //#region src/gameplay/score-keeper.d.ts
379
456
  /**
380
457
  * The game's state hub — score, lives, and win/lose detection. Put it on the
@@ -787,4 +864,4 @@ declare function registerGameplayBehaviors(opts?: {
787
864
  replace?: boolean;
788
865
  }): void;
789
866
  //#endregion
790
- export { CameraShake, Chase, Collector, Cooldown, DamageOnContact, DayNight, type DayPhase, FollowCamera, GAMEPLAY_BEHAVIORS, GameFlow, type GameFlowState, Health, Interactable, Lifetime, MoveTo, Oscillate, PathFollow, Patrol, Pickup, Projectile, ScoreKeeper, Spawner, Wander, WaveSpawner, ZombieAI, goToScene, hitStop, phaseOf, registerGameplayBehaviors, restartScene, screenFlash };
867
+ export { CameraShake, Chase, Collector, Cooldown, DamageOnContact, DayNight, type DayPhase, FollowCamera, GAMEPLAY_BEHAVIORS, GameFlow, type GameFlowState, Health, Interactable, Lifetime, MoveTo, Oscillate, PathFollow, Patrol, Pickup, Projectile, SavePoint, ScoreKeeper, Spawner, Wander, WaveSpawner, ZombieAI, goToScene, hitStop, phaseOf, registerGameplayBehaviors, restartScene, screenFlash };
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-02Btmmjn.js";
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 };
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-bStgtZBV.js";
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 };