incanto 0.69.0 → 0.71.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 (147) hide show
  1. package/bin/_behaviors-loader.mjs +22 -0
  2. package/bin/_read-json.mjs +28 -0
  3. package/bin/incanto-assets.mjs +5 -4
  4. package/bin/incanto-check.mjs +58 -31
  5. package/bin/incanto-editor.mjs +128 -5
  6. package/bin/incanto-env.mjs +3 -2
  7. package/bin/incanto-feel.mjs +24 -13
  8. package/bin/incanto-frame.mjs +8 -1
  9. package/bin/incanto-multiplay.mjs +11 -9
  10. package/bin/incanto-new.mjs +105 -10
  11. package/bin/incanto-play.mjs +158 -16
  12. package/bin/incanto-playtest.mjs +66 -23
  13. package/bin/incanto-skills.mjs +14 -2
  14. package/bin/incanto-verify.mjs +121 -102
  15. package/bin/incanto.mjs +3 -2
  16. package/dist/2d.d.ts +296 -34
  17. package/dist/2d.js +4 -4
  18. package/dist/3d.d.ts +157 -9
  19. package/dist/3d.js +8 -8
  20. package/dist/{agent8-CvsfVskX.js → agent8-Cfmd3ar_.js} +61 -8
  21. package/dist/{audio-player-C0j2k-yB.d.ts → audio-player-L8yccdMP.d.ts} +2 -2
  22. package/dist/{behavior-rm8vVzu_.d.ts → behavior-Dcz0fr1S.d.ts} +755 -46
  23. package/dist/{create-game-CwxEDlCm.js → create-game-CH4kt78v.js} +143 -60
  24. package/dist/{create-game-CvJ1wSRP.js → create-game-LCHBLfA4.js} +98 -48
  25. package/dist/debug.d.ts +1 -1
  26. package/dist/debug.js +2 -3
  27. package/dist/diagnostics-Cu85N3tL.d.ts +12 -0
  28. package/dist/{editor-switch-CnIOiyNJ.d.ts → editor-switch-CFU9mCec.d.ts} +22 -13
  29. package/dist/editor.js +1088 -882
  30. package/dist/env.d.ts +1 -1
  31. package/dist/env.js +5 -3
  32. package/dist/{environment-presets-BO-7XoFV.js → environment-presets-99BzP_L-.js} +298 -45
  33. package/dist/{frame-report-Dlq13Gyj.js → frame-report-D-_7YF2G.js} +1 -1
  34. package/dist/{gameplay-D1KixWK8.js → gameplay-CNULJvwh.js} +914 -214
  35. package/dist/gameplay.d.ts +254 -10
  36. package/dist/gameplay.js +2 -2
  37. package/dist/{heightmap-CRK0M4jT.js → heightmap-BYgD5Edk.js} +1 -1
  38. package/dist/index.d.ts +179 -13
  39. package/dist/index.js +10 -12
  40. package/dist/json-CfTjpvW8.js +67 -0
  41. package/dist/{loader-DadV2QCT.d.ts → loader-8-IHvbKD.d.ts} +2 -2
  42. package/dist/net.d.ts +27 -3
  43. package/dist/net.js +2 -2
  44. package/dist/{noise-CGUMx44x.js → noise-D3nPpmFg.js} +1 -1
  45. package/dist/{physics-2d-CF5yyjbj.js → physics-2d-FhrXUc6B.js} +136 -11
  46. package/dist/{physics-3d-DbYaEcZy.js → physics-3d-CFEGxBXZ.js} +104 -23
  47. package/dist/{teardown-CMqFVxO9.js → picking-DVo7fI13.js} +104 -13
  48. package/dist/react.d.ts +2 -2
  49. package/dist/react.js +2 -2
  50. package/dist/{register-DbVArsns.js → register-BYQCBySi.js} +429 -28
  51. package/dist/{register-mmfugpaQ.js → register-DuVdzxbq.js} +594 -831
  52. package/dist/{replay-BEHLDcQV.js → replay-DEvp3kyV.js} +224 -14
  53. package/dist/{replay-BQfkX4x7.d.ts → replay-F7IZHdFR.d.ts} +20 -3
  54. package/dist/{schema-B6ugCV1Q.d.ts → rng-Bb-IutXB.d.ts} +38 -21
  55. package/dist/{rng-DP-SR7eg.js → rng-CDOMybym.js} +22 -0
  56. package/dist/{loader-DZE7B07H.js → save-slots-Bvuh2p_r.js} +4619 -2318
  57. package/dist/{split-screen-Cg76MCV5.d.ts → split-screen-CIYf1zSf.d.ts} +36 -4
  58. package/dist/{split-screen-CauMwu6y.js → split-screen-eULetcg-.js} +159 -26
  59. package/dist/{sprite-animation-CY-mrr1L.js → sprite-animation-CqR2o3SA.js} +39 -8
  60. package/dist/{src-CYEu-_S9.js → src-_jk0qLsS.js} +2 -17
  61. package/dist/test-CmN54kWv.js +4264 -0
  62. package/dist/test.d.ts +639 -30
  63. package/dist/test.js +3 -3
  64. package/dist/touch-BnCyPA0G.js +519 -0
  65. package/dist/vite.d.ts +20 -37
  66. package/dist/vite.js +177 -7
  67. package/dist/{webgl-unavailable-Z5_g8x9H.js → webgl-unavailable-C8aDbGmR.js} +44 -0
  68. package/editor/assets/agent8-yLIEYHbd.js +1 -0
  69. package/editor/assets/{debug-CMULinBW.js → debug-u31w_yhq.js} +2 -2
  70. package/editor/assets/index-DK9xMGpW.js +11046 -0
  71. package/editor/index.html +1 -1
  72. package/package.json +4 -14
  73. package/schemas/scene.schema.json +34 -3
  74. package/skills/incanto-3d-character.md +14 -1
  75. package/skills/incanto-3d-models.md +12 -0
  76. package/skills/incanto-assets.md +9 -1
  77. package/skills/incanto-audio.md +8 -2
  78. package/skills/incanto-behaviors-and-scripts.md +105 -7
  79. package/skills/incanto-building-2d-games.md +97 -10
  80. package/skills/incanto-building-3d-games.md +58 -4
  81. package/skills/incanto-editor.md +46 -7
  82. package/skills/incanto-environment.md +19 -1
  83. package/skills/incanto-game-feel.md +70 -0
  84. package/skills/incanto-gameplay-behaviors.md +172 -20
  85. package/skills/incanto-hud.md +142 -7
  86. package/skills/incanto-localization.md +13 -5
  87. package/skills/incanto-multiplayer.md +83 -3
  88. package/skills/incanto-node-reference.md +239 -59
  89. package/skills/incanto-performance.md +52 -0
  90. package/skills/incanto-physics-and-input.md +123 -24
  91. package/skills/incanto-playtesting.md +112 -2
  92. package/skills/incanto-save-slots.md +217 -7
  93. package/skills/incanto-scene-json-authoring.md +69 -12
  94. package/skills/incanto-verifying-your-game.md +207 -10
  95. package/skills/incanto-web-integration.md +28 -0
  96. package/skills/incanto-your-first-game.md +3 -1
  97. package/templates-app/beacon-isle-3d/generate-world.ts +77 -9
  98. package/templates-app/beacon-isle-3d/package.json +2 -2
  99. package/templates-app/beacon-isle-3d/src/behaviors.ts +22 -0
  100. package/templates-app/beacon-isle-3d/src/game.scene.json +143 -600
  101. package/templates-app/beacon-isle-3d/src/main.ts +11 -1
  102. package/templates-app/beacon-isle-3d/tsconfig.json +1 -1
  103. package/templates-app/beacon-isle-3d/verify.ts +3 -1
  104. package/templates-app/molehill-2d/.incanto/playtest/lost-seed1.json +4277 -0
  105. package/templates-app/molehill-2d/PROJECT/Context.md +58 -0
  106. package/templates-app/molehill-2d/PROJECT/Requirements.md +39 -0
  107. package/templates-app/molehill-2d/PROJECT/Status.md +27 -0
  108. package/templates-app/molehill-2d/PROJECT/Structure.md +48 -0
  109. package/templates-app/molehill-2d/docs/project-2d-rules.md +44 -0
  110. package/templates-app/molehill-2d/index.html +73 -0
  111. package/templates-app/molehill-2d/package.json +23 -0
  112. package/templates-app/molehill-2d/src/behaviors.ts +198 -0
  113. package/templates-app/molehill-2d/src/game.scene.json +1205 -0
  114. package/templates-app/molehill-2d/src/main.ts +41 -0
  115. package/templates-app/molehill-2d/tsconfig.json +13 -0
  116. package/templates-app/molehill-2d/verify.ts +247 -0
  117. package/templates-app/molehill-2d/vite.config.ts +12 -0
  118. package/templates-app/platformer-2d/package.json +2 -2
  119. package/templates-app/platformer-2d/src/game.scene.json +178 -986
  120. package/templates-app/platformer-2d/src/main.ts +11 -1
  121. package/templates-app/platformer-2d/tsconfig.json +1 -1
  122. package/templates-app/star-survivor/package.json +2 -2
  123. package/templates-app/star-survivor/src/game.scene.json +43 -216
  124. package/templates-app/star-survivor/src/main.ts +11 -1
  125. package/templates-app/star-survivor/tsconfig.json +1 -1
  126. package/templates-app/tps-3d/PROJECT/Context.md +1 -1
  127. package/templates-app/tps-3d/package.json +2 -2
  128. package/templates-app/tps-3d/src/behaviors.ts +19 -1
  129. package/templates-app/tps-3d/src/game.scene.json +66 -263
  130. package/templates-app/tps-3d/src/main.ts +13 -2
  131. package/templates-app/tps-3d/tsconfig.json +1 -1
  132. package/templates-app/village-quest-3d/.incanto/playtest/swapped-seed1.json +1735 -0
  133. package/templates-app/village-quest-3d/package.json +2 -2
  134. package/templates-app/village-quest-3d/src/behaviors.ts +42 -4
  135. package/templates-app/village-quest-3d/src/grove.scene.json +68 -306
  136. package/templates-app/village-quest-3d/src/main.ts +11 -1
  137. package/templates-app/village-quest-3d/src/village.scene.json +202 -863
  138. package/templates-app/village-quest-3d/tsconfig.json +1 -1
  139. package/templates-app/village-quest-3d/verify.ts +14 -1
  140. package/dist/duplicate-Dj4L-5AA.js +0 -22
  141. package/dist/json-CwwhxQgb.js +0 -36
  142. package/dist/registry-CF70EArN.js +0 -212
  143. package/dist/rolldown-runtime-D7D4PA-g.js +0 -13
  144. package/dist/test-D6ISmuFE.js +0 -2383
  145. package/dist/touch-DEAmqGdf.js +0 -225
  146. package/editor/assets/agent8-BDvaZMQJ.js +0 -1
  147. package/editor/assets/index-CZIRr5EL.js +0 -10958
