incanto 0.66.0 → 0.68.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.
- package/bin/_behaviors-loader.mjs +87 -0
- package/bin/_pick-server.mjs +48 -0
- package/bin/incanto-check.mjs +16 -2
- package/bin/incanto-editor.mjs +39 -2
- package/bin/incanto-feel.mjs +4 -1
- package/bin/incanto-frame.mjs +37 -3
- package/bin/incanto-logs.mjs +23 -3
- package/bin/incanto-multiplay.mjs +50 -1
- package/bin/incanto-play.mjs +3 -1
- package/bin/incanto-playtest.mjs +3 -25
- package/bin/incanto-verify.mjs +15 -2
- package/dist/2d.d.ts +44 -7
- package/dist/2d.js +6 -4
- package/dist/3d.d.ts +112 -16
- package/dist/3d.js +8 -6
- package/dist/{audio-player-_UAcHxnC.d.ts → audio-player-DOrq7sP-.d.ts} +62 -4
- package/dist/{behavior-BXNLfIJk.d.ts → behavior-DoFPYrgo.d.ts} +117 -2
- package/dist/boot-failure-CKYrEcGF.js +47 -0
- package/dist/{create-game-DYJCIzO0.js → create-game-DbWtVTxD.js} +91 -29
- package/dist/{create-game-Dd7H4bJV.js → create-game-IZIydDwI.js} +16 -10
- package/dist/debug.d.ts +1 -1
- package/dist/debug.js +3 -2
- package/dist/{duplicate-CqSAtdrh.js → duplicate-IWIqk0HJ.js} +1 -1
- package/dist/{editor-switch-DAvWQeld.d.ts → editor-switch-CnIOiyNJ.d.ts} +1 -1
- package/dist/editor.js +18 -7
- package/dist/env.d.ts +1 -1
- package/dist/{environment-presets-BlPsEmq6.js → environment-presets-CybQXNqS.js} +100 -11
- package/dist/{gameplay-D1RADWu3.js → gameplay-DM1eu_cV.js} +46 -5
- package/dist/gameplay.d.ts +75 -6
- package/dist/gameplay.js +2 -2
- package/dist/index.d.ts +52 -8
- package/dist/index.js +9 -7
- package/dist/{loader-lQDCwNag.js → loader-BC4PNtJX.js} +173 -1
- package/dist/{loader-Cga7FVP4.d.ts → loader-CcB533FR.d.ts} +2 -2
- package/dist/net.d.ts +2 -2
- package/dist/net.js +1 -1
- package/dist/{physics-2d-Cji5A6sX.js → physics-2d-B7Y6dPZO.js} +29 -2
- package/dist/{physics-3d-BkHjwJgI.js → physics-3d-bG3n70Ky.js} +30 -3
- package/dist/react.d.ts +2 -2
- package/dist/react.js +1 -1
- package/dist/{register-BmuqYTiY.js → register-3ta-2Xig.js} +148 -22
- package/dist/{register-D0CxCveZ.js → register-ibCjm-wH.js} +3 -3
- package/dist/{replay-IsZbNu6d.js → replay-CYvhVHHN.js} +28 -4
- package/dist/{replay-BHoB6fCU.d.ts → replay-Dvn8aeBd.d.ts} +38 -4
- package/dist/{schema-CFeioQRE.d.ts → schema-B6ugCV1Q.d.ts} +11 -0
- package/dist/{split-screen-D_i7GRcY.js → split-screen-CYwDkbLF.js} +2 -2
- package/dist/{split-screen-eJUFjhi1.d.ts → split-screen-DhrSzZIB.d.ts} +3 -3
- package/dist/{src-BTLbXFPZ.js → src-D7RIqXYF.js} +1 -1
- package/dist/{teardown-byR9USax.js → teardown-D2NEmxPB.js} +2 -2
- package/dist/{test-DgrD0jHD.js → test-8hoHeRmo.js} +140 -32
- package/dist/test.d.ts +9 -7
- package/dist/test.js +3 -3
- package/dist/{touch-DESwnpOc.js → touch-DEAmqGdf.js} +30 -186
- package/dist/vite.d.ts +0 -7
- package/dist/vite.js +13 -3
- package/dist/webgl-unavailable-N9nQqesw.js +228 -0
- package/editor/assets/{agent8-9N-Pd_YS.js → agent8-BrrHOjMJ.js} +1 -1
- package/editor/assets/debug-BBhKuBNV.js +3 -0
- package/editor/assets/{index-CeDhIPTC.js → index-eVd0BToA.js} +92 -92
- package/editor/index.html +1 -1
- package/package.json +1 -1
- package/schemas/scene.schema.json +5 -1
- package/skills/incanto-3d-character.md +1 -0
- package/skills/incanto-audio.md +49 -0
- package/skills/incanto-building-2d-games.md +37 -0
- package/skills/incanto-building-3d-games.md +56 -0
- package/skills/incanto-editor.md +5 -1
- package/skills/incanto-environment.md +2 -2
- package/skills/incanto-gameplay-behaviors.md +29 -2
- package/skills/incanto-hud.md +14 -1
- package/skills/incanto-multiplayer.md +14 -0
- package/skills/incanto-node-reference.md +2 -1
- package/skills/incanto-performance.md +35 -0
- package/skills/incanto-physics-and-input.md +23 -0
- package/skills/incanto-playtesting.md +15 -1
- package/skills/incanto-save-slots.md +21 -1
- package/skills/incanto-scene-json-authoring.md +12 -1
- package/skills/incanto-verifying-your-game.md +19 -0
- package/skills/incanto-your-first-game.md +4 -4
- package/templates-app/beacon-isle-3d/package.json +1 -1
- package/templates-app/beacon-isle-3d/src/game.scene.json +38 -19
- package/templates-app/beacon-isle-3d/src/main.ts +8 -1
- package/templates-app/platformer-2d/package.json +1 -1
- package/templates-app/platformer-2d/src/game.scene.json +99 -53
- package/templates-app/platformer-2d/src/main.ts +8 -1
- package/templates-app/star-survivor/package.json +1 -1
- package/templates-app/star-survivor/src/game.scene.json +31 -17
- package/templates-app/star-survivor/src/main.ts +8 -1
- package/templates-app/tps-3d/package.json +1 -1
- package/templates-app/tps-3d/src/game.scene.json +14 -8
- package/templates-app/tps-3d/src/main.ts +8 -1
- package/templates-app/village-quest-3d/package.json +1 -1
- package/templates-app/village-quest-3d/src/grove.scene.json +20 -11
- package/templates-app/village-quest-3d/src/main.ts +8 -1
- package/templates-app/village-quest-3d/src/village.scene.json +10 -7
- package/editor/assets/debug-CkbJICYp.js +0 -3
package/dist/gameplay.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { b as Engine, d as PropSchema, jt as Node, n as BehaviorCtor, t as Behavior } from "./behavior-
|
|
2
|
-
import { c as JsonValue, s as JsonObject } from "./schema-
|
|
1
|
+
import { b as Engine, d as PropSchema, jt as Node, n as BehaviorCtor, t as Behavior } from "./behavior-DoFPYrgo.js";
|
|
2
|
+
import { c as JsonValue, s as JsonObject } from "./schema-B6ugCV1Q.js";
|
|
3
3
|
|
|
4
4
|
//#region src/gameplay/chase.d.ts
|
|
5
5
|
/**
|
|
@@ -143,16 +143,37 @@ declare class Health extends Behavior {
|
|
|
143
143
|
* unlosable. Every checker was green on it, and `incanto-playtest` even
|
|
144
144
|
* reported `lost in 19/20` because a single `died` reads as a loss.
|
|
145
145
|
*
|
|
146
|
-
* Wire
|
|
146
|
+
* **Wire `reviveFull`, not this, to `lifeLost`.** `ScoreKeeper.loseLife()`
|
|
147
|
+
* emits `lifeLost` WITH the remaining life count, and this method's first
|
|
148
|
+
* parameter is the HP to come back at — so the documented recipe revived at
|
|
149
|
+
* 2 HP, then 1, then 0, and nothing warned (the wire-arity guard fires only
|
|
150
|
+
* when a handler is UNDER-fed, and `hp` is optional):
|
|
147
151
|
*
|
|
148
|
-
* ```
|
|
149
|
-
*
|
|
152
|
+
* ```
|
|
153
|
+
* death 1: lives=2 hp=2
|
|
154
|
+
* death 2: lives=1 hp=1
|
|
155
|
+
* death 3: lives=0 hp=0 dead=false <- alive at zero HP
|
|
150
156
|
* ```
|
|
151
157
|
*
|
|
158
|
+
* Reach for `revive(hp)` when your game genuinely wants a partial comeback.
|
|
159
|
+
*
|
|
152
160
|
* A no-op on the living — reviving someone who never died would silently
|
|
153
161
|
* refill their health, which is a different feature and not this one.
|
|
154
162
|
*/
|
|
155
163
|
revive(hp?: number): void;
|
|
164
|
+
/**
|
|
165
|
+
* Bring a dead one back at FULL health — the handler a life-lost wire wants.
|
|
166
|
+
*
|
|
167
|
+
* It exists because `revive` takes an optional `hp` and `lifeLost` carries the
|
|
168
|
+
* life count, so wiring the two silently made "you have 2 lives left" mean
|
|
169
|
+
* "come back at 2 HP". This takes no arguments, so no signal's payload can
|
|
170
|
+
* ever change what it means.
|
|
171
|
+
*
|
|
172
|
+
* ```json
|
|
173
|
+
* { "signal": "lifeLost", "from": "Score", "to": "Player", "handler": "reviveFull" }
|
|
174
|
+
* ```
|
|
175
|
+
*/
|
|
176
|
+
reviveFull(): void;
|
|
156
177
|
/** Drop to 0 and die immediately (ignores i-frames). */
|
|
157
178
|
kill(): void;
|
|
158
179
|
/**
|
|
@@ -537,6 +558,20 @@ declare class SavePoint extends Behavior {
|
|
|
537
558
|
private elapsed;
|
|
538
559
|
private slots;
|
|
539
560
|
private store;
|
|
561
|
+
/**
|
|
562
|
+
* Has this run already said the saves are going nowhere?
|
|
563
|
+
*
|
|
564
|
+
* `saved(slot)` is the engine's own instrument for "it saved", and it fired
|
|
565
|
+
* identically in a private window where the write landed in a Map that dies
|
|
566
|
+
* with the tab. In-session everything looked healthy — the slot read back,
|
|
567
|
+
* `slotScene()` answered — and across the reload the score went 5 to 0 and
|
|
568
|
+
* `list()` went `["1"]` to `[]`, with `engine.log.entries().length = 0` and
|
|
569
|
+
* `stats().errors = 0` in every phase. Byte-identical to a healthy control.
|
|
570
|
+
*
|
|
571
|
+
* So it goes through `engine.log`, not just the console: that is what
|
|
572
|
+
* `incanto-logs` reads and what the `says` rung reports.
|
|
573
|
+
*/
|
|
574
|
+
private warnedVolatile;
|
|
540
575
|
private pendingRestore;
|
|
541
576
|
private pendingProbe;
|
|
542
577
|
override onReady(): void;
|
|
@@ -1050,6 +1085,40 @@ declare class PathFollow extends Behavior {
|
|
|
1050
1085
|
override update(dt: number): void;
|
|
1051
1086
|
}
|
|
1052
1087
|
//#endregion
|
|
1088
|
+
//#region src/gameplay/spatial.d.ts
|
|
1089
|
+
/**
|
|
1090
|
+
* A node's position with every spatial ancestor's added in.
|
|
1091
|
+
*
|
|
1092
|
+
* The tree composes transforms for DRAWING — three does it for free — and every
|
|
1093
|
+
* piece of engine logic that reads `node.position` gets the LOCAL number
|
|
1094
|
+
* instead. Physics already knew (`worldPosition2D`/`worldPosition3D` in the
|
|
1095
|
+
* adapters); the camera did not, and neither did anything that chases a target.
|
|
1096
|
+
*
|
|
1097
|
+
* A camera parented to the player is the Godot and Phaser idiom, and the
|
|
1098
|
+
* authoring skill explicitly permits it — *"EVERY node type can hold children …
|
|
1099
|
+
* any node works as a grouping container"*. Measured on exactly that scene:
|
|
1100
|
+
*
|
|
1101
|
+
* ```
|
|
1102
|
+
* player world position : [1400, 900]
|
|
1103
|
+
* renderer view centre : {"x":0,"y":0}
|
|
1104
|
+
* framing : camera /Level/Player/Cam centred [1400, 900]
|
|
1105
|
+
* 1 in view, 0 outside it
|
|
1106
|
+
* ```
|
|
1107
|
+
*
|
|
1108
|
+
* The renderer drew empty world 1600 px from the player while `framing` — the
|
|
1109
|
+
* instrument the skills tell you to trust before handing a game back —
|
|
1110
|
+
* certified the player on screen.
|
|
1111
|
+
*
|
|
1112
|
+
* Arity comes from the node itself: a 2D node has two components, a 3D node
|
|
1113
|
+
* three, and an ancestor that is neither contributes nothing.
|
|
1114
|
+
*/
|
|
1115
|
+
declare function worldPosition(node: Node): number[];
|
|
1116
|
+
/**
|
|
1117
|
+
* Turn a world position into the local one `node.position` has to hold for the
|
|
1118
|
+
* node to END UP there — the inverse of `worldPosition` for its parent chain.
|
|
1119
|
+
*/
|
|
1120
|
+
declare function localFromWorld(node: Node, world: readonly number[]): number[];
|
|
1121
|
+
//#endregion
|
|
1053
1122
|
//#region src/gameplay/index.d.ts
|
|
1054
1123
|
/** Every built-in gameplay behavior, keyed by its registration name. */
|
|
1055
1124
|
declare const GAMEPLAY_BEHAVIORS: Readonly<Record<string, BehaviorCtor>>;
|
|
@@ -1062,4 +1131,4 @@ declare function registerGameplayBehaviors(opts?: {
|
|
|
1062
1131
|
replace?: boolean;
|
|
1063
1132
|
}): void;
|
|
1064
1133
|
//#endregion
|
|
1065
|
-
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, phaseOf, registerGameplayBehaviors, restartScene, screenFlash };
|
|
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 };
|
package/dist/gameplay.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as DamageOnContact, C as restartScene, D as FaceTarget, E as FloatAway, 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-
|
|
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, phaseOf, registerGameplayBehaviors, restartScene, screenFlash };
|
|
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-DM1eu_cV.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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as
|
|
3
|
-
import { n as
|
|
1
|
+
import { a as ParticleSimConfig, c as showBootFailure, i as ParticleSim, n as AudioPlayer, o as ParticleView, s as isWebGLAvailable, t as AudioElementLike } from "./audio-player-DOrq7sP-.js";
|
|
2
|
+
import { $ as T_PREFIX, A as SettingsValues, At as BusName, B as captureBehaviors, C as EngineStats, Ct as sfxDuration, Dt as MusicTrack, E as DeviceHints, Et as MusicManager, F as BehaviorState, Ft as LogLevel, G as ORDER_GROUP_BASE, H as savesWithoutUid, I as RestoreReport, It as LogManager, J as effectiveOrder, K as OrderGroup, L as SaveSlot, Lt as Signal, M as readDeviceHints, Mt as NodeLifecycle, N as suggestQuality, Nt as SceneTree, O as QualityTier, Ot as PlayMusicOptions, P as Scene, Pt as LogEntry, Q as Localization, R as SaveSlots, Rt as SignalListener, S as Scheduler, St as SynthOptions, T as RendererStats, Tt as MusicBackend, U as SaveStore, V as restoreBehaviors, W as createSaveStore, X as BASE_LOCALE, Y as resolveOrderGroups, Z as LocaleTables, _ as mergeStaticSignals, _t as spatialPan, a as clearBehaviors, at as EffectLog, b as Engine, bt as SfxParams, c as registeredBehaviors, ct as isAudioContextAvailable, d as PropSchema, dt as Listener, et as suggestLocale, f as clearRegistry, ft as ROLLOFF_MODELS, g as getNodeType, gt as spatialGain, h as getNodeSignals, ht as Vec3, i as behaviorSignals, it as EffectKind, j as qualityEnvironment, jt as Node, k as Settings, kt as AudioBuses, l as NodeCtor, lt as Voice, m as getNodeSchema, mt as SpatialParams, n as BehaviorCtor, nt as InputMap, o as getBehavior, ot as SfxEngine, p as createNode, pt as RolloffModel, q as OrderGroupTable, r as behaviorSchema, rt as EffectEvent, s as registerBehavior, st as SfxPlayOptions, t as Behavior, tt as translationKey, u as PropDef, ut as VoicePreset, v as registerNode, vt as SFX_PRESETS, w as GameStats, wt as synthSfx, x as EngineOptions, xt as SfxWave, y as registeredTypes, yt as SFX_PRESET_NAMES, z as behaviorsWithoutSave } from "./behavior-DoFPYrgo.js";
|
|
3
|
+
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-B6ugCV1Q.js";
|
|
4
|
+
import { n as loadScene, t as LoadSceneOptions } from "./loader-CcB533FR.js";
|
|
4
5
|
import { i as resolveFrames, n as AnimationEntry, r as resolveAnimation, t as AnimationDef } from "./sprite-animation-CMr6f1K2.js";
|
|
5
|
-
import { a as ParticleSimConfig, i as ParticleSim, n as AudioPlayer, o as ParticleView, t as AudioElementLike } from "./audio-player-_UAcHxnC.js";
|
|
6
6
|
import { i as gridFromRows, n as PathGrid, r as findPath, t as FindPathOptions } from "./pathfinding-_fGrCFmH.js";
|
|
7
|
-
import { a as startRecording, c as IncantoErrorDetails, i as replay, l as auditScene, n as ReplayEvent, o as IncantoError, r as ReplayJson, s as IncantoErrorCode, t as Recorder } from "./replay-
|
|
7
|
+
import { a as startRecording, c as IncantoErrorDetails, i as replay, l as auditScene, n as ReplayEvent, o as IncantoError, r as ReplayJson, s as IncantoErrorCode, t as Recorder } from "./replay-Dvn8aeBd.js";
|
|
8
8
|
|
|
9
9
|
//#region src/core/audio/crossfade.d.ts
|
|
10
10
|
/**
|
|
@@ -313,8 +313,24 @@ declare class HudLayer extends Node {
|
|
|
313
313
|
* play resumes.
|
|
314
314
|
*/
|
|
315
315
|
private stepFocus;
|
|
316
|
-
/**
|
|
317
|
-
|
|
316
|
+
/**
|
|
317
|
+
* Focusable widgets under this layer, in tree order — **on screen only**.
|
|
318
|
+
*
|
|
319
|
+
* This filtered on each widget's OWN `visible` and recursed into hidden
|
|
320
|
+
* containers anyway, so arrow-keys walked into closed panels and Enter really
|
|
321
|
+
* pressed what it found. Measured on a title screen: 23 focus stops, 21 of
|
|
322
|
+
* them under an ancestor with `visible = false`, and
|
|
323
|
+
*
|
|
324
|
+
* gold before = 150 -> Enter on the hidden Shop/Row0/Buy -> gold 120,
|
|
325
|
+
* screen still 'title'
|
|
326
|
+
*
|
|
327
|
+
* The layer's own visibility was not a gate either. It is now: a HUD nobody
|
|
328
|
+
* can see has no ring, and a subtree nobody can see is not walked.
|
|
329
|
+
*
|
|
330
|
+
* PUBLIC, because a game that wants to know what its ring contains could not
|
|
331
|
+
* ask — and "why is focus on that" is exactly the question this answers.
|
|
332
|
+
*/
|
|
333
|
+
focusables(): HudWidgetBase[];
|
|
318
334
|
/** Move focus to a widget by hand — opening a menu should start somewhere. */
|
|
319
335
|
focus(widget: HudWidgetBase | null): void;
|
|
320
336
|
/** @internal Widgets mount into per-anchor flex columns. */
|
|
@@ -377,6 +393,11 @@ declare abstract class HudWidgetBase extends Node {
|
|
|
377
393
|
private host;
|
|
378
394
|
override onExitTree(): void;
|
|
379
395
|
override update(_dt: number): void;
|
|
396
|
+
/**
|
|
397
|
+
* The display this widget's own css asks for while shown — `grid` for a grid
|
|
398
|
+
* panel, `flex` for a row, `''` for a widget that never says.
|
|
399
|
+
*/
|
|
400
|
+
private _shownDisplay;
|
|
380
401
|
/** Which anchor slot this widget was mounted into. */
|
|
381
402
|
private mountedAnchor;
|
|
382
403
|
/**
|
|
@@ -607,6 +628,29 @@ declare class UiDialogue extends HudWidgetBase {
|
|
|
607
628
|
/** Queue a line; optional `choices` renders buttons after the line types out. */
|
|
608
629
|
say(speaker: string, text: string, choices?: string[]): void;
|
|
609
630
|
/**
|
|
631
|
+
* A dialogue takes focus while it is up.
|
|
632
|
+
*
|
|
633
|
+
* It paints a focus ring and the generated reference advertises the prop, and
|
|
634
|
+
* it was inert: choices are raw DOM buttons inside the widget, so
|
|
635
|
+
* `HudLayer.focusables()` can never see them, and this class — unlike
|
|
636
|
+
* `UiButton` a hundred lines above it — never overrode `_activate`. Eleven
|
|
637
|
+
* keys and eight pad codes did nothing to a waiting choice while the same
|
|
638
|
+
* Enter pressed a sibling button. Native Tab worked, and is closed by any
|
|
639
|
+
* game that binds Tab; a pad has no Tab at all.
|
|
640
|
+
*/
|
|
641
|
+
override focusable: boolean;
|
|
642
|
+
/** Which choice the keyboard/pad is on. Reset by every new line. */
|
|
643
|
+
private cursor;
|
|
644
|
+
/**
|
|
645
|
+
* Enter / A, and left / right between the options.
|
|
646
|
+
*
|
|
647
|
+
* `dir` is 0 for "activate" and ±1 for a nudge, the same shape `UiSelect`
|
|
648
|
+
* uses — so one key layout drives a menu, a slider and a conversation.
|
|
649
|
+
*/
|
|
650
|
+
override _activate(dir: number): void;
|
|
651
|
+
/** Show which option the keyboard is on — a ring nobody can see is not one. */
|
|
652
|
+
private paintCursor;
|
|
653
|
+
/**
|
|
610
654
|
* Player pressed "next": mid-typing reveals the whole line; a fully-typed
|
|
611
655
|
* line without choices advances (choice lines wait for `choose`).
|
|
612
656
|
*/
|
|
@@ -1265,4 +1309,4 @@ declare function computeViewport(canvasW: number, canvasH: number, viewport: {
|
|
|
1265
1309
|
/** Engine version. Kept in sync with package.json by the release pipeline. */
|
|
1266
1310
|
declare const VERSION: string;
|
|
1267
1311
|
//#endregion
|
|
1268
|
-
export { type AnimationDef, type AnimationEntry, type AssetFailure, AudioBuses, type AudioElementLike, AudioPlayer, BASE_LOCALE, Behavior, type BehaviorCtor, type BehaviorState, type BusName, CONST_REF_KEY, type ComputedViewport, type ConnectionJson, type CrossfadeGains, type DebugLineSource, type DeviceHints, type DriveScript, type DriveStep, type EffectEvent, type EffectKind, EffectLog, Engine, type EngineOptions, type EngineStats, type FindPathOptions, type GameStats, type GroupedLog, type HudAnchor, HudLayer, IncantoError, type IncantoErrorCode, type IncantoErrorDetails, InputMap, type JsonKind, type JsonObject, type JsonValue, type Listener, type LiveInput, type LiveReport, type LoadSceneOptions, type LocaleTables, Localization, type LogEntry, type LogLevel, LogManager, type MusicBackend, MusicManager, type MusicTrack, Node, type NodeCtor, type NodeJson, type NodeLifecycle, ORDER_GROUP_BASE, type OrderGroup, type OrderGroupTable, PARTICLE_PRESETS, PARTICLE_PRESET_NAMES, type ParsedNodePath, type ParticlePresetValues, ParticleSim, type ParticleSimConfig, type ParticleView, type PathGrid, type PlayMusicOptions, type PreloadResult, type PropDef, type PropSchema, type QualityTier, ROLLOFF_MODELS, type Recorder, type RefNode, type RefProblem, type RefResult, type RendererStats, type ReplayEvent, type ReplayJson, type ResolvedRendering, type ResolvedViewport, Respawn, type RestoreReport, Rng, type RolloffModel, SCENE_FORMAT, SFX_PRESETS, SFX_PRESET_NAMES, type SaveSlot, SaveSlots, type SaveStore, Scene, type SceneJson, SceneTree, type Scheduler, Settings, type SettingsValues, SfxEngine, type SfxParams, type SfxPlayOptions, type SfxWave, Signal, type SignalListener, type SpatialParams, type SynthOptions, T_PREFIX, Timer, TouchControls, type TouchControlsOptions, UiBanner, UiBar, UiButton, UiDialogue, UiFrameCapSelect, UiImage, UiLanguageSelect, UiMuteToggle, UiPanel, UiQualitySelect, UiRenderScaleSelect, UiSelect, UiSlider, UiText, UiToggle, UiVolumeSlider, VERSION, type Vec3, type ViewportFit, type Voice, type VoicePreset, WebAudioMusicBackend, applyParticlePreset, assetUrls, attachTouchControls, auditScene, behaviorSchema, behaviorSignals, behaviorsWithoutSave, captureBehaviors, clearBehaviors, clearRegistry, computeViewport, createNode, createNoise2D, createSaveStore, crossfadeGains, describeRefProblem, duplicateNode, effectiveOrder, fadeGain, findPath, getBehavior, getNodeSchema, getNodeSignals, getNodeType, gridFromRows, isAudioContextAvailable, isConstRef, joystickVector, jsonClone, jsonEquals, jsonKind, loadScene, logReport, logText, mergeStaticSignals, newUid, nodeRefWarnings, parseDrive, parseNodePath, preloadSceneAssets, preloadUrls, qualityEnvironment, readDeviceHints, registerBehavior, registerCoreNodes, registerNode, registeredBehaviors, registeredTypes, replay, resolveAnimation, resolveConstants, resolveFrames, resolveOrderGroups, resolveRefInJson, resolveRendering, resolveViewport, restoreBehaviors, savesWithoutUid, serializeNode, sfxDuration, spatialGain, spatialPan, startRecording, suggestLocale, suggestQuality, synthSfx, translationKey };
|
|
1312
|
+
export { type AnimationDef, type AnimationEntry, type AssetFailure, AudioBuses, type AudioElementLike, AudioPlayer, BASE_LOCALE, Behavior, type BehaviorCtor, type BehaviorState, type BusName, CONST_REF_KEY, type ComputedViewport, type ConnectionJson, type CrossfadeGains, type DebugLineSource, type DeviceHints, type DriveScript, type DriveStep, type EffectEvent, type EffectKind, EffectLog, Engine, type EngineOptions, type EngineStats, type FindPathOptions, type GameStats, type GroupedLog, type HudAnchor, HudLayer, IncantoError, type IncantoErrorCode, type IncantoErrorDetails, InputMap, type JsonKind, type JsonObject, type JsonValue, type Listener, type LiveInput, type LiveReport, type LoadSceneOptions, type LocaleTables, Localization, type LogEntry, type LogLevel, LogManager, type MusicBackend, MusicManager, type MusicTrack, Node, type NodeCtor, type NodeJson, type NodeLifecycle, ORDER_GROUP_BASE, type OrderGroup, type OrderGroupTable, PARTICLE_PRESETS, PARTICLE_PRESET_NAMES, type ParsedNodePath, type ParticlePresetValues, ParticleSim, type ParticleSimConfig, type ParticleView, type PathGrid, type PlayMusicOptions, type PreloadResult, type PropDef, type PropSchema, type QualityTier, ROLLOFF_MODELS, type Recorder, type RefNode, type RefProblem, type RefResult, type RendererStats, type ReplayEvent, type ReplayJson, type ResolvedRendering, type ResolvedViewport, Respawn, type RestoreReport, Rng, type RolloffModel, SCENE_FORMAT, SFX_PRESETS, SFX_PRESET_NAMES, type SaveSlot, SaveSlots, type SaveStore, Scene, type SceneJson, SceneTree, type Scheduler, Settings, type SettingsValues, SfxEngine, type SfxParams, type SfxPlayOptions, type SfxWave, Signal, type SignalListener, type SpatialParams, type SynthOptions, T_PREFIX, Timer, TouchControls, type TouchControlsOptions, UiBanner, UiBar, UiButton, UiDialogue, UiFrameCapSelect, UiImage, UiLanguageSelect, UiMuteToggle, UiPanel, UiQualitySelect, UiRenderScaleSelect, UiSelect, UiSlider, UiText, UiToggle, UiVolumeSlider, VERSION, type Vec3, type ViewportFit, type Voice, type VoicePreset, WebAudioMusicBackend, applyParticlePreset, assetUrls, attachTouchControls, auditScene, behaviorSchema, behaviorSignals, behaviorsWithoutSave, captureBehaviors, clearBehaviors, clearRegistry, computeViewport, createNode, createNoise2D, createSaveStore, crossfadeGains, describeRefProblem, duplicateNode, effectiveOrder, fadeGain, findPath, getBehavior, getNodeSchema, getNodeSignals, getNodeType, gridFromRows, isAudioContextAvailable, isConstRef, isWebGLAvailable, joystickVector, jsonClone, jsonEquals, jsonKind, loadScene, logReport, logText, mergeStaticSignals, newUid, nodeRefWarnings, parseDrive, parseNodePath, preloadSceneAssets, preloadUrls, qualityEnvironment, readDeviceHints, registerBehavior, registerCoreNodes, registerNode, registeredBehaviors, registeredTypes, replay, resolveAnimation, resolveConstants, resolveFrames, resolveOrderGroups, resolveRefInJson, resolveRendering, resolveViewport, restoreBehaviors, savesWithoutUid, serializeNode, sfxDuration, showBootFailure, spatialGain, spatialPan, startRecording, suggestLocale, suggestQuality, synthSfx, translationKey };
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import { A as describeRefProblem, B as registeredBehaviors, C as ORDER_GROUP_BASE, E as InputMap, F as behaviorSchema, I as behaviorSignals, L as clearBehaviors, M as resolveRefInJson, N as parseNodePath, O as Node, P as Behavior, R as getBehavior, S as createSaveStore, T as resolveOrderGroups, V as Signal, _ as SaveSlots, a as serializeNode, b as restoreBehaviors, d as computeViewport, f as resolveViewport, g as resolveConstants, h as isConstRef, i as Scene, j as nodeRefWarnings, m as CONST_REF_KEY, n as loadScene, o as SCENE_FORMAT, p as SceneTree, v as behaviorsWithoutSave, w as effectiveOrder, x as savesWithoutUid, y as captureBehaviors, z as registerBehavior } from "./loader-
|
|
2
|
-
import { A as UiBanner, B as isAudioContextAvailable, D as EffectLog, E as LogManager, F as T_PREFIX, G as SFX_PRESET_NAMES, H as spatialGain, I as suggestLocale, J as MusicManager, K as sfxDuration, M as UiText, N as BASE_LOCALE, O as HudLayer, P as Localization, Q as AudioBuses, R as translationKey, S as qualityEnvironment, T as suggestQuality, U as spatialPan, V as ROLLOFF_MODELS, W as SFX_PRESETS, X as crossfadeGains, Y as WebAudioMusicBackend, Z as fadeGain, _ as AudioPlayer, a as UiMuteToggle, b as Settings, c as UiRenderScaleSelect, d as UiToggle, f as UiVolumeSlider, g as UiDialogue, h as UiButton, i as UiLanguageSelect, j as UiBar, l as UiSelect, m as Respawn, n as UiFrameCapSelect, o as UiPanel, p as Timer, q as synthSfx, r as UiImage, s as UiQualitySelect, t as registerCoreNodes, u as UiSlider, v as Engine, w as readDeviceHints, z as SfxEngine } from "./register-
|
|
1
|
+
import { A as describeRefProblem, B as registeredBehaviors, C as ORDER_GROUP_BASE, E as InputMap, F as behaviorSchema, I as behaviorSignals, L as clearBehaviors, M as resolveRefInJson, N as parseNodePath, O as Node, P as Behavior, R as getBehavior, S as createSaveStore, T as resolveOrderGroups, V as Signal, _ as SaveSlots, a as serializeNode, b as restoreBehaviors, d as computeViewport, f as resolveViewport, g as resolveConstants, h as isConstRef, i as Scene, j as nodeRefWarnings, m as CONST_REF_KEY, n as loadScene, o as SCENE_FORMAT, p as SceneTree, v as behaviorsWithoutSave, w as effectiveOrder, x as savesWithoutUid, y as captureBehaviors, z as registerBehavior } from "./loader-BC4PNtJX.js";
|
|
2
|
+
import { A as UiBanner, B as isAudioContextAvailable, D as EffectLog, E as LogManager, F as T_PREFIX, G as SFX_PRESET_NAMES, H as spatialGain, I as suggestLocale, J as MusicManager, K as sfxDuration, M as UiText, N as BASE_LOCALE, O as HudLayer, P as Localization, Q as AudioBuses, R as translationKey, S as qualityEnvironment, T as suggestQuality, U as spatialPan, V as ROLLOFF_MODELS, W as SFX_PRESETS, X as crossfadeGains, Y as WebAudioMusicBackend, Z as fadeGain, _ as AudioPlayer, a as UiMuteToggle, b as Settings, c as UiRenderScaleSelect, d as UiToggle, f as UiVolumeSlider, g as UiDialogue, h as UiButton, i as UiLanguageSelect, j as UiBar, l as UiSelect, m as Respawn, n as UiFrameCapSelect, o as UiPanel, p as Timer, q as synthSfx, r as UiImage, s as UiQualitySelect, t as registerCoreNodes, u as UiSlider, v as Engine, w as readDeviceHints, z as SfxEngine } from "./register-3ta-2Xig.js";
|
|
3
3
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
4
4
|
import { t as Rng } from "./rng-DP-SR7eg.js";
|
|
5
5
|
import { n as jsonEquals, r as jsonKind, t as jsonClone } from "./json-CwwhxQgb.js";
|
|
6
6
|
import { a as getNodeSignals, c as mergeStaticSignals, i as getNodeSchema, l as registerNode, n as clearRegistry, o as getNodeType, r as createNode, u as registeredTypes } from "./registry-CF70EArN.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { i as joystickVector, n as attachTouchControls, t as TouchControls } from "./touch-DEAmqGdf.js";
|
|
8
|
+
import { n as startRecording, r as auditScene, t as replay } from "./replay-CYvhVHHN.js";
|
|
9
|
+
import { t as showBootFailure } from "./boot-failure-CKYrEcGF.js";
|
|
10
|
+
import { a as logText, i as logReport, o as parseDrive, r as resolveRendering, t as isWebGLAvailable } from "./webgl-unavailable-N9nQqesw.js";
|
|
9
11
|
import { t as createNoise2D } from "./noise-CGUMx44x.js";
|
|
10
12
|
import { a as PARTICLE_PRESETS, i as ParticleSim, n as resolveFrames, o as PARTICLE_PRESET_NAMES, s as applyParticlePreset, t as resolveAnimation } from "./sprite-animation-CY-mrr1L.js";
|
|
11
|
-
import { a as preloadUrls, i as preloadSceneAssets, n as newUid, o as findPath, r as assetUrls, s as gridFromRows, t as VERSION } from "./src-
|
|
12
|
-
import { t as duplicateNode } from "./duplicate-
|
|
13
|
-
export { AudioBuses, AudioPlayer, BASE_LOCALE, Behavior, CONST_REF_KEY, EffectLog, Engine, HudLayer, IncantoError, InputMap, Localization, LogManager, MusicManager, Node, ORDER_GROUP_BASE, PARTICLE_PRESETS, PARTICLE_PRESET_NAMES, ParticleSim, ROLLOFF_MODELS, Respawn, Rng, SCENE_FORMAT, SFX_PRESETS, SFX_PRESET_NAMES, SaveSlots, Scene, SceneTree, Settings, SfxEngine, Signal, T_PREFIX, Timer, TouchControls, UiBanner, UiBar, UiButton, UiDialogue, UiFrameCapSelect, UiImage, UiLanguageSelect, UiMuteToggle, UiPanel, UiQualitySelect, UiRenderScaleSelect, UiSelect, UiSlider, UiText, UiToggle, UiVolumeSlider, VERSION, WebAudioMusicBackend, applyParticlePreset, assetUrls, attachTouchControls, auditScene, behaviorSchema, behaviorSignals, behaviorsWithoutSave, captureBehaviors, clearBehaviors, clearRegistry, computeViewport, createNode, createNoise2D, createSaveStore, crossfadeGains, describeRefProblem, duplicateNode, effectiveOrder, fadeGain, findPath, getBehavior, getNodeSchema, getNodeSignals, getNodeType, gridFromRows, isAudioContextAvailable, isConstRef, joystickVector, jsonClone, jsonEquals, jsonKind, loadScene, logReport, logText, mergeStaticSignals, newUid, nodeRefWarnings, parseDrive, parseNodePath, preloadSceneAssets, preloadUrls, qualityEnvironment, readDeviceHints, registerBehavior, registerCoreNodes, registerNode, registeredBehaviors, registeredTypes, replay, resolveAnimation, resolveConstants, resolveFrames, resolveOrderGroups, resolveRefInJson, resolveRendering, resolveViewport, restoreBehaviors, savesWithoutUid, serializeNode, sfxDuration, spatialGain, spatialPan, startRecording, suggestLocale, suggestQuality, synthSfx, translationKey };
|
|
13
|
+
import { a as preloadUrls, i as preloadSceneAssets, n as newUid, o as findPath, r as assetUrls, s as gridFromRows, t as VERSION } from "./src-D7RIqXYF.js";
|
|
14
|
+
import { t as duplicateNode } from "./duplicate-IWIqk0HJ.js";
|
|
15
|
+
export { AudioBuses, AudioPlayer, BASE_LOCALE, Behavior, CONST_REF_KEY, EffectLog, Engine, HudLayer, IncantoError, InputMap, Localization, LogManager, MusicManager, Node, ORDER_GROUP_BASE, PARTICLE_PRESETS, PARTICLE_PRESET_NAMES, ParticleSim, ROLLOFF_MODELS, Respawn, Rng, SCENE_FORMAT, SFX_PRESETS, SFX_PRESET_NAMES, SaveSlots, Scene, SceneTree, Settings, SfxEngine, Signal, T_PREFIX, Timer, TouchControls, UiBanner, UiBar, UiButton, UiDialogue, UiFrameCapSelect, UiImage, UiLanguageSelect, UiMuteToggle, UiPanel, UiQualitySelect, UiRenderScaleSelect, UiSelect, UiSlider, UiText, UiToggle, UiVolumeSlider, VERSION, WebAudioMusicBackend, applyParticlePreset, assetUrls, attachTouchControls, auditScene, behaviorSchema, behaviorSignals, behaviorsWithoutSave, captureBehaviors, clearBehaviors, clearRegistry, computeViewport, createNode, createNoise2D, createSaveStore, crossfadeGains, describeRefProblem, duplicateNode, effectiveOrder, fadeGain, findPath, getBehavior, getNodeSchema, getNodeSignals, getNodeType, gridFromRows, isAudioContextAvailable, isConstRef, isWebGLAvailable, joystickVector, jsonClone, jsonEquals, jsonKind, loadScene, logReport, logText, mergeStaticSignals, newUid, nodeRefWarnings, parseDrive, parseNodePath, preloadSceneAssets, preloadUrls, qualityEnvironment, readDeviceHints, registerBehavior, registerCoreNodes, registerNode, registeredBehaviors, registeredTypes, replay, resolveAnimation, resolveConstants, resolveFrames, resolveOrderGroups, resolveRefInJson, resolveRendering, resolveViewport, restoreBehaviors, savesWithoutUid, serializeNode, sfxDuration, showBootFailure, spatialGain, spatialPan, startRecording, suggestLocale, suggestQuality, synthSfx, translationKey };
|
|
@@ -20,6 +20,17 @@ var Signal = class {
|
|
|
20
20
|
disconnectAll() {
|
|
21
21
|
this.connections.length = 0;
|
|
22
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* How many listeners are attached.
|
|
25
|
+
*
|
|
26
|
+
* A leak is a SLOPE, and a game had no way to see one: every check in this
|
|
27
|
+
* engine is a snapshot. A handler connected per frame and never disconnected,
|
|
28
|
+
* or a `once` that armed and never fired, accumulates invisibly and is
|
|
29
|
+
* perfectly healthy for the first thirty seconds of every playthrough.
|
|
30
|
+
*/
|
|
31
|
+
get count() {
|
|
32
|
+
return this.connections.length;
|
|
33
|
+
}
|
|
23
34
|
emit(...args) {
|
|
24
35
|
const snapshot = [...this.connections];
|
|
25
36
|
for (const conn of snapshot) {
|
|
@@ -652,6 +663,16 @@ var Node = class {
|
|
|
652
663
|
off(signal, fn) {
|
|
653
664
|
this._signals.get(signal)?.disconnect(fn);
|
|
654
665
|
}
|
|
666
|
+
/**
|
|
667
|
+
* How many listeners this node has on `signal` — 0 for one nobody watches.
|
|
668
|
+
*
|
|
669
|
+
* The engine's own leak test reads it, and so can a game: a handler connected
|
|
670
|
+
* every frame and never disconnected is invisible to every other check here,
|
|
671
|
+
* because every other check is a snapshot and a leak is a slope.
|
|
672
|
+
*/
|
|
673
|
+
listenerCount(signal) {
|
|
674
|
+
return this._signals.get(signal)?.count ?? 0;
|
|
675
|
+
}
|
|
655
676
|
emit(signal, ...args) {
|
|
656
677
|
this.assertDeclared(signal);
|
|
657
678
|
this._signals.get(signal)?.emit(...args);
|
|
@@ -1549,10 +1570,16 @@ function effectiveOrder(group, renderOrder, table = ORDER_GROUP_BASE) {
|
|
|
1549
1570
|
//#endregion
|
|
1550
1571
|
//#region src/core/save.ts
|
|
1551
1572
|
const memoryStores = /* @__PURE__ */ new Map();
|
|
1573
|
+
/** Namespaces already warned about, so a per-frame save cannot spam the console. */
|
|
1574
|
+
const warnedVolatile = /* @__PURE__ */ new Set();
|
|
1552
1575
|
function createSaveStore(namespace) {
|
|
1553
1576
|
const prefix = `incanto:${namespace}:`;
|
|
1554
1577
|
const local = storageOrNull();
|
|
1555
1578
|
if (!local) {
|
|
1579
|
+
if (typeof localStorage !== "undefined" && !warnedVolatile.has(namespace)) {
|
|
1580
|
+
warnedVolatile.add(namespace);
|
|
1581
|
+
console.warn(`[incanto] this browser refused localStorage, so saves for '${namespace}' live in memory and are lost when the page closes (private window, storage disabled, or quota exhausted). Check \`store.persistent\` and tell the player.`);
|
|
1582
|
+
}
|
|
1556
1583
|
let mem = memoryStores.get(namespace);
|
|
1557
1584
|
if (!mem) {
|
|
1558
1585
|
mem = /* @__PURE__ */ new Map();
|
|
@@ -1560,6 +1587,7 @@ function createSaveStore(namespace) {
|
|
|
1560
1587
|
}
|
|
1561
1588
|
const backing = mem;
|
|
1562
1589
|
return {
|
|
1590
|
+
persistent: false,
|
|
1563
1591
|
get: (key, fallback) => parse(backing.get(key), fallback),
|
|
1564
1592
|
set: (key, value) => void backing.set(key, JSON.stringify(value)),
|
|
1565
1593
|
remove: (key) => void backing.delete(key),
|
|
@@ -1567,6 +1595,7 @@ function createSaveStore(namespace) {
|
|
|
1567
1595
|
};
|
|
1568
1596
|
}
|
|
1569
1597
|
return {
|
|
1598
|
+
persistent: true,
|
|
1570
1599
|
get: (key, fallback) => parse(local.getItem(prefix + key) ?? void 0, fallback),
|
|
1571
1600
|
set: (key, value) => local.setItem(prefix + key, JSON.stringify(value)),
|
|
1572
1601
|
remove: (key) => local.removeItem(prefix + key),
|
|
@@ -1674,6 +1703,7 @@ const FREED_KEY = "#freed";
|
|
|
1674
1703
|
function captureBehaviors(root, source) {
|
|
1675
1704
|
const state = {};
|
|
1676
1705
|
const unaddressable = [];
|
|
1706
|
+
const unrecordable = [];
|
|
1677
1707
|
let saveable = 0;
|
|
1678
1708
|
const walk = (node) => {
|
|
1679
1709
|
const behavior = node.behavior;
|
|
@@ -1698,13 +1728,46 @@ function captureBehaviors(root, source) {
|
|
|
1698
1728
|
mark(root);
|
|
1699
1729
|
const freed = authoredUids(source).filter((uid) => !alive.has(uid));
|
|
1700
1730
|
if (freed.length > 0) state[FREED_KEY] = freed;
|
|
1731
|
+
unrecordable.push(...goneWithoutUid(source, root));
|
|
1701
1732
|
}
|
|
1702
1733
|
return {
|
|
1703
1734
|
state,
|
|
1704
1735
|
unaddressable,
|
|
1705
|
-
saveable
|
|
1736
|
+
saveable,
|
|
1737
|
+
unrecordable
|
|
1706
1738
|
};
|
|
1707
1739
|
}
|
|
1740
|
+
/**
|
|
1741
|
+
* Authored nodes this run CONSUMED that the ledger cannot record.
|
|
1742
|
+
*
|
|
1743
|
+
* `#freed` is authored uids minus alive uids, so a node with no uid can never
|
|
1744
|
+
* enter it — it is silently omitted, and on load it comes back. Every collected
|
|
1745
|
+
* coin in a scene without uids returns, while the score that counted it does
|
|
1746
|
+
* not, and nothing says a word.
|
|
1747
|
+
*
|
|
1748
|
+
* A uid is the only stable handle (it survives a rename and a reparent), so the
|
|
1749
|
+
* engine must not guess a replacement — a saved PATH would point at whatever
|
|
1750
|
+
* node moved into that slot after the next edit. What it can do is notice, by
|
|
1751
|
+
* walking the authored tree and asking the live one whether each node is still
|
|
1752
|
+
* there. Reported, not repaired.
|
|
1753
|
+
*/
|
|
1754
|
+
function goneWithoutUid(source, root) {
|
|
1755
|
+
const out = [];
|
|
1756
|
+
const walk = (node, path) => {
|
|
1757
|
+
if (typeof node !== "object" || node === null) return;
|
|
1758
|
+
const n = node;
|
|
1759
|
+
const name = typeof n.name === "string" ? n.name : "";
|
|
1760
|
+
const here = path === "" ? name : `${path}/${name}`;
|
|
1761
|
+
if (!(typeof n.uid === "string" && n.uid !== "") && here !== "" && path !== "") {
|
|
1762
|
+
const live = root.getNodeOrNull(here.slice(here.indexOf("/") + 1));
|
|
1763
|
+
const leaving = live ? live.tree?._isQueuedFree(live) ?? false : false;
|
|
1764
|
+
if (!live || leaving) out.push(here);
|
|
1765
|
+
}
|
|
1766
|
+
if (Array.isArray(n.children)) for (const child of n.children) walk(child, here);
|
|
1767
|
+
};
|
|
1768
|
+
walk(source.root, "");
|
|
1769
|
+
return out;
|
|
1770
|
+
}
|
|
1708
1771
|
/** Every uid the scene FILE declares, in document order. */
|
|
1709
1772
|
function authoredUids(source) {
|
|
1710
1773
|
const out = [];
|
|
@@ -1779,6 +1842,17 @@ var SaveSlots = class {
|
|
|
1779
1842
|
constructor(namespace = "saves", store) {
|
|
1780
1843
|
this.store = store ?? createSaveStore(namespace);
|
|
1781
1844
|
}
|
|
1845
|
+
/**
|
|
1846
|
+
* Will these slots survive the page closing?
|
|
1847
|
+
*
|
|
1848
|
+
* `false` in a private window, with storage disabled, or headless — every
|
|
1849
|
+
* write still succeeds and every read still answers, for as long as the tab
|
|
1850
|
+
* lives. A load menu should say so instead of offering a slot that will not
|
|
1851
|
+
* be there tomorrow.
|
|
1852
|
+
*/
|
|
1853
|
+
get persistent() {
|
|
1854
|
+
return this.store.persistent !== false;
|
|
1855
|
+
}
|
|
1782
1856
|
/** Slot ids that have a save, newest first. */
|
|
1783
1857
|
list() {
|
|
1784
1858
|
const rows = this.store.get(INDEX_KEY, []).map((id) => ({
|
|
@@ -2795,6 +2869,8 @@ var Scene = class {
|
|
|
2795
2869
|
orderGroups;
|
|
2796
2870
|
/** Physics config (gravity etc. — interpreted by the physics modules). */
|
|
2797
2871
|
physics;
|
|
2872
|
+
/** Declared piece of another scene — see `SceneJson.fragment`. */
|
|
2873
|
+
fragment;
|
|
2798
2874
|
/** Multiplayer config (room etc. — interpreted by incanto/net). */
|
|
2799
2875
|
multiplayer;
|
|
2800
2876
|
/** Design-resolution viewport (consumed by renderers via resolveViewport). */
|
|
@@ -2814,6 +2890,7 @@ var Scene = class {
|
|
|
2814
2890
|
this.orderGroups = source.orderGroups ? { ...source.orderGroups } : void 0;
|
|
2815
2891
|
this.strings = source.strings ? jsonClone(source.strings) : void 0;
|
|
2816
2892
|
this.physics = source.physics ? jsonClone(source.physics) : void 0;
|
|
2893
|
+
this.fragment = source.fragment === true ? true : void 0;
|
|
2817
2894
|
this.multiplayer = source.multiplayer ? jsonClone(source.multiplayer) : void 0;
|
|
2818
2895
|
this.environment = source.environment ? jsonClone(source.environment) : void 0;
|
|
2819
2896
|
this.viewport = source.viewport ? jsonClone(source.viewport) : void 0;
|
|
@@ -2832,6 +2909,7 @@ var Scene = class {
|
|
|
2832
2909
|
...this.orderGroups ? { orderGroups: { ...this.orderGroups } } : {},
|
|
2833
2910
|
...this.strings ? { strings: jsonClone(this.strings) } : {},
|
|
2834
2911
|
...this.physics ? { physics: jsonClone(this.physics) } : {},
|
|
2912
|
+
...this.fragment ? { fragment: true } : {},
|
|
2835
2913
|
...this.multiplayer ? { multiplayer: jsonClone(this.multiplayer) } : {},
|
|
2836
2914
|
...this.environment ? { environment: jsonClone(this.environment) } : {},
|
|
2837
2915
|
...this.viewport ? { viewport: jsonClone(this.viewport) } : {},
|
|
@@ -3004,9 +3082,11 @@ function buildNodeJson(json, opts) {
|
|
|
3004
3082
|
return buildNode(json, opts, [], "", void 0);
|
|
3005
3083
|
}
|
|
3006
3084
|
function validateHeader(json) {
|
|
3085
|
+
rejectUnknownKeys(json, SCENE_KEYS, "the scene", "");
|
|
3007
3086
|
if (json.format !== 1) throw new IncantoError("BAD_FORMAT", `Unsupported scene format ${JSON.stringify(json.format)}; this engine reads format 1.`);
|
|
3008
3087
|
if (json.type !== "scene") throw new IncantoError("BAD_FORMAT", `Expected "type": "scene", got ${JSON.stringify(json.type)}.`);
|
|
3009
3088
|
if (typeof json.name !== "string" || json.name === "") throw new IncantoError("BAD_FORMAT", "Scene \"name\" must be a non-empty string.");
|
|
3089
|
+
if (json.fragment !== void 0 && typeof json.fragment !== "boolean") throw new IncantoError("BAD_FORMAT", `Scene "fragment" must be true or false, got ${JSON.stringify(json.fragment)}.`);
|
|
3010
3090
|
if (json.dimension !== void 0 && json.dimension !== "2d" && json.dimension !== "3d") throw new IncantoError("BAD_FORMAT", `Scene "dimension" must be "2d" or "3d", got ${JSON.stringify(json.dimension)}.`);
|
|
3011
3091
|
if (typeof json.root !== "object" || json.root === null) throw new IncantoError("BAD_FORMAT", "Scene \"root\" must be a node object.");
|
|
3012
3092
|
resolveViewport(json.viewport);
|
|
@@ -3042,9 +3122,101 @@ function withNodeContext(e, path, nj) {
|
|
|
3042
3122
|
});
|
|
3043
3123
|
return e;
|
|
3044
3124
|
}
|
|
3125
|
+
/**
|
|
3126
|
+
* Every key a node object may carry.
|
|
3127
|
+
*
|
|
3128
|
+
* The node object was an OPEN dictionary sitting between a dozen closed ones —
|
|
3129
|
+
* `props`, `environment`, `material`, `network` and the environment's own
|
|
3130
|
+
* sub-blocks all reject an unknown key with a did-you-mean. A node accepted
|
|
3131
|
+
* anything, and the one that costs an author a day is `connections`:
|
|
3132
|
+
*
|
|
3133
|
+
* ```jsonc
|
|
3134
|
+
* { "name": "Room", "type": "Node3D",
|
|
3135
|
+
* "connections": [ … ], // <- silently nothing
|
|
3136
|
+
* "children": [ … ] }
|
|
3137
|
+
* ```
|
|
3138
|
+
*
|
|
3139
|
+
* `wireConnections` is reached from the scene header and from a sub-scene's own
|
|
3140
|
+
* header, and NOTHING reads `nodeJson.connections`. Measured on two scenes
|
|
3141
|
+
* differing only in which brace the same wire sits inside:
|
|
3142
|
+
*
|
|
3143
|
+
* ```
|
|
3144
|
+
* inside Dst.n=0 validateScene.ok=true auditWarnings=0
|
|
3145
|
+
* beside Dst.n=1 validateScene.ok=true auditWarnings=0
|
|
3146
|
+
* none Dst.n=0 validateScene.ok=true auditWarnings=0
|
|
3147
|
+
* ```
|
|
3148
|
+
*
|
|
3149
|
+
* The misplaced wire is bit-for-bit indistinguishable from deleting it. A
|
|
3150
|
+
* builder moved 28 of them into `root`, got `1/1 scene(s) valid` with zero
|
|
3151
|
+
* warnings, and found out sixty seconds later from `incanto-playtest`.
|
|
3152
|
+
*/
|
|
3153
|
+
const NODE_KEYS = [
|
|
3154
|
+
"name",
|
|
3155
|
+
"uid",
|
|
3156
|
+
"type",
|
|
3157
|
+
"instance",
|
|
3158
|
+
"overrides",
|
|
3159
|
+
"groups",
|
|
3160
|
+
"tags",
|
|
3161
|
+
"props",
|
|
3162
|
+
"script",
|
|
3163
|
+
"network",
|
|
3164
|
+
"children"
|
|
3165
|
+
];
|
|
3166
|
+
/** Every key the scene HEADER may carry — closed for the same reason. */
|
|
3167
|
+
const SCENE_KEYS = [
|
|
3168
|
+
"format",
|
|
3169
|
+
"type",
|
|
3170
|
+
"name",
|
|
3171
|
+
"dimension",
|
|
3172
|
+
"viewport",
|
|
3173
|
+
"assets",
|
|
3174
|
+
"constants",
|
|
3175
|
+
"orderGroups",
|
|
3176
|
+
"input",
|
|
3177
|
+
"multiplayer",
|
|
3178
|
+
"environment",
|
|
3179
|
+
"strings",
|
|
3180
|
+
"physics",
|
|
3181
|
+
"fragment",
|
|
3182
|
+
"root",
|
|
3183
|
+
"connections"
|
|
3184
|
+
];
|
|
3185
|
+
/** Closest known key by edit distance — only a genuinely near one. */
|
|
3186
|
+
function nearestKey(key, known) {
|
|
3187
|
+
const lower = key.toLowerCase();
|
|
3188
|
+
let best = null;
|
|
3189
|
+
let bestScore = 3;
|
|
3190
|
+
for (const candidate of known) {
|
|
3191
|
+
let d = 0;
|
|
3192
|
+
const a = lower;
|
|
3193
|
+
const b = candidate.toLowerCase();
|
|
3194
|
+
const rows = Array.from({ length: a.length + 1 }, (_, i) => [i, ...Array(b.length).fill(0)]);
|
|
3195
|
+
for (let j = 0; j <= b.length; j++) rows[0][j] = j;
|
|
3196
|
+
for (let i = 1; i <= a.length; i++) for (let j = 1; j <= b.length; j++) {
|
|
3197
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
3198
|
+
rows[i][j] = Math.min(rows[i - 1][j] + 1, rows[i][j - 1] + 1, rows[i - 1][j - 1] + cost);
|
|
3199
|
+
}
|
|
3200
|
+
d = rows[a.length][b.length];
|
|
3201
|
+
if (d < bestScore) {
|
|
3202
|
+
bestScore = d;
|
|
3203
|
+
best = candidate;
|
|
3204
|
+
}
|
|
3205
|
+
}
|
|
3206
|
+
return best;
|
|
3207
|
+
}
|
|
3208
|
+
/** Reject a key the shape does not have, the way `props` already does. */
|
|
3209
|
+
function rejectUnknownKeys(obj, known, what, where) {
|
|
3210
|
+
for (const key of Object.keys(obj)) {
|
|
3211
|
+
if (known.includes(key)) continue;
|
|
3212
|
+
const guess = nearestKey(key, known);
|
|
3213
|
+
throw new IncantoError("BAD_FORMAT", `Unknown key "${key}" on ${what}${where ? ` '${where}'` : ""}.` + (guess ? ` Did you mean "${guess}"?` : "") + ` Valid keys: [${known.join(", ")}].` + (key === "connections" ? " Connections live on the SCENE, beside \"root\" — a node cannot carry them, and one placed here does exactly nothing." : ""), { validOptions: [...known] });
|
|
3214
|
+
}
|
|
3215
|
+
}
|
|
3045
3216
|
function buildNode(nj, opts, stack, parentPath, constants, bands = []) {
|
|
3046
3217
|
const path = `${parentPath}/${nj.name}`;
|
|
3047
3218
|
try {
|
|
3219
|
+
rejectUnknownKeys(nj, NODE_KEYS, "node", nj.name ?? "");
|
|
3048
3220
|
if (nj.instance !== void 0 && nj.type !== void 0) throw new IncantoError("BAD_FORMAT", `Node '${nj.name}' declares both "type" and "instance" — they are mutually exclusive.`);
|
|
3049
3221
|
if (nj.instance !== void 0) return buildInstance(nj, opts, stack, path, constants, bands);
|
|
3050
3222
|
if (nj.type === void 0) throw new IncantoError("BAD_FORMAT", `Node '${nj.name}' must declare either "type" or "instance".`);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { P as Scene, b as Engine } from "./behavior-
|
|
2
|
-
import { i as SceneJson } from "./schema-
|
|
1
|
+
import { P as Scene, b as Engine } from "./behavior-DoFPYrgo.js";
|
|
2
|
+
import { i as SceneJson } from "./schema-B6ugCV1Q.js";
|
|
3
3
|
|
|
4
4
|
//#region src/core/scene/loader.d.ts
|
|
5
5
|
interface LoadSceneOptions {
|
package/dist/net.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { d as PropSchema, jt as Node } from "./behavior-
|
|
2
|
-
import { a as NetworkManagerOptions, c as LocalGameServer, d as ServerClass, f as createLocalGameServer, g as Unsubscribe, h as NetworkTransport, i as NetworkManager, l as LocalGameServerOptions, m as LoopbackTransport, n as SplitScreenPlayer, o as applySyncPatch, p as LoopbackHub, r as createSplitScreen, s as CollectionOptions, t as SplitScreenOptions, u as LocalGameServerTransport } from "./split-screen-
|
|
1
|
+
import { d as PropSchema, jt as Node } from "./behavior-DoFPYrgo.js";
|
|
2
|
+
import { a as NetworkManagerOptions, c as LocalGameServer, d as ServerClass, f as createLocalGameServer, g as Unsubscribe, h as NetworkTransport, i as NetworkManager, l as LocalGameServerOptions, m as LoopbackTransport, n as SplitScreenPlayer, o as applySyncPatch, p as LoopbackHub, r as createSplitScreen, s as CollectionOptions, t as SplitScreenOptions, u as LocalGameServerTransport } from "./split-screen-DhrSzZIB.js";
|
|
3
3
|
|
|
4
4
|
//#region src/net/agent8.d.ts
|
|
5
5
|
/**
|
package/dist/net.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { n as createAgent8Server } from "./agent8-CvsfVskX.js";
|
|
2
|
-
import { a as NetworkManager, d as createLocalGameServer, f as LoopbackHub, l as LocalGameServer, n as registerNodesNet, o as applySyncPatch, p as LoopbackTransport, r as NetworkSpawner, t as createSplitScreen, u as LocalGameServerTransport } from "./split-screen-
|
|
2
|
+
import { a as NetworkManager, d as createLocalGameServer, f as LoopbackHub, l as LocalGameServer, n as registerNodesNet, o as applySyncPatch, p as LoopbackTransport, r as NetworkSpawner, t as createSplitScreen, u as LocalGameServerTransport } from "./split-screen-CYwDkbLF.js";
|
|
3
3
|
export { LocalGameServer, LocalGameServerTransport, LoopbackHub, LoopbackTransport, NetworkManager, NetworkSpawner, applySyncPatch, createAgent8Server, createLocalGameServer, createSplitScreen, registerNodesNet };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
-
import { k as diagnose } from "./loader-
|
|
2
|
+
import { k as diagnose } from "./loader-BC4PNtJX.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-
|
|
4
|
+
import { _ as RigidBody2D, b as validateCollider2D, g as PhysicsBody2D, h as CharacterBody2D, m as Area2D, p as Joint2D, y as Node2D } from "./register-ibCjm-wH.js";
|
|
5
5
|
import { n as registerDebugSource } from "./debug-draw-BM3DsvtT.js";
|
|
6
6
|
import { n as jointContacts, t as withoutRapierInitNoise } from "./quiet-rapier-C6fW4zcW.js";
|
|
7
7
|
//#region src/2d/physics/physics-2d.ts
|
|
@@ -66,6 +66,7 @@ var Physics2D = class {
|
|
|
66
66
|
this.unregisterDebug = registerDebugSource(this);
|
|
67
67
|
this.disconnect = engine.fixedUpdated.connect((dt) => this.step(dt));
|
|
68
68
|
this.disconnectScene = engine.sceneChanged.connect(() => {
|
|
69
|
+
this.pruneOverlaps();
|
|
69
70
|
if (engine.scene) this.syncBodies();
|
|
70
71
|
});
|
|
71
72
|
engine.physics = this;
|
|
@@ -306,6 +307,31 @@ var Physics2D = class {
|
|
|
306
307
|
if (set.size === 0) this.overlaps.delete(self);
|
|
307
308
|
}
|
|
308
309
|
}
|
|
310
|
+
/**
|
|
311
|
+
* Forget every overlap whose node has left the tree.
|
|
312
|
+
*
|
|
313
|
+
* The lazy drop inside `overlapping()` only cleans the SETS of a key someone
|
|
314
|
+
* asks about, and only when they ask. This clears the KEYS, which is where the
|
|
315
|
+
* dead nodes were pinned.
|
|
316
|
+
*/
|
|
317
|
+
/** Drop one node from the overlap map, both as a key and as a member. */
|
|
318
|
+
forgetOverlaps(node) {
|
|
319
|
+
this.overlaps.delete(node);
|
|
320
|
+
for (const [self, others] of this.overlaps) {
|
|
321
|
+
others.delete(node);
|
|
322
|
+
if (others.size === 0) this.overlaps.delete(self);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
pruneOverlaps() {
|
|
326
|
+
for (const [self, others] of this.overlaps) {
|
|
327
|
+
if (self.tree === null) {
|
|
328
|
+
this.overlaps.delete(self);
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
for (const other of others) if (other.tree === null) others.delete(other);
|
|
332
|
+
if (others.size === 0) this.overlaps.delete(self);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
309
335
|
/** The bodies currently overlapping `node`. Freed nodes are dropped on read. */
|
|
310
336
|
overlapping(node) {
|
|
311
337
|
const set = this.overlaps.get(node);
|
|
@@ -426,6 +452,7 @@ var Physics2D = class {
|
|
|
426
452
|
this.byColliderHandle.delete(e.collider.handle);
|
|
427
453
|
this.world.removeRigidBody(e.body);
|
|
428
454
|
this.entries.delete(node);
|
|
455
|
+
this.forgetOverlaps(node);
|
|
429
456
|
node._physics = null;
|
|
430
457
|
}
|
|
431
458
|
}
|