@patterkit/runtime 0.12.1 → 0.14.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/CHANGELOG.md +48 -0
- package/README.md +25 -6
- package/dist/index.cjs +275 -125
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +90 -27
- package/dist/index.d.ts +90 -27
- package/dist/index.js +272 -122
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ScalarValue } from '@wildwinter/expr';
|
|
2
|
-
import { ScopeResolver, ScopeRegistry,
|
|
2
|
+
import { ScopeResolver, ScopeRegistry, PropertyBag, ScopeDeclaration, LogMount, PropertyRow } from '@wildwinter/scoperegistry';
|
|
3
3
|
export { PropertyRow } from '@wildwinter/scoperegistry';
|
|
4
|
-
import { GameData, Bundle, CompiledGroup, CompiledSnippet, CompiledBlock, FlowSnapshot, EngineSave, SaveGame, PropertyType, ScalarValue as ScalarValue$1, GameDataNodeKind, GameDataField } from '@patterkit/model';
|
|
4
|
+
import { GameData, Bundle, CompiledGroup, CompiledSnippet, CompiledBlock, FlowSnapshot, EngineSave, SaveGame, SaveGameV2, PropertyType, ScalarValue as ScalarValue$1, GameDataNodeKind, GameDataField } from '@patterkit/model';
|
|
5
5
|
export { Bundle, EngineSave, FlowCursor, FlowSnapshot, SaveEnvelope, SaveGame, SavedChoice, SavedChoiceOption, SavedChoicePrompt, SelectorSnapshot, StackFrame } from '@patterkit/model';
|
|
6
6
|
|
|
7
7
|
type SelectableNode = CompiledGroup | CompiledSnippet;
|
|
@@ -224,8 +224,18 @@ interface EngineOptions {
|
|
|
224
224
|
* Ignored by an "ids" bundle, which emits beat IDs for the game to localise itself. */
|
|
225
225
|
locale?: string;
|
|
226
226
|
/** The host's resolver for **World Properties** (`@world.*`): the values the game owns and the story
|
|
227
|
-
* reads. Omit it and the runtime self-backs `@world` from the declared defaults
|
|
227
|
+
* reads. Omit it and the runtime self-backs `@world` from the declared defaults, as a property the
|
|
228
|
+
* registry stores and saves. Shared by all flows. A game running several engines registers `@world`
|
|
229
|
+
* in its registry itself instead, and no engine is given a resolver. */
|
|
228
230
|
world?: WorldResolver;
|
|
231
|
+
/** The game's registry: ONE per game, holding every engine's properties except those the game keeps
|
|
232
|
+
* itself, saved once. Given one, the engine registers its own scopes in it (`@patter` under `patter`,
|
|
233
|
+
* its per-flow and per-scene bags under keys starting `patter/`, and `@world` if `world` is passed),
|
|
234
|
+
* reads every other scope from it, and `saveGame()` leaves the property values to the game. Host
|
|
235
|
+
* scopes the bundle declares (`@world`) are then the game's to register: owned if the registry should
|
|
236
|
+
* store them, foreign if the game keeps them. Omit it and the engine makes its own registry and acts as
|
|
237
|
+
* its own game: it self-backs `@world`, and `saveGame()` carries the registry's values too. */
|
|
238
|
+
registry?: ScopeRegistry;
|
|
229
239
|
/**
|
|
230
240
|
* Replay a chosen option's `prompt` as its first played beat (spec §5). Default `false`:
|
|
231
241
|
* the prompt is a label only and `choose()` plays just the option's content. `true`: the
|
|
@@ -291,8 +301,14 @@ interface FlowHost {
|
|
|
291
301
|
blockGameIdToId: Map<string, Map<string, string>>;
|
|
292
302
|
/** Author tags (#215): node id -> accumulated tags (own + every ancestor's, deduped). Built once. */
|
|
293
303
|
tagIndex: Map<string, string[]>;
|
|
294
|
-
/** The
|
|
295
|
-
|
|
304
|
+
/** The game's one registry: `@patter` (the SHARED globals), host scopes, every instance bag. */
|
|
305
|
+
registry: ScopeRegistry;
|
|
306
|
+
/** True when the engine made the registry (a standalone game): `saveGame()` then carries its values. */
|
|
307
|
+
readonly ownsRegistry: boolean;
|
|
308
|
+
/** The SHARED `@patter` globals' bag, registered under `patter`. */
|
|
309
|
+
patterBag: PropertyBag;
|
|
310
|
+
/** Host scopes this engine self-backed and registered (the game bound none, nobody else had). */
|
|
311
|
+
hostScopes: string[];
|
|
296
312
|
/** Decls for the shared `@patter` globals - (re)seed on `engine.reset()`. */
|
|
297
313
|
patterSharedDecls: ScopeDeclaration[];
|
|
298
314
|
/** Decls for the per-flow `@patter` globals - seed each flow's local registry. */
|
|
@@ -305,11 +321,8 @@ interface FlowHost {
|
|
|
305
321
|
sharedVisits: Map<string, number>;
|
|
306
322
|
/** Shared selector cursors (node id -> SelectorState) for `shared` memoried selectors. */
|
|
307
323
|
sharedSelectors: Map<string, SelectorState>;
|
|
308
|
-
/**
|
|
309
|
-
|
|
310
|
-
* 2026-09-02: seeding, name normalisation and the mutable-default clone were all
|
|
311
|
-
* written out by hand here, and the shared bag already had them. The SAVE is
|
|
312
|
-
* unchanged - bag.save() is a bare record, which is what the envelope carries. */
|
|
324
|
+
/** Per-scene SHARED scene props, each registered under `keys.stage(sceneId)`. Made the first time any
|
|
325
|
+
* flow needs the scene, so a bag loaded before then waits in the registry and is claimed here. */
|
|
313
326
|
stageBags: Map<string, PropertyBag>;
|
|
314
327
|
customRng?: () => number;
|
|
315
328
|
/** Play a chosen option's prompt as its first beat (spec §5); default false. */
|
|
@@ -327,12 +340,13 @@ interface FlowHost {
|
|
|
327
340
|
* fallback - so it falls through and the flow continues past it. Zero cost when unset; the coverage
|
|
328
341
|
* harness passes it to surface silent fall-throughs. Not a gameplay signal (the behaviour is unchanged). */
|
|
329
342
|
onDryChoice?: (groupId: string) => void;
|
|
330
|
-
/** Memoised `splitRef` results (ref string -> {scope,name}). The split depends only on
|
|
331
|
-
* set,
|
|
343
|
+
/** Memoised `splitRef` results (ref string -> {scope,name}). The split depends only on the registry's
|
|
344
|
+
* scope set, so the cache is dropped whenever that moves (`refSplitRevision`). */
|
|
332
345
|
refSplitCache: Map<string, {
|
|
333
346
|
scope: string;
|
|
334
347
|
name: string;
|
|
335
348
|
}>;
|
|
349
|
+
refSplitRevision: number;
|
|
336
350
|
}
|
|
337
351
|
declare class Engine {
|
|
338
352
|
private readonly host;
|
|
@@ -390,10 +404,12 @@ declare class Engine {
|
|
|
390
404
|
* content-drift policy (§9.8) resolves edits under the cursor: stack frames re-find their next
|
|
391
405
|
* child by id, drifted options drop, a vanished snippet is skipped.
|
|
392
406
|
*
|
|
393
|
-
* Returns the REPLACEMENT engine
|
|
394
|
-
*
|
|
395
|
-
*
|
|
396
|
-
*
|
|
407
|
+
* Returns the REPLACEMENT engine, on the same registry. This one hands its bags over (each is
|
|
408
|
+
* removed from the registry with its values kept, and the replacement claims them as it
|
|
409
|
+
* registers), its flows are closed, and it should be discarded. Hosts re-bind their flow handles
|
|
410
|
+
* via `next.getFlow(id)`. If the restore throws (defensive - §9.8 makes this unreachable for
|
|
411
|
+
* ordinary edits), the swap falls back to a fresh engine with each saved flow restarted from the
|
|
412
|
+
* top of the scene it was in; the shared properties carry over.
|
|
397
413
|
*/
|
|
398
414
|
hotSwap(bundle: Bundle): Engine;
|
|
399
415
|
/** Whether closed captions are currently shown (full dialogue text). */
|
|
@@ -444,6 +460,11 @@ declare class Engine {
|
|
|
444
460
|
* fetch it with `engine.getFlow(name)?.getChoices()`.
|
|
445
461
|
*/
|
|
446
462
|
runFlow(flow: string, scene: string, block?: string): AdvanceToStopResult["played"];
|
|
463
|
+
/** Content that names another engine's scope (`@story.act`) runs only where that engine is on this
|
|
464
|
+
* registry: without it every read would answer false and every write fail, so the flow is refused as
|
|
465
|
+
* it opens (or the save as it loads), before anything changes. By then a game has built all of its
|
|
466
|
+
* engines, whatever order it built them in. The same message on every runtime. */
|
|
467
|
+
private assertExternalScopes;
|
|
447
468
|
/** Resolve a scene reference (a gameId address OR an internal id) to its internal id. */
|
|
448
469
|
private resolveSceneRef;
|
|
449
470
|
/** Resolve a block reference (a scene-scoped gameId OR an internal id) to its internal id. */
|
|
@@ -523,9 +544,18 @@ declare class Engine {
|
|
|
523
544
|
* After reset, open fresh flows with `openFlow`.
|
|
524
545
|
*/
|
|
525
546
|
reset(): void;
|
|
547
|
+
/** Remove every bag this engine registered, keeping the values parked when `keep` (a live reload
|
|
548
|
+
* handing its state to a replacement), and close its flows. The engine is inert afterwards. */
|
|
549
|
+
private release;
|
|
526
550
|
/** Read a shared (`@patter` / foreign) property by ref. `@scene` refs are rejected (flow-level). */
|
|
527
551
|
getProperty(ref: string): ScalarValue | undefined;
|
|
528
|
-
/** Write a shared (`@patter` / foreign) property by ref. `@scene` refs are rejected (flow-level).
|
|
552
|
+
/** Write a shared (`@patter` / foreign) property by ref. `@scene` refs are rejected (flow-level).
|
|
553
|
+
*
|
|
554
|
+
* This is the GAME's surface, so it writes with host authority: a host-scope declaration's
|
|
555
|
+
* `writable: false` is the story's promise not to write the value, never a lock on the game that
|
|
556
|
+
* owns it. The story's own writes come through `Flow.runEffects`, which never claims it. (Ruled
|
|
557
|
+
* across the family 2026-09-05, after a Storylets venue clock and this project's coverage driver
|
|
558
|
+
* were each refused the one property they existed to move.) */
|
|
529
559
|
setProperty(ref: string, value: ScalarValue): void;
|
|
530
560
|
/** The shared `@patter` properties, for a live state inspector: each with its ref, type, current
|
|
531
561
|
* value, declared default (for reset), and enum options. Mirrors the Unity / Godot ports. */
|
|
@@ -542,19 +572,33 @@ declare class Engine {
|
|
|
542
572
|
private emitEngine;
|
|
543
573
|
listProperties(): PropertyRow[];
|
|
544
574
|
private splitShared;
|
|
545
|
-
/**
|
|
575
|
+
/** The shared `@patter` values alone, as `{ patter: {...} }`.
|
|
576
|
+
* @deprecated The registry holds every property now: a game saves `registry.save()` once, and a
|
|
577
|
+
* standalone engine's `saveGame()` carries its registry's values. Removed at the next breaking release. */
|
|
546
578
|
save(): EngineSave;
|
|
547
|
-
/**
|
|
579
|
+
/** Lay a `{ patter: {...} }` section over the shared `@patter` values; anything else is ignored.
|
|
580
|
+
* @deprecated See `save`. Removed at the next breaking release. */
|
|
548
581
|
load(blob: EngineSave): void;
|
|
549
|
-
/** Snapshot the whole game
|
|
582
|
+
/** Snapshot the whole game's NON-property state: visit counts, shared selector cursors, and every
|
|
583
|
+
* live flow's cursor and PRNG. The property values are the registry's: a standalone engine (one that
|
|
584
|
+
* made its own registry) carries them here under `registry`; a game that passed a registry saves it
|
|
585
|
+
* once itself, beside each engine's `saveGame()`. */
|
|
550
586
|
saveGame(): SaveGame;
|
|
551
|
-
/**
|
|
552
|
-
|
|
587
|
+
/**
|
|
588
|
+
* Restore a `saveGame()`: visit counts, shared selector cursors, and every flow. Property values come
|
|
589
|
+
* from the registry. A save that carries them (a standalone engine's, or a version 2 save from before
|
|
590
|
+
* the registry held them) has them moved into the registry here; otherwise the game loads its
|
|
591
|
+
* registry itself, before or after this call. Either order works: this engine's bags are handed back
|
|
592
|
+
* to the registry (values kept) and the restored flows claim them as they register.
|
|
593
|
+
*/
|
|
594
|
+
loadGame(save: SaveGame | SaveGameV2): void;
|
|
553
595
|
}
|
|
554
596
|
declare class Flow {
|
|
555
597
|
readonly id: string;
|
|
556
598
|
private readonly host;
|
|
557
599
|
private local;
|
|
600
|
+
/** The registry keys this flow has registered (its globals and each scene bag). */
|
|
601
|
+
private readonly registered;
|
|
558
602
|
private rngState;
|
|
559
603
|
private started;
|
|
560
604
|
private flowEnded;
|
|
@@ -579,10 +623,14 @@ declare class Flow {
|
|
|
579
623
|
private readonly patterResolver;
|
|
580
624
|
private readonly sceneResolver;
|
|
581
625
|
private readonly evalCtx;
|
|
626
|
+
private ctxRevision;
|
|
627
|
+
private registryQualities;
|
|
582
628
|
private readonly flowLog;
|
|
583
629
|
/** Monotonic across the flow's life; survives clearLog so two reads agree on order. */
|
|
584
630
|
private flowSeq;
|
|
585
631
|
constructor(id: string, host: FlowHost, seed: number);
|
|
632
|
+
/** The eval context, its scopes refreshed if the registry's set of scopes has moved since. */
|
|
633
|
+
private context;
|
|
586
634
|
/** The stage ladder of `@scope.name` when it is a declared quality, else undefined. Names compare
|
|
587
635
|
* lowercase, as the compiler emits references (the selfBackedResolver lesson). */
|
|
588
636
|
private stagesFor;
|
|
@@ -668,9 +716,14 @@ declare class Flow {
|
|
|
668
716
|
isEnded(): boolean;
|
|
669
717
|
/** Read a property by ref - `@patter` / `@scene` (each routed by its `shared` flag) or foreign. */
|
|
670
718
|
getProperty(ref: string): ScalarValue | undefined;
|
|
671
|
-
/** Write a property by ref (routed by scope, then by the property's `shared` flag).
|
|
719
|
+
/** Write a property by ref (routed by scope, then by the property's `shared` flag). The GAME's
|
|
720
|
+
* surface: it writes with host authority, so a `writable: false` host declaration does not refuse
|
|
721
|
+
* it (see `Engine.setProperty`). The story's writes go through `writeProperty` instead. */
|
|
672
722
|
setProperty(ref: string, value: ScalarValue): void;
|
|
673
|
-
/**
|
|
723
|
+
/** The write itself. `host` says WHO is writing, which is the only thing `writable: false` cares
|
|
724
|
+
* about: the story is refused, the game is not. */
|
|
725
|
+
private writeProperty;
|
|
726
|
+
/** @internal Snapshot this flow's cursor + PRNG + visits. Its properties are the registry's. */
|
|
674
727
|
snapshot(): FlowSnapshot;
|
|
675
728
|
/** @internal Restore this flow from a snapshot. */
|
|
676
729
|
restore(snap: FlowSnapshot): void;
|
|
@@ -782,10 +835,20 @@ declare class Flow {
|
|
|
782
835
|
* locale, else the authoring `displayName`. Undefined when the character has no display name at all
|
|
783
836
|
* (the host falls back to the `character` token itself). */
|
|
784
837
|
private resolveCharacterName;
|
|
785
|
-
/** Split a ref into scope + name. Tokens: `@scene`,
|
|
838
|
+
/** Split a ref into scope + name. Tokens: `@scene`, registered tokens, else `@patter` (incl. bare `@name`). */
|
|
786
839
|
private splitRef;
|
|
787
|
-
/**
|
|
788
|
-
private
|
|
840
|
+
/** A fresh, unregistered bag for the NOT-shared `@patter` globals (the shared ones live on the host). */
|
|
841
|
+
private newLocal;
|
|
842
|
+
/** Register a fresh globals bag under this flow's key; it claims any values waiting there. */
|
|
843
|
+
private mountLocal;
|
|
844
|
+
/** @internal Remove every bag this flow registered; with `keep`, their values wait in the registry for
|
|
845
|
+
* the flow that replaces this one. Engine-driven (close, loadGame, hotSwap). */
|
|
846
|
+
releaseBags(keep: boolean): void;
|
|
847
|
+
/** The bag a `@scene` property of scene `s` lives in (stage or this flow's), made if missing. */
|
|
848
|
+
private sceneBagFor;
|
|
849
|
+
/** Make (and register) scene `s`'s stage bag and this flow's bag for it, if not made yet. A bag made
|
|
850
|
+
* here claims whatever values the registry holds for its key: that is how a loaded save reaches it. */
|
|
851
|
+
private ensureSceneBags;
|
|
789
852
|
/**
|
|
790
853
|
* Seed a scene's `@scene` props (spec §7). The not-shared props seed THIS flow's
|
|
791
854
|
* bag the first time it enters (persist across re-entries thereafter); the shared
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ScalarValue } from '@wildwinter/expr';
|
|
2
|
-
import { ScopeResolver, ScopeRegistry,
|
|
2
|
+
import { ScopeResolver, ScopeRegistry, PropertyBag, ScopeDeclaration, LogMount, PropertyRow } from '@wildwinter/scoperegistry';
|
|
3
3
|
export { PropertyRow } from '@wildwinter/scoperegistry';
|
|
4
|
-
import { GameData, Bundle, CompiledGroup, CompiledSnippet, CompiledBlock, FlowSnapshot, EngineSave, SaveGame, PropertyType, ScalarValue as ScalarValue$1, GameDataNodeKind, GameDataField } from '@patterkit/model';
|
|
4
|
+
import { GameData, Bundle, CompiledGroup, CompiledSnippet, CompiledBlock, FlowSnapshot, EngineSave, SaveGame, SaveGameV2, PropertyType, ScalarValue as ScalarValue$1, GameDataNodeKind, GameDataField } from '@patterkit/model';
|
|
5
5
|
export { Bundle, EngineSave, FlowCursor, FlowSnapshot, SaveEnvelope, SaveGame, SavedChoice, SavedChoiceOption, SavedChoicePrompt, SelectorSnapshot, StackFrame } from '@patterkit/model';
|
|
6
6
|
|
|
7
7
|
type SelectableNode = CompiledGroup | CompiledSnippet;
|
|
@@ -224,8 +224,18 @@ interface EngineOptions {
|
|
|
224
224
|
* Ignored by an "ids" bundle, which emits beat IDs for the game to localise itself. */
|
|
225
225
|
locale?: string;
|
|
226
226
|
/** The host's resolver for **World Properties** (`@world.*`): the values the game owns and the story
|
|
227
|
-
* reads. Omit it and the runtime self-backs `@world` from the declared defaults
|
|
227
|
+
* reads. Omit it and the runtime self-backs `@world` from the declared defaults, as a property the
|
|
228
|
+
* registry stores and saves. Shared by all flows. A game running several engines registers `@world`
|
|
229
|
+
* in its registry itself instead, and no engine is given a resolver. */
|
|
228
230
|
world?: WorldResolver;
|
|
231
|
+
/** The game's registry: ONE per game, holding every engine's properties except those the game keeps
|
|
232
|
+
* itself, saved once. Given one, the engine registers its own scopes in it (`@patter` under `patter`,
|
|
233
|
+
* its per-flow and per-scene bags under keys starting `patter/`, and `@world` if `world` is passed),
|
|
234
|
+
* reads every other scope from it, and `saveGame()` leaves the property values to the game. Host
|
|
235
|
+
* scopes the bundle declares (`@world`) are then the game's to register: owned if the registry should
|
|
236
|
+
* store them, foreign if the game keeps them. Omit it and the engine makes its own registry and acts as
|
|
237
|
+
* its own game: it self-backs `@world`, and `saveGame()` carries the registry's values too. */
|
|
238
|
+
registry?: ScopeRegistry;
|
|
229
239
|
/**
|
|
230
240
|
* Replay a chosen option's `prompt` as its first played beat (spec §5). Default `false`:
|
|
231
241
|
* the prompt is a label only and `choose()` plays just the option's content. `true`: the
|
|
@@ -291,8 +301,14 @@ interface FlowHost {
|
|
|
291
301
|
blockGameIdToId: Map<string, Map<string, string>>;
|
|
292
302
|
/** Author tags (#215): node id -> accumulated tags (own + every ancestor's, deduped). Built once. */
|
|
293
303
|
tagIndex: Map<string, string[]>;
|
|
294
|
-
/** The
|
|
295
|
-
|
|
304
|
+
/** The game's one registry: `@patter` (the SHARED globals), host scopes, every instance bag. */
|
|
305
|
+
registry: ScopeRegistry;
|
|
306
|
+
/** True when the engine made the registry (a standalone game): `saveGame()` then carries its values. */
|
|
307
|
+
readonly ownsRegistry: boolean;
|
|
308
|
+
/** The SHARED `@patter` globals' bag, registered under `patter`. */
|
|
309
|
+
patterBag: PropertyBag;
|
|
310
|
+
/** Host scopes this engine self-backed and registered (the game bound none, nobody else had). */
|
|
311
|
+
hostScopes: string[];
|
|
296
312
|
/** Decls for the shared `@patter` globals - (re)seed on `engine.reset()`. */
|
|
297
313
|
patterSharedDecls: ScopeDeclaration[];
|
|
298
314
|
/** Decls for the per-flow `@patter` globals - seed each flow's local registry. */
|
|
@@ -305,11 +321,8 @@ interface FlowHost {
|
|
|
305
321
|
sharedVisits: Map<string, number>;
|
|
306
322
|
/** Shared selector cursors (node id -> SelectorState) for `shared` memoried selectors. */
|
|
307
323
|
sharedSelectors: Map<string, SelectorState>;
|
|
308
|
-
/**
|
|
309
|
-
|
|
310
|
-
* 2026-09-02: seeding, name normalisation and the mutable-default clone were all
|
|
311
|
-
* written out by hand here, and the shared bag already had them. The SAVE is
|
|
312
|
-
* unchanged - bag.save() is a bare record, which is what the envelope carries. */
|
|
324
|
+
/** Per-scene SHARED scene props, each registered under `keys.stage(sceneId)`. Made the first time any
|
|
325
|
+
* flow needs the scene, so a bag loaded before then waits in the registry and is claimed here. */
|
|
313
326
|
stageBags: Map<string, PropertyBag>;
|
|
314
327
|
customRng?: () => number;
|
|
315
328
|
/** Play a chosen option's prompt as its first beat (spec §5); default false. */
|
|
@@ -327,12 +340,13 @@ interface FlowHost {
|
|
|
327
340
|
* fallback - so it falls through and the flow continues past it. Zero cost when unset; the coverage
|
|
328
341
|
* harness passes it to surface silent fall-throughs. Not a gameplay signal (the behaviour is unchanged). */
|
|
329
342
|
onDryChoice?: (groupId: string) => void;
|
|
330
|
-
/** Memoised `splitRef` results (ref string -> {scope,name}). The split depends only on
|
|
331
|
-
* set,
|
|
343
|
+
/** Memoised `splitRef` results (ref string -> {scope,name}). The split depends only on the registry's
|
|
344
|
+
* scope set, so the cache is dropped whenever that moves (`refSplitRevision`). */
|
|
332
345
|
refSplitCache: Map<string, {
|
|
333
346
|
scope: string;
|
|
334
347
|
name: string;
|
|
335
348
|
}>;
|
|
349
|
+
refSplitRevision: number;
|
|
336
350
|
}
|
|
337
351
|
declare class Engine {
|
|
338
352
|
private readonly host;
|
|
@@ -390,10 +404,12 @@ declare class Engine {
|
|
|
390
404
|
* content-drift policy (§9.8) resolves edits under the cursor: stack frames re-find their next
|
|
391
405
|
* child by id, drifted options drop, a vanished snippet is skipped.
|
|
392
406
|
*
|
|
393
|
-
* Returns the REPLACEMENT engine
|
|
394
|
-
*
|
|
395
|
-
*
|
|
396
|
-
*
|
|
407
|
+
* Returns the REPLACEMENT engine, on the same registry. This one hands its bags over (each is
|
|
408
|
+
* removed from the registry with its values kept, and the replacement claims them as it
|
|
409
|
+
* registers), its flows are closed, and it should be discarded. Hosts re-bind their flow handles
|
|
410
|
+
* via `next.getFlow(id)`. If the restore throws (defensive - §9.8 makes this unreachable for
|
|
411
|
+
* ordinary edits), the swap falls back to a fresh engine with each saved flow restarted from the
|
|
412
|
+
* top of the scene it was in; the shared properties carry over.
|
|
397
413
|
*/
|
|
398
414
|
hotSwap(bundle: Bundle): Engine;
|
|
399
415
|
/** Whether closed captions are currently shown (full dialogue text). */
|
|
@@ -444,6 +460,11 @@ declare class Engine {
|
|
|
444
460
|
* fetch it with `engine.getFlow(name)?.getChoices()`.
|
|
445
461
|
*/
|
|
446
462
|
runFlow(flow: string, scene: string, block?: string): AdvanceToStopResult["played"];
|
|
463
|
+
/** Content that names another engine's scope (`@story.act`) runs only where that engine is on this
|
|
464
|
+
* registry: without it every read would answer false and every write fail, so the flow is refused as
|
|
465
|
+
* it opens (or the save as it loads), before anything changes. By then a game has built all of its
|
|
466
|
+
* engines, whatever order it built them in. The same message on every runtime. */
|
|
467
|
+
private assertExternalScopes;
|
|
447
468
|
/** Resolve a scene reference (a gameId address OR an internal id) to its internal id. */
|
|
448
469
|
private resolveSceneRef;
|
|
449
470
|
/** Resolve a block reference (a scene-scoped gameId OR an internal id) to its internal id. */
|
|
@@ -523,9 +544,18 @@ declare class Engine {
|
|
|
523
544
|
* After reset, open fresh flows with `openFlow`.
|
|
524
545
|
*/
|
|
525
546
|
reset(): void;
|
|
547
|
+
/** Remove every bag this engine registered, keeping the values parked when `keep` (a live reload
|
|
548
|
+
* handing its state to a replacement), and close its flows. The engine is inert afterwards. */
|
|
549
|
+
private release;
|
|
526
550
|
/** Read a shared (`@patter` / foreign) property by ref. `@scene` refs are rejected (flow-level). */
|
|
527
551
|
getProperty(ref: string): ScalarValue | undefined;
|
|
528
|
-
/** Write a shared (`@patter` / foreign) property by ref. `@scene` refs are rejected (flow-level).
|
|
552
|
+
/** Write a shared (`@patter` / foreign) property by ref. `@scene` refs are rejected (flow-level).
|
|
553
|
+
*
|
|
554
|
+
* This is the GAME's surface, so it writes with host authority: a host-scope declaration's
|
|
555
|
+
* `writable: false` is the story's promise not to write the value, never a lock on the game that
|
|
556
|
+
* owns it. The story's own writes come through `Flow.runEffects`, which never claims it. (Ruled
|
|
557
|
+
* across the family 2026-09-05, after a Storylets venue clock and this project's coverage driver
|
|
558
|
+
* were each refused the one property they existed to move.) */
|
|
529
559
|
setProperty(ref: string, value: ScalarValue): void;
|
|
530
560
|
/** The shared `@patter` properties, for a live state inspector: each with its ref, type, current
|
|
531
561
|
* value, declared default (for reset), and enum options. Mirrors the Unity / Godot ports. */
|
|
@@ -542,19 +572,33 @@ declare class Engine {
|
|
|
542
572
|
private emitEngine;
|
|
543
573
|
listProperties(): PropertyRow[];
|
|
544
574
|
private splitShared;
|
|
545
|
-
/**
|
|
575
|
+
/** The shared `@patter` values alone, as `{ patter: {...} }`.
|
|
576
|
+
* @deprecated The registry holds every property now: a game saves `registry.save()` once, and a
|
|
577
|
+
* standalone engine's `saveGame()` carries its registry's values. Removed at the next breaking release. */
|
|
546
578
|
save(): EngineSave;
|
|
547
|
-
/**
|
|
579
|
+
/** Lay a `{ patter: {...} }` section over the shared `@patter` values; anything else is ignored.
|
|
580
|
+
* @deprecated See `save`. Removed at the next breaking release. */
|
|
548
581
|
load(blob: EngineSave): void;
|
|
549
|
-
/** Snapshot the whole game
|
|
582
|
+
/** Snapshot the whole game's NON-property state: visit counts, shared selector cursors, and every
|
|
583
|
+
* live flow's cursor and PRNG. The property values are the registry's: a standalone engine (one that
|
|
584
|
+
* made its own registry) carries them here under `registry`; a game that passed a registry saves it
|
|
585
|
+
* once itself, beside each engine's `saveGame()`. */
|
|
550
586
|
saveGame(): SaveGame;
|
|
551
|
-
/**
|
|
552
|
-
|
|
587
|
+
/**
|
|
588
|
+
* Restore a `saveGame()`: visit counts, shared selector cursors, and every flow. Property values come
|
|
589
|
+
* from the registry. A save that carries them (a standalone engine's, or a version 2 save from before
|
|
590
|
+
* the registry held them) has them moved into the registry here; otherwise the game loads its
|
|
591
|
+
* registry itself, before or after this call. Either order works: this engine's bags are handed back
|
|
592
|
+
* to the registry (values kept) and the restored flows claim them as they register.
|
|
593
|
+
*/
|
|
594
|
+
loadGame(save: SaveGame | SaveGameV2): void;
|
|
553
595
|
}
|
|
554
596
|
declare class Flow {
|
|
555
597
|
readonly id: string;
|
|
556
598
|
private readonly host;
|
|
557
599
|
private local;
|
|
600
|
+
/** The registry keys this flow has registered (its globals and each scene bag). */
|
|
601
|
+
private readonly registered;
|
|
558
602
|
private rngState;
|
|
559
603
|
private started;
|
|
560
604
|
private flowEnded;
|
|
@@ -579,10 +623,14 @@ declare class Flow {
|
|
|
579
623
|
private readonly patterResolver;
|
|
580
624
|
private readonly sceneResolver;
|
|
581
625
|
private readonly evalCtx;
|
|
626
|
+
private ctxRevision;
|
|
627
|
+
private registryQualities;
|
|
582
628
|
private readonly flowLog;
|
|
583
629
|
/** Monotonic across the flow's life; survives clearLog so two reads agree on order. */
|
|
584
630
|
private flowSeq;
|
|
585
631
|
constructor(id: string, host: FlowHost, seed: number);
|
|
632
|
+
/** The eval context, its scopes refreshed if the registry's set of scopes has moved since. */
|
|
633
|
+
private context;
|
|
586
634
|
/** The stage ladder of `@scope.name` when it is a declared quality, else undefined. Names compare
|
|
587
635
|
* lowercase, as the compiler emits references (the selfBackedResolver lesson). */
|
|
588
636
|
private stagesFor;
|
|
@@ -668,9 +716,14 @@ declare class Flow {
|
|
|
668
716
|
isEnded(): boolean;
|
|
669
717
|
/** Read a property by ref - `@patter` / `@scene` (each routed by its `shared` flag) or foreign. */
|
|
670
718
|
getProperty(ref: string): ScalarValue | undefined;
|
|
671
|
-
/** Write a property by ref (routed by scope, then by the property's `shared` flag).
|
|
719
|
+
/** Write a property by ref (routed by scope, then by the property's `shared` flag). The GAME's
|
|
720
|
+
* surface: it writes with host authority, so a `writable: false` host declaration does not refuse
|
|
721
|
+
* it (see `Engine.setProperty`). The story's writes go through `writeProperty` instead. */
|
|
672
722
|
setProperty(ref: string, value: ScalarValue): void;
|
|
673
|
-
/**
|
|
723
|
+
/** The write itself. `host` says WHO is writing, which is the only thing `writable: false` cares
|
|
724
|
+
* about: the story is refused, the game is not. */
|
|
725
|
+
private writeProperty;
|
|
726
|
+
/** @internal Snapshot this flow's cursor + PRNG + visits. Its properties are the registry's. */
|
|
674
727
|
snapshot(): FlowSnapshot;
|
|
675
728
|
/** @internal Restore this flow from a snapshot. */
|
|
676
729
|
restore(snap: FlowSnapshot): void;
|
|
@@ -782,10 +835,20 @@ declare class Flow {
|
|
|
782
835
|
* locale, else the authoring `displayName`. Undefined when the character has no display name at all
|
|
783
836
|
* (the host falls back to the `character` token itself). */
|
|
784
837
|
private resolveCharacterName;
|
|
785
|
-
/** Split a ref into scope + name. Tokens: `@scene`,
|
|
838
|
+
/** Split a ref into scope + name. Tokens: `@scene`, registered tokens, else `@patter` (incl. bare `@name`). */
|
|
786
839
|
private splitRef;
|
|
787
|
-
/**
|
|
788
|
-
private
|
|
840
|
+
/** A fresh, unregistered bag for the NOT-shared `@patter` globals (the shared ones live on the host). */
|
|
841
|
+
private newLocal;
|
|
842
|
+
/** Register a fresh globals bag under this flow's key; it claims any values waiting there. */
|
|
843
|
+
private mountLocal;
|
|
844
|
+
/** @internal Remove every bag this flow registered; with `keep`, their values wait in the registry for
|
|
845
|
+
* the flow that replaces this one. Engine-driven (close, loadGame, hotSwap). */
|
|
846
|
+
releaseBags(keep: boolean): void;
|
|
847
|
+
/** The bag a `@scene` property of scene `s` lives in (stage or this flow's), made if missing. */
|
|
848
|
+
private sceneBagFor;
|
|
849
|
+
/** Make (and register) scene `s`'s stage bag and this flow's bag for it, if not made yet. A bag made
|
|
850
|
+
* here claims whatever values the registry holds for its key: that is how a loaded save reaches it. */
|
|
851
|
+
private ensureSceneBags;
|
|
789
852
|
/**
|
|
790
853
|
* Seed a scene's `@scene` props (spec §7). The not-shared props seed THIS flow's
|
|
791
854
|
* bag the first time it enters (persist across re-entries thereafter); the shared
|