@@ -1,5 +1,5 @@
1
- import { b as Engine, d as PropSchema, jt as Node, n as BehaviorCtor, t as Behavior } from "./behavior-rm8vVzu_.js";
2
- import { c as JsonValue, s as JsonObject } from "./schema-B6ugCV1Q.js";
1
+ import { b as Engine, d as PropSchema, jt as Node, n as BehaviorCtor, t as Behavior } from "./behavior-Dcz0fr1S.js";
2
+ import { c as JsonValue, s as JsonObject } from "./rng-Bb-IutXB.js";
3
3
 
4
4
  //#region src/gameplay/chase.d.ts
5
5
  /**
@@ -58,6 +58,8 @@ declare class Collector extends Behavior {
58
58
  /** How many have been picked up so far. */
59
59
  override serialize(): JsonValue;
60
60
  override deserialize(data: JsonValue): void;
61
+ /** The counter on screen. `collect(0)` used to be the only way to repaint it. */
62
+ override announce(): void;
61
63
  }
62
64
  //#endregion
63
65
  //#region src/gameplay/currency.d.ts
@@ -85,10 +87,21 @@ declare class Currency extends Behavior {
85
87
  spend(cost: number): boolean;
86
88
  /** Take payment in. */
87
89
  earn(gain: number): void;
90
+ /** What has already been complained about, so a loop says it once. */
91
+ private complained;
92
+ /**
93
+ * A zero is ordinary; a NEGATIVE is a caller that flipped a sign.
94
+ *
95
+ * Reported rather than thrown: this is called from game code inside a frame,
96
+ * and a throw here quarantines the node holding the player's money.
97
+ */
98
+ private refuseNonsense;
88
99
  /** Set it outright (a shop that grants, a cheat, a restore). */
89
100
  setAmount(next: number): void;
90
101
  override serialize(): JsonValue;
91
102
  override deserialize(data: JsonValue): void;
103
+ /** The wallet on screen — `setAmount`'s own doc comment already named a restore. */
104
+ override announce(): void;
92
105
  }
93
106
  //#endregion
94
107
  //#region src/gameplay/health.d.ts
@@ -116,6 +129,7 @@ declare class Health extends Behavior {
116
129
  regenPerSec: number;
117
130
  invulnerableFor: number;
118
131
  freeOnDeath: boolean;
132
+ freeParent: boolean;
119
133
  /** Current hit points (set to `max` on ready). */
120
134
  current: number;
121
135
  private invulnTimer;
@@ -194,6 +208,14 @@ declare class Health extends Behavior {
194
208
  /** Current HP and whether death already fired — `max` comes back from JSON. */
195
209
  override serialize(): JsonValue;
196
210
  override deserialize(data: JsonValue): void;
211
+ /**
212
+ * Repaint the bar. Not `died` — a save is being read, nobody just died.
213
+ *
214
+ * This one needed the hook most: `setCurrent` is private AND early-returns
215
+ * when the value is unchanged, so no call a game could make would repaint a
216
+ * restored bar. It read 100 at 38 HP until the next hit teleported it to 37.
217
+ */
218
+ override announce(): void;
197
219
  }
198
220
  //#endregion
199
221
  //#region src/gameplay/damage-on-contact.d.ts
@@ -297,6 +319,7 @@ declare class FollowCamera extends Behavior {
297
319
  offset: number[];
298
320
  smoothing: number;
299
321
  deadzone: number;
322
+ private shakeRng;
300
323
  override onReady(): void;
301
324
  private shakeMag;
302
325
  private shakeT;
@@ -321,6 +344,8 @@ declare class Interactable extends Behavior {
321
344
  action: string;
322
345
  range: number;
323
346
  actorGroup: string;
347
+ /** Actions already reported missing, so a per-frame read says it once. */
348
+ private readonly reportedActions;
324
349
  override update(): void;
325
350
  }
326
351
  //#endregion
@@ -466,7 +491,31 @@ declare class Patrol extends Behavior {
466
491
  * this one is invisible to it.
467
492
  */
468
493
  private reportDeadWaypoint;
469
- /** Resolve waypoint `i` to a position, reading node paths live. */
494
+ /**
495
+ * Resolve waypoint `i` to a position in the MOVER's frame, reading node paths
496
+ * live.
497
+ *
498
+ * A node-path waypoint used to return the marker's raw `position`, which is
499
+ * parent-relative — and it was then fed to `moveToward(mover.position, …)`,
500
+ * i.e. read in the MOVER's frame. So a walker and its markers under different
501
+ * parents walked a shifted copy of the path, silently, with the marker nodes
502
+ * sitting right there in the scene. Measured on a tower-defence lane
503
+ * organised the ordinary way (markers under `/root/Path`, spawns under a
504
+ * container at [-50, 90]): every creep stacked motionless 90 px below and
505
+ * 72 px short of the keep, `incanto-check` `1/1 scene(s) valid`, zero errors,
506
+ * zero warnings, and the wave could be neither won nor lost.
507
+ *
508
+ * `Chase` has converted through world space since the identical bug was found
509
+ * there ("comparing two nodes' raw `position` only worked while they happened
510
+ * to share a parent"), and a `FollowCamera` aimed at a node uses its world
511
+ * position too. Patrol was the outlier.
512
+ *
513
+ * A LITERAL array stays local, because that is the only frame an author can
514
+ * mean when they type numbers into a behaviour's props.
515
+ *
516
+ * `reportDeadWaypoint` could not see this: it fires when a path resolves to
517
+ * NOTHING, and a path that resolves into the wrong frame resolves fine.
518
+ */
470
519
  private pointAt;
471
520
  private advance;
472
521
  }
@@ -508,14 +557,58 @@ declare class Pickup extends Behavior {
508
557
  * `null` (the engine's "any JSON" escape hatch) and `null` means `'forward'`.
509
558
  * `gravity` adds a constant downward (+y, the 2D y-down convention) pull, for
510
559
  * lobbed/arcing shots.
560
+ *
561
+ * **Every prop here is live.** `speed` and `direction` re-bake the heading when
562
+ * written, and the heading is resolved on the FIRST STEP rather than at
563
+ * `onReady` — because `onReady` is before the one place a game aims a spawned
564
+ * bullet. `SpawnSource.spawn` ends with `parent.addChild(clone)` and `Spawner`
565
+ * emits `spawned(node)` after that, so a handler that sets a rotation or a
566
+ * direction was always too late. Measured at speed 100 over 60 frames:
567
+ *
568
+ * ```
569
+ * A rotation 0 direction [0,1] -> pos [ 0, 100] authored: obeyed
570
+ * B rotation 90 direction "forward" -> pos [ 0, 100] authored: obeyed
571
+ * C rotation 90 set after addChild -> pos [100, 0] IGNORED
572
+ * D direction [0,1] set after add -> pos [100, 0] IGNORED — and it read
573
+ * back [0,1] while
574
+ * travelling +x
575
+ * ```
576
+ *
577
+ * What is deliberately NOT live is `rotation` after that first step: a
578
+ * projectile that spins for looks would have its spin become its trajectory.
579
+ * Call `aim()` to re-derive from the current rotation on purpose.
511
580
  */
512
581
  declare class Projectile extends Behavior {
513
582
  static readonly props: PropSchema;
514
583
  static readonly signals: readonly string[];
515
- speed: number;
516
- direction: JsonValue;
584
+ private _speed;
585
+ private _direction;
517
586
  gravity: number;
587
+ /** The straight-line part, `direction × speed`. Empty until the first step. */
518
588
  private velocity;
589
+ /**
590
+ * The +y accumulated by `gravity`, kept APART from `velocity`.
591
+ *
592
+ * Re-aiming replaces the straight-line part; the fall is a fact about the
593
+ * world, not about the shot, so an arrow re-aimed halfway down keeps falling
594
+ * rather than snapping back to level.
595
+ */
596
+ private fall;
597
+ /** Units per second along `direction`. Writable at any time. */
598
+ get speed(): number;
599
+ set speed(value: number);
600
+ /** A vector (`[x,y(,z)]`) or `'forward'`. Writable at any time. */
601
+ get direction(): JsonValue;
602
+ set direction(value: JsonValue);
603
+ private get armed();
604
+ /**
605
+ * Re-derive the heading from `direction` (or, for `'forward'`, from the
606
+ * node's CURRENT rotation) and bake it into the velocity.
607
+ *
608
+ * This is what a `spawned(node)` handler calls after turning the clone, and
609
+ * the only way a `'forward'` projectile ever re-reads its rotation.
610
+ */
611
+ aim(direction?: JsonValue | undefined): void;
519
612
  override onReady(): void;
520
613
  override update(dt: number): void;
521
614
  /** Resolve `direction` to a unit vector, deriving 'forward' from rotation. */
@@ -642,6 +735,14 @@ declare class ScoreKeeper extends Behavior {
642
735
  /** The run's progress. `scoreToWin` is balance and comes back from JSON. */
643
736
  override serialize(): JsonValue;
644
737
  override deserialize(data: JsonValue): void;
738
+ /**
739
+ * The score line and the lives counter. NOT `won`/`lost`.
740
+ *
741
+ * Re-firing those on load is how a Continue lands straight on the game-over
742
+ * screen it was loaded to escape — the flags come back through `hasWon` /
743
+ * `hasLost` and the game reads them, rather than being told again.
744
+ */
745
+ override announce(): void;
645
746
  }
646
747
  //#endregion
647
748
  //#region src/gameplay/spawner.d.ts
@@ -679,6 +780,7 @@ declare class Spawner extends Behavior {
679
780
  get liveCount(): number;
680
781
  /** @internal The detached template node (test/debug only). */
681
782
  _templateForTest(): Node;
783
+ override onEnterTree(): void;
682
784
  override onReady(): void;
683
785
  /** Begin (or resume) interval spawning. */
684
786
  start(): void;
@@ -724,8 +826,9 @@ declare class Wander extends Behavior {
724
826
  * every `interval` seconds, then waits until they're ALL cleared (freed) before
725
827
  * starting the next wave.
726
828
  *
727
- * Signals: `waveStarted(i)` when a wave begins spawning, `waveCleared(i)` when
728
- * its last instance frees, `allCleared` after the final wave clears.
829
+ * Signals: `spawned(node)` per clone, `waveStarted(i)` when a wave begins
830
+ * spawning, `waveCleared(i)` when its last instance frees, `allCleared` after
831
+ * the final wave clears.
729
832
  */
730
833
  declare class WaveSpawner extends Behavior {
731
834
  static readonly props: PropSchema;
@@ -740,9 +843,27 @@ declare class WaveSpawner extends Behavior {
740
843
  private timer;
741
844
  private spawnedThisWave;
742
845
  private announced;
846
+ override onEnterTree(): void;
743
847
  override onReady(): void;
744
848
  /** Start (or restart from) wave 0. */
745
849
  start(): void;
850
+ /**
851
+ * Which wave you were on — and nothing else.
852
+ *
853
+ * `behaviorsWithoutSave` is documented as "the list to read before shipping",
854
+ * and nobody had read it against this repo's own examples. Five of them put a
855
+ * `WaveSpawner` at the centre of the game (`fps-3d`, `tps-3d`, `tps-3d-neon`,
856
+ * `tps-3d-ruins`, `dogfight-3d`), and every one of them saved a run on wave 3
857
+ * and continued it on wave 1 — silently, because a save reloads the scene
858
+ * from source and a fresh `onReady` honestly starts at the beginning.
859
+ *
860
+ * The wave RESTARTS rather than resuming mid-spawn, which is the same
861
+ * checkpoint rule the rest of the save system follows: spawned entities are
862
+ * never restored, so resuming "four enemies into wave 3" would mean resuming
863
+ * a wave whose enemies do not exist. `waveStarted` fires again for the HUD.
864
+ */
865
+ override serialize(): JsonValue;
866
+ override deserialize(data: JsonValue): void;
746
867
  override update(dt: number): void;
747
868
  private beginWave;
748
869
  private prune;
@@ -834,6 +955,18 @@ declare class DayNight extends Behavior {
834
955
  private baseAmbient;
835
956
  private baseExposure;
836
957
  private captured;
958
+ /** Said once: there was no sky to move when this first looked. */
959
+ private reportedNoSky;
960
+ /**
961
+ * What time it is. A save that forgets it returns you at dawn.
962
+ *
963
+ * `startHour` is the scene's opening, not the run's clock: a fresh `onReady`
964
+ * cannot know that this player has been out all night. `paused` goes with it
965
+ * because a game that stops the clock for a cutscene must not resume ticking
966
+ * on load.
967
+ */
968
+ override serialize(): JsonValue;
969
+ override deserialize(data: JsonValue): void;
837
970
  override onReady(): void;
838
971
  override update(dt: number): void;
839
972
  private applyHour;
@@ -904,6 +1037,25 @@ type GameFlowState = "playing" | "paused" | "gameover" | "won";
904
1037
  /**
905
1038
  * Swap to another scene behind a black fade (title→game→next level).
906
1039
  * Headless (no DOM) the swap is immediate. Restores timeScale to 1.
1040
+ *
1041
+ * ONE AT A TIME. A door is usually a trigger, and a player who walks along a
1042
+ * wall of them fires several inside one fade. Measured on three doorways passed
1043
+ * in about a second:
1044
+ *
1045
+ * ```
1046
+ * setScene calls: 3 | order: ["crypt","garden","forge"]
1047
+ * final scene : forge
1048
+ * ```
1049
+ *
1050
+ * Three complete scenes loaded and the player landed in the third — a level
1051
+ * they never walked to, from a door they never reached. Further calls while a
1052
+ * travel is in flight are IGNORED rather than queued: you already went through
1053
+ * a door, and the second one is the same input arriving twice.
1054
+ *
1055
+ * The outgoing scene also stops while the screen is black. It kept simulating
1056
+ * through the fade, so a player who triggered the door mid-fall died in the
1057
+ * level they were leaving — and the checkpoint that death wrote was for the
1058
+ * level they had left.
907
1059
  */
908
1060
  declare function goToScene(engine: Engine, sceneJson: JsonObject, opts?: {
909
1061
  fadeSeconds?: number;
@@ -961,8 +1113,24 @@ declare class GameFlow extends Behavior {
961
1113
  state: GameFlowState;
962
1114
  private frozeScale;
963
1115
  override onReady(): void;
964
- /** The panel follows the state; nothing else may own its visibility. */
1116
+ /**
1117
+ * The panel follows the state; nothing else may own its visibility.
1118
+ *
1119
+ * …and while it is up, the layer it lives on takes ARROW KEYS AND THE PAD.
1120
+ * `HudLayer.focusNavigation` is off by default and rightly so — a game whose
1121
+ * HUD happens to contain a button must not lose its arrow keys the moment one
1122
+ * exists — and `incanto-hud.md` says "turn it on for the screens that ARE
1123
+ * menus and off again when play resumes". A pause panel IS that screen, and
1124
+ * this is the one place in the engine that knows when it opens and closes, so
1125
+ * every game with a pause menu was writing the same two lines or shipping a
1126
+ * menu no controller could reach.
1127
+ *
1128
+ * The layer's own value is remembered and put back: a game that turned
1129
+ * navigation on for its own reasons keeps it after a pause.
1130
+ */
965
1131
  private syncPausePanel;
1132
+ /** The layer's own `focusNavigation`, while this flow is borrowing it. */
1133
+ private navigationWas;
966
1134
  gameOver(text?: string, color?: string): void;
967
1135
  win(text?: string, color?: string): void;
968
1136
  pause(): void;
@@ -971,7 +1139,7 @@ declare class GameFlow extends Behavior {
971
1139
  /** Fade to another scene (next level, back to title). */
972
1140
  goToScene(sceneJson: JsonObject, opts?: {
973
1141
  fadeSeconds?: number;
974
- }): void;
1142
+ } | undefined): void;
975
1143
  private transition;
976
1144
  private freeze;
977
1145
  private thaw;
@@ -1025,6 +1193,19 @@ declare class CameraShake extends Behavior {
1025
1193
  private t;
1026
1194
  private prev;
1027
1195
  /**
1196
+ * Decoration draws from its OWN stream — see `effectRng`.
1197
+ *
1198
+ * A shake drew from `engine.rng`, the stream game logic reads and
1199
+ * `captureState` saves, once per axis per RENDERED frame. So the player's
1200
+ * frame rate decided the game's random numbers, and toggling the
1201
+ * `reduceMotion` accessibility switch changed the rolls: a replay recorded at
1202
+ * 60 fps does not reproduce on a laptop that dropped to 30, and two players
1203
+ * with the same seed diverge because one of them was hit. The particle
1204
+ * siblings were moved off `engine.rng` for exactly this; both camera shakes
1205
+ * were left behind.
1206
+ */
1207
+ private shakeRng;
1208
+ /**
1028
1209
  * Start a shake — unless the player asked for no motion.
1029
1210
  *
1030
1211
  * `settings.reduceMotion` is an accessibility switch, and camera movement the
@@ -1085,6 +1266,69 @@ declare class PathFollow extends Behavior {
1085
1266
  override update(dt: number): void;
1086
1267
  }
1087
1268
  //#endregion
1269
+ //#region src/gameplay/prefab-shelf.d.ts
1270
+ /**
1271
+ * A shelf of templates the GAME clones itself — towers, bolts, damage numbers.
1272
+ *
1273
+ * `Spawner` detaches its one prefab at enter, so a spawned enemy's whole
1274
+ * subtree is live and nobody has to think about it. A game that clones on its
1275
+ * own terms — a tower the player places, a bolt that tower fires — has no
1276
+ * spawner, so its templates sat in the tree as LIVE nodes: their behaviours
1277
+ * ticked, their turrets shot, and `visible: false` hid a node without switching
1278
+ * it off. The documented answer was to author every template `"enabled": false`
1279
+ * and wake the clone with `clone.behavior?.enable()`.
1280
+ *
1281
+ * That wakes ONE node, and the same docs teach that a node carries one
1282
+ * behaviour — so a prefab's parts live on children, and every one of those
1283
+ * stays asleep, silently and for good, because `enabled` is a pause switch that
1284
+ * never complains. Measured on a shipped tower defense: the bolt's
1285
+ * `Projectile` was woken and its sibling `Lifetime` was not, so every bolt that
1286
+ * MISSED flew forever — five still in the tree at 69 seconds, in a game that
1287
+ * passed its harness, its audit and every rung of the ladder.
1288
+ *
1289
+ * ```json
1290
+ * { "name": "Prefabs", "type": "Node2D", "script": { "name": "PrefabShelf" },
1291
+ * "children": [ { "name": "Tower", "type": "Node2D", "children": [] } ] }
1292
+ * ```
1293
+ * ```ts
1294
+ * const shelf = this.node.getNode('/Game/Prefabs').behavior as PrefabShelf;
1295
+ * const tower = shelf.make('Tower'); // detached, like `duplicateNode`
1296
+ * tower.position = at; // set it up BEFORE it readies
1297
+ * this.node.getNode('/Game/Towers').addChild(tower);
1298
+ * ```
1299
+ *
1300
+ * The children never ENTER the tree, so nothing has to be asleep, nothing has
1301
+ * to be hidden, and `make()` hands back a clone that is awake all the way down.
1302
+ */
1303
+ declare class PrefabShelf extends Behavior {
1304
+ static readonly props: PropSchema;
1305
+ static readonly signals: readonly string[];
1306
+ prefabs: string;
1307
+ /** Templates, in the order they were authored. */
1308
+ private readonly held;
1309
+ override onEnterTree(): void;
1310
+ /** A shelf attached at runtime never saw `onEnterTree`; idempotent. */
1311
+ override onReady(): void;
1312
+ private claim;
1313
+ /** What this shelf holds, in authored order. */
1314
+ names(): string[];
1315
+ /**
1316
+ * A live clone of one template, DETACHED — `addChild` it yourself.
1317
+ *
1318
+ * Detached for the same reason `duplicateNode` is, and it is not a
1319
+ * formality: `onReady` fires on attach, and a behaviour that reads its node
1320
+ * there (`FloatAway` banks its starting position) would bank the wrong one.
1321
+ * Set the clone up, then attach it.
1322
+ *
1323
+ * The clone is VISIBLE even if the template was authored hidden (a scene
1324
+ * migrating off the old `visible: false` shelf keeps working), and every
1325
+ * behaviour in it is however it was authored: awake unless the template
1326
+ * itself asks to sleep, which is now only ever a state machine's own doing.
1327
+ */
1328
+ make(name: string): Node;
1329
+ private childNames;
1330
+ }
1331
+ //#endregion
1088
1332
  //#region src/gameplay/spatial.d.ts
1089
1333
  /**
1090
1334
  * A node's position with every spatial ancestor's added in.
@@ -1131,4 +1375,4 @@ declare function registerGameplayBehaviors(opts?: {
1131
1375
  replace?: boolean;
1132
1376
  }): void;
1133
1377
  //#endregion
1134
- export { CameraShake, Chase, Collector, Cooldown, Currency, DamageOnContact, DayNight, type DayPhase, FaceTarget, FloatAway, FollowCamera, GAMEPLAY_BEHAVIORS, GameFlow, type GameFlowState, Health, Interactable, Lifetime, MoveTo, Oscillate, PathFollow, Patrol, Pickup, Projectile, SavePoint, ScoreKeeper, Spawner, Wander, WaveSpawner, ZombieAI, goToScene, hitStop, localFromWorld, phaseOf, registerGameplayBehaviors, restartScene, screenFlash, worldPosition };
1378
+ export { CameraShake, Chase, Collector, Cooldown, Currency, DamageOnContact, DayNight, type DayPhase, FaceTarget, FloatAway, FollowCamera, GAMEPLAY_BEHAVIORS, GameFlow, type GameFlowState, Health, Interactable, Lifetime, MoveTo, Oscillate, PathFollow, Patrol, Pickup, PrefabShelf, Projectile, SavePoint, ScoreKeeper, Spawner, Wander, WaveSpawner, ZombieAI, goToScene, hitStop, localFromWorld, phaseOf, registerGameplayBehaviors, restartScene, screenFlash, worldPosition };
package/dist/gameplay.js CHANGED
@@ -1,2 +1,2 @@
1
- import { A as DamageOnContact, C as restartScene, D as FaceTarget, E as FloatAway, F as localFromWorld, I as worldPosition, M as Currency, N as Collector, O as DayNight, P as Chase, S as goToScene, T as FollowCamera, _ 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, j as Health, k as phaseOf, 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, x as GameFlow, y as screenFlash } from "./gameplay-D1KixWK8.js";
2
- export { CameraShake, Chase, Collector, Cooldown, Currency, DamageOnContact, DayNight, FaceTarget, FloatAway, FollowCamera, GAMEPLAY_BEHAVIORS, GameFlow, Health, Interactable, Lifetime, MoveTo, Oscillate, PathFollow, Patrol, Pickup, Projectile, SavePoint, ScoreKeeper, Spawner, Wander, WaveSpawner, ZombieAI, goToScene, hitStop, localFromWorld, phaseOf, registerGameplayBehaviors, restartScene, screenFlash, worldPosition };
1
+ import { A as DayNight, C as goToScene, E as FollowCamera, F as Collector, I as Chase, M as DamageOnContact, N as Health, O as FloatAway, P as Currency, Q as worldPosition, S as GameFlow, Z as localFromWorld, _ as CameraShake, a as Wander, b as screenFlash, c as SavePoint, d as Pickup, f as Patrol, g as Lifetime, h as MoveTo, i as WaveSpawner, j as phaseOf, k as FaceTarget, l as Projectile, m as Oscillate, n as registerGameplayBehaviors, o as Spawner, p as PathFollow, r as ZombieAI, s as ScoreKeeper, t as GAMEPLAY_BEHAVIORS, u as PrefabShelf, v as Cooldown, w as restartScene, x as Interactable, y as hitStop } from "./gameplay-CNULJvwh.js";
2
+ export { CameraShake, Chase, Collector, Cooldown, Currency, DamageOnContact, DayNight, FaceTarget, FloatAway, FollowCamera, GAMEPLAY_BEHAVIORS, GameFlow, Health, Interactable, Lifetime, MoveTo, Oscillate, PathFollow, Patrol, Pickup, PrefabShelf, Projectile, SavePoint, ScoreKeeper, Spawner, Wander, WaveSpawner, ZombieAI, goToScene, hitStop, localFromWorld, phaseOf, registerGameplayBehaviors, restartScene, screenFlash, worldPosition };
@@ -1,4 +1,4 @@
1
- import { t as createNoise2D } from "./noise-CGUMx44x.js";
1
+ import { t as createNoise2D } from "./noise-D3nPpmFg.js";
2
2
  //#region src/3d/terrain/heightmap.ts
3
3
  /**
4
4
  * Pure heightfield + splat-weight math for `Terrain3D` — NO three imports, so