@patterkit/runtime 0.15.0 → 0.17.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 +33 -0
- package/dist/index.cjs +199 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +70 -3
- package/dist/index.d.ts +70 -3
- package/dist/index.js +198 -12
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ScalarValue } from '@wildwinter/expr';
|
|
2
|
-
import {
|
|
2
|
+
import { ScopeRegistry, PropertyBag, ScopeDeclaration, LogMount, ScopeResolver, PropertyRow } from '@wildwinter/scoperegistry';
|
|
3
3
|
export { PropertyRow } from '@wildwinter/scoperegistry';
|
|
4
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';
|
|
@@ -277,6 +277,25 @@ interface SelectorState {
|
|
|
277
277
|
bag?: string[];
|
|
278
278
|
last?: string;
|
|
279
279
|
}
|
|
280
|
+
/** What an open checkpoint records: how to undo each change, newest last. Each piece of state records
|
|
281
|
+
* itself when it changes (a value, a count, a cursor), never a copy of everything, so a checkpoint costs
|
|
282
|
+
* what the steps inside it do, however much history the game has built up. */
|
|
283
|
+
interface Journal {
|
|
284
|
+
undo: Array<() => void>;
|
|
285
|
+
/** Flows already snapshotted (or opened inside the checkpoint, so closed on rollback). */
|
|
286
|
+
flows: Set<Flow>;
|
|
287
|
+
/** Flows opened inside the checkpoint. */
|
|
288
|
+
opened: Set<Flow>;
|
|
289
|
+
/** Selector cursors already copied: shared ones by group id, a flow's own in `flowSelectors`. */
|
|
290
|
+
selectors: Set<string>;
|
|
291
|
+
flowSelectors: Map<Flow, Set<string>>;
|
|
292
|
+
}
|
|
293
|
+
/** An open checkpoint, from {@link Engine.checkpoint}. Opaque: hand it back to `rollback` or `commit`. */
|
|
294
|
+
declare class Checkpoint {
|
|
295
|
+
readonly journal: Journal;
|
|
296
|
+
/** @internal */
|
|
297
|
+
constructor(journal: Journal);
|
|
298
|
+
}
|
|
280
299
|
/** Shared, read-mostly context the engine hands to every flow it owns. */
|
|
281
300
|
interface FlowHost {
|
|
282
301
|
/** True when the run asked for a log; flows skip building entries otherwise. */
|
|
@@ -328,6 +347,8 @@ interface FlowHost {
|
|
|
328
347
|
/** Per-scene SHARED scene props, each registered under `keys.stage(sceneId)`. Made the first time any
|
|
329
348
|
* flow needs the scene, so a bag loaded before then waits in the registry and is claimed here. */
|
|
330
349
|
stageBags: Map<string, PropertyBag>;
|
|
350
|
+
/** The open checkpoint's undo journal, or null when none is open (see {@link Engine.checkpoint}). */
|
|
351
|
+
journal: Journal | null;
|
|
331
352
|
customRng?: () => number;
|
|
332
353
|
/** Play a chosen option's prompt as its first beat (spec §5); default false. */
|
|
333
354
|
replayPromptOnChoose?: boolean;
|
|
@@ -438,6 +459,10 @@ declare class Engine {
|
|
|
438
459
|
*
|
|
439
460
|
* Contrast {@link runFlow}, which REUSES a flow of the same name instead of replacing it - that is the
|
|
440
461
|
* call to reach for when you want a speaker's variation state to carry on.
|
|
462
|
+
*
|
|
463
|
+
* THROWS on an address that does not resolve (an unknown scene, or a block that is not in the named
|
|
464
|
+
* scene), before anything changes: nothing is opened, and a flow already open under `id` carries on
|
|
465
|
+
* exactly as it was.
|
|
441
466
|
*/
|
|
442
467
|
openFlow(id: string, opts?: OpenFlowOptions): Flow;
|
|
443
468
|
/**
|
|
@@ -469,6 +494,13 @@ declare class Engine {
|
|
|
469
494
|
* it opens (or the save as it loads), before anything changes. By then a game has built all of its
|
|
470
495
|
* engines, whatever order it built them in. The same message on every runtime. */
|
|
471
496
|
private assertExternalScopes;
|
|
497
|
+
/**
|
|
498
|
+
* `openFlow`'s address as internal ids, or a throw when it does not resolve. With a scene, the block is
|
|
499
|
+
* scene-scoped exactly as {@link Flow.goto} resolves it (a gameId address in that scene, or the internal
|
|
500
|
+
* id of a block in that scene), so a block from another scene does not resolve. With no scene, the block
|
|
501
|
+
* is an internal id from any scene. Neither: the bundle's first scene. The same rule on every runtime.
|
|
502
|
+
*/
|
|
503
|
+
private resolveOpenAddress;
|
|
472
504
|
/** Resolve a scene reference (a gameId address OR an internal id) to its internal id. */
|
|
473
505
|
private resolveSceneRef;
|
|
474
506
|
/** Resolve a block reference (a scene-scoped gameId OR an internal id) to its internal id. */
|
|
@@ -548,6 +580,34 @@ declare class Engine {
|
|
|
548
580
|
* loadGame() replaces every bag, so re-enumerate after a load.
|
|
549
581
|
*/
|
|
550
582
|
listBags(): LogMount[];
|
|
583
|
+
/**
|
|
584
|
+
* Open a checkpoint: from here until `rollback` or `commit`, the engine keeps what it needs to undo
|
|
585
|
+
* every change the story makes. `rollback` puts the game back exactly as it was at the checkpoint:
|
|
586
|
+
* property values (every scope, including a host's `@world`, which is written back through the
|
|
587
|
+
* game), visit counts, shuffle and sequence positions, `@scene` bags made since, every flow's cursor
|
|
588
|
+
* and PRNG, and any flow opened since (closed and forgotten). `commit` keeps everything and stops
|
|
589
|
+
* recording.
|
|
590
|
+
*
|
|
591
|
+
* The use is asking "would this say anything?" without consequences: move a flow to an address,
|
|
592
|
+
* advance it, and roll back if it had nothing to give, so its scene's `onEntry` effects, its visits,
|
|
593
|
+
* and its shuffle draws never happened.
|
|
594
|
+
*
|
|
595
|
+
* One checkpoint at a time. Inside one, the calls that replace or drop flows or their bags wholesale
|
|
596
|
+
* (`reset`, `loadGame`, `hotSwap`, `closeFlow`, `openFlow` on the name of an open flow, and a flow's own
|
|
597
|
+
* `start`, `reset`, and `restore`) throw. Not undone:
|
|
598
|
+
* trace and log events already emitted, and draws from a custom `rng` the game supplied.
|
|
599
|
+
*/
|
|
600
|
+
checkpoint(): Checkpoint;
|
|
601
|
+
/** Undo everything since `checkpoint` (see there), and close it. */
|
|
602
|
+
rollback(checkpoint: Checkpoint): void;
|
|
603
|
+
/** Keep everything since `checkpoint`, and close it. */
|
|
604
|
+
commit(checkpoint: Checkpoint): void;
|
|
605
|
+
/** True while a checkpoint is open. */
|
|
606
|
+
get inCheckpoint(): boolean;
|
|
607
|
+
private endCheckpoint;
|
|
608
|
+
/** Let go of what a closed checkpoint recorded, so a handle the game keeps holds nothing. */
|
|
609
|
+
private static forget;
|
|
610
|
+
private refuseInCheckpoint;
|
|
551
611
|
/** Close (remove) a flow. The flow object is FINISHED, not merely unregistered, so a host still
|
|
552
612
|
* holding it cannot keep advancing it into the shared world (see {@link Flow.close}). */
|
|
553
613
|
closeFlow(id: string): void;
|
|
@@ -686,6 +746,10 @@ declare class Flow {
|
|
|
686
746
|
* cannot quietly mutate the world. Terminal: unlike ending, a close is never revived.
|
|
687
747
|
*/
|
|
688
748
|
close(): void;
|
|
749
|
+
/** Inside a checkpoint, the first change to this flow records its cursor and PRNG, so a rollback can
|
|
750
|
+
* put them back: a handful of fields and a copy of the call stack (a few frames), never its history.
|
|
751
|
+
* Its visits, selector cursors, and property values are recorded change by change as they happen. */
|
|
752
|
+
private touch;
|
|
689
753
|
/** True once the engine has closed this flow (closed, dropped by `reset()`, or replaced by name). */
|
|
690
754
|
get isClosed(): boolean;
|
|
691
755
|
/** The scene the cursor is currently in - set on entry and whenever a jump crosses scenes. Read
|
|
@@ -739,8 +803,11 @@ declare class Flow {
|
|
|
739
803
|
private writeProperty;
|
|
740
804
|
/** @internal Snapshot this flow's cursor + PRNG + visits. Its properties are the registry's. */
|
|
741
805
|
snapshot(): FlowSnapshot;
|
|
742
|
-
/** @internal Restore this flow from a snapshot.
|
|
806
|
+
/** @internal Restore this flow from a snapshot: its cursor, PRNG, visits, and selector cursors. Its
|
|
807
|
+
* property values are the registry's and stay as they are (a load puts the saved ones there first). */
|
|
743
808
|
restore(snap: FlowSnapshot): void;
|
|
809
|
+
/** The cursor half of `restore`, without touching the bags (a rollback has already put those back). */
|
|
810
|
+
private restoreCursor;
|
|
744
811
|
/** Set the current scene, reset its scene-local props, run onEntry. */
|
|
745
812
|
private enterSceneSetup;
|
|
746
813
|
/**
|
|
@@ -1027,4 +1094,4 @@ declare function effectiveGameData(fields: GameDataField[], node: GameData | und
|
|
|
1027
1094
|
*/
|
|
1028
1095
|
declare function buildTagIndex(bundle: Bundle): Map<string, string[]>;
|
|
1029
1096
|
|
|
1030
|
-
export { type AddressSummary, type AdvanceToStopResult, type BeatInfo, type BundleCounts, type BundleDescription, type BundleIdentity, type ChoiceOption, Engine, type EngineLogEntry, type EngineOptions, type EngineTraceHandler, type FlatBeat, Flow, type GameDataFieldSummary, type GameDataSummary, type HostScopeSummary, type LogEntry, type OpenFlowOptions, type OutlineBlock, type OutlineNode, type OutlineScene, type OwnedProperties, type PropertySummary, type StepResult, type TraceEvent, type TraceHandler, type WorldResolver, buildTagIndex, describeBundle, effectiveGameData, gameDataFields, gameDataValue };
|
|
1097
|
+
export { type AddressSummary, type AdvanceToStopResult, type BeatInfo, type BundleCounts, type BundleDescription, type BundleIdentity, Checkpoint, type ChoiceOption, Engine, type EngineLogEntry, type EngineOptions, type EngineTraceHandler, type FlatBeat, Flow, type GameDataFieldSummary, type GameDataSummary, type HostScopeSummary, type LogEntry, type OpenFlowOptions, type OutlineBlock, type OutlineNode, type OutlineScene, type OwnedProperties, type PropertySummary, type StepResult, type TraceEvent, type TraceHandler, type WorldResolver, buildTagIndex, describeBundle, effectiveGameData, gameDataFields, gameDataValue };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ScalarValue } from '@wildwinter/expr';
|
|
2
|
-
import {
|
|
2
|
+
import { ScopeRegistry, PropertyBag, ScopeDeclaration, LogMount, ScopeResolver, PropertyRow } from '@wildwinter/scoperegistry';
|
|
3
3
|
export { PropertyRow } from '@wildwinter/scoperegistry';
|
|
4
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';
|
|
@@ -277,6 +277,25 @@ interface SelectorState {
|
|
|
277
277
|
bag?: string[];
|
|
278
278
|
last?: string;
|
|
279
279
|
}
|
|
280
|
+
/** What an open checkpoint records: how to undo each change, newest last. Each piece of state records
|
|
281
|
+
* itself when it changes (a value, a count, a cursor), never a copy of everything, so a checkpoint costs
|
|
282
|
+
* what the steps inside it do, however much history the game has built up. */
|
|
283
|
+
interface Journal {
|
|
284
|
+
undo: Array<() => void>;
|
|
285
|
+
/** Flows already snapshotted (or opened inside the checkpoint, so closed on rollback). */
|
|
286
|
+
flows: Set<Flow>;
|
|
287
|
+
/** Flows opened inside the checkpoint. */
|
|
288
|
+
opened: Set<Flow>;
|
|
289
|
+
/** Selector cursors already copied: shared ones by group id, a flow's own in `flowSelectors`. */
|
|
290
|
+
selectors: Set<string>;
|
|
291
|
+
flowSelectors: Map<Flow, Set<string>>;
|
|
292
|
+
}
|
|
293
|
+
/** An open checkpoint, from {@link Engine.checkpoint}. Opaque: hand it back to `rollback` or `commit`. */
|
|
294
|
+
declare class Checkpoint {
|
|
295
|
+
readonly journal: Journal;
|
|
296
|
+
/** @internal */
|
|
297
|
+
constructor(journal: Journal);
|
|
298
|
+
}
|
|
280
299
|
/** Shared, read-mostly context the engine hands to every flow it owns. */
|
|
281
300
|
interface FlowHost {
|
|
282
301
|
/** True when the run asked for a log; flows skip building entries otherwise. */
|
|
@@ -328,6 +347,8 @@ interface FlowHost {
|
|
|
328
347
|
/** Per-scene SHARED scene props, each registered under `keys.stage(sceneId)`. Made the first time any
|
|
329
348
|
* flow needs the scene, so a bag loaded before then waits in the registry and is claimed here. */
|
|
330
349
|
stageBags: Map<string, PropertyBag>;
|
|
350
|
+
/** The open checkpoint's undo journal, or null when none is open (see {@link Engine.checkpoint}). */
|
|
351
|
+
journal: Journal | null;
|
|
331
352
|
customRng?: () => number;
|
|
332
353
|
/** Play a chosen option's prompt as its first beat (spec §5); default false. */
|
|
333
354
|
replayPromptOnChoose?: boolean;
|
|
@@ -438,6 +459,10 @@ declare class Engine {
|
|
|
438
459
|
*
|
|
439
460
|
* Contrast {@link runFlow}, which REUSES a flow of the same name instead of replacing it - that is the
|
|
440
461
|
* call to reach for when you want a speaker's variation state to carry on.
|
|
462
|
+
*
|
|
463
|
+
* THROWS on an address that does not resolve (an unknown scene, or a block that is not in the named
|
|
464
|
+
* scene), before anything changes: nothing is opened, and a flow already open under `id` carries on
|
|
465
|
+
* exactly as it was.
|
|
441
466
|
*/
|
|
442
467
|
openFlow(id: string, opts?: OpenFlowOptions): Flow;
|
|
443
468
|
/**
|
|
@@ -469,6 +494,13 @@ declare class Engine {
|
|
|
469
494
|
* it opens (or the save as it loads), before anything changes. By then a game has built all of its
|
|
470
495
|
* engines, whatever order it built them in. The same message on every runtime. */
|
|
471
496
|
private assertExternalScopes;
|
|
497
|
+
/**
|
|
498
|
+
* `openFlow`'s address as internal ids, or a throw when it does not resolve. With a scene, the block is
|
|
499
|
+
* scene-scoped exactly as {@link Flow.goto} resolves it (a gameId address in that scene, or the internal
|
|
500
|
+
* id of a block in that scene), so a block from another scene does not resolve. With no scene, the block
|
|
501
|
+
* is an internal id from any scene. Neither: the bundle's first scene. The same rule on every runtime.
|
|
502
|
+
*/
|
|
503
|
+
private resolveOpenAddress;
|
|
472
504
|
/** Resolve a scene reference (a gameId address OR an internal id) to its internal id. */
|
|
473
505
|
private resolveSceneRef;
|
|
474
506
|
/** Resolve a block reference (a scene-scoped gameId OR an internal id) to its internal id. */
|
|
@@ -548,6 +580,34 @@ declare class Engine {
|
|
|
548
580
|
* loadGame() replaces every bag, so re-enumerate after a load.
|
|
549
581
|
*/
|
|
550
582
|
listBags(): LogMount[];
|
|
583
|
+
/**
|
|
584
|
+
* Open a checkpoint: from here until `rollback` or `commit`, the engine keeps what it needs to undo
|
|
585
|
+
* every change the story makes. `rollback` puts the game back exactly as it was at the checkpoint:
|
|
586
|
+
* property values (every scope, including a host's `@world`, which is written back through the
|
|
587
|
+
* game), visit counts, shuffle and sequence positions, `@scene` bags made since, every flow's cursor
|
|
588
|
+
* and PRNG, and any flow opened since (closed and forgotten). `commit` keeps everything and stops
|
|
589
|
+
* recording.
|
|
590
|
+
*
|
|
591
|
+
* The use is asking "would this say anything?" without consequences: move a flow to an address,
|
|
592
|
+
* advance it, and roll back if it had nothing to give, so its scene's `onEntry` effects, its visits,
|
|
593
|
+
* and its shuffle draws never happened.
|
|
594
|
+
*
|
|
595
|
+
* One checkpoint at a time. Inside one, the calls that replace or drop flows or their bags wholesale
|
|
596
|
+
* (`reset`, `loadGame`, `hotSwap`, `closeFlow`, `openFlow` on the name of an open flow, and a flow's own
|
|
597
|
+
* `start`, `reset`, and `restore`) throw. Not undone:
|
|
598
|
+
* trace and log events already emitted, and draws from a custom `rng` the game supplied.
|
|
599
|
+
*/
|
|
600
|
+
checkpoint(): Checkpoint;
|
|
601
|
+
/** Undo everything since `checkpoint` (see there), and close it. */
|
|
602
|
+
rollback(checkpoint: Checkpoint): void;
|
|
603
|
+
/** Keep everything since `checkpoint`, and close it. */
|
|
604
|
+
commit(checkpoint: Checkpoint): void;
|
|
605
|
+
/** True while a checkpoint is open. */
|
|
606
|
+
get inCheckpoint(): boolean;
|
|
607
|
+
private endCheckpoint;
|
|
608
|
+
/** Let go of what a closed checkpoint recorded, so a handle the game keeps holds nothing. */
|
|
609
|
+
private static forget;
|
|
610
|
+
private refuseInCheckpoint;
|
|
551
611
|
/** Close (remove) a flow. The flow object is FINISHED, not merely unregistered, so a host still
|
|
552
612
|
* holding it cannot keep advancing it into the shared world (see {@link Flow.close}). */
|
|
553
613
|
closeFlow(id: string): void;
|
|
@@ -686,6 +746,10 @@ declare class Flow {
|
|
|
686
746
|
* cannot quietly mutate the world. Terminal: unlike ending, a close is never revived.
|
|
687
747
|
*/
|
|
688
748
|
close(): void;
|
|
749
|
+
/** Inside a checkpoint, the first change to this flow records its cursor and PRNG, so a rollback can
|
|
750
|
+
* put them back: a handful of fields and a copy of the call stack (a few frames), never its history.
|
|
751
|
+
* Its visits, selector cursors, and property values are recorded change by change as they happen. */
|
|
752
|
+
private touch;
|
|
689
753
|
/** True once the engine has closed this flow (closed, dropped by `reset()`, or replaced by name). */
|
|
690
754
|
get isClosed(): boolean;
|
|
691
755
|
/** The scene the cursor is currently in - set on entry and whenever a jump crosses scenes. Read
|
|
@@ -739,8 +803,11 @@ declare class Flow {
|
|
|
739
803
|
private writeProperty;
|
|
740
804
|
/** @internal Snapshot this flow's cursor + PRNG + visits. Its properties are the registry's. */
|
|
741
805
|
snapshot(): FlowSnapshot;
|
|
742
|
-
/** @internal Restore this flow from a snapshot.
|
|
806
|
+
/** @internal Restore this flow from a snapshot: its cursor, PRNG, visits, and selector cursors. Its
|
|
807
|
+
* property values are the registry's and stay as they are (a load puts the saved ones there first). */
|
|
743
808
|
restore(snap: FlowSnapshot): void;
|
|
809
|
+
/** The cursor half of `restore`, without touching the bags (a rollback has already put those back). */
|
|
810
|
+
private restoreCursor;
|
|
744
811
|
/** Set the current scene, reset its scene-local props, run onEntry. */
|
|
745
812
|
private enterSceneSetup;
|
|
746
813
|
/**
|
|
@@ -1027,4 +1094,4 @@ declare function effectiveGameData(fields: GameDataField[], node: GameData | und
|
|
|
1027
1094
|
*/
|
|
1028
1095
|
declare function buildTagIndex(bundle: Bundle): Map<string, string[]>;
|
|
1029
1096
|
|
|
1030
|
-
export { type AddressSummary, type AdvanceToStopResult, type BeatInfo, type BundleCounts, type BundleDescription, type BundleIdentity, type ChoiceOption, Engine, type EngineLogEntry, type EngineOptions, type EngineTraceHandler, type FlatBeat, Flow, type GameDataFieldSummary, type GameDataSummary, type HostScopeSummary, type LogEntry, type OpenFlowOptions, type OutlineBlock, type OutlineNode, type OutlineScene, type OwnedProperties, type PropertySummary, type StepResult, type TraceEvent, type TraceHandler, type WorldResolver, buildTagIndex, describeBundle, effectiveGameData, gameDataFields, gameDataValue };
|
|
1097
|
+
export { type AddressSummary, type AdvanceToStopResult, type BeatInfo, type BundleCounts, type BundleDescription, type BundleIdentity, Checkpoint, type ChoiceOption, Engine, type EngineLogEntry, type EngineOptions, type EngineTraceHandler, type FlatBeat, Flow, type GameDataFieldSummary, type GameDataSummary, type HostScopeSummary, type LogEntry, type OpenFlowOptions, type OutlineBlock, type OutlineNode, type OutlineScene, type OwnedProperties, type PropertySummary, type StepResult, type TraceEvent, type TraceHandler, type WorldResolver, buildTagIndex, describeBundle, effectiveGameData, gameDataFields, gameDataValue };
|
package/dist/index.js
CHANGED
|
@@ -55,6 +55,13 @@ var keys = {
|
|
|
55
55
|
/** A flow's NOT-shared `@scene` props for one scene. */
|
|
56
56
|
flowScene: (flowId, sceneId) => `patter/flow/${esc(flowId)}/scene/${esc(sceneId)}`
|
|
57
57
|
};
|
|
58
|
+
var Checkpoint = class {
|
|
59
|
+
/** @internal */
|
|
60
|
+
constructor(journal) {
|
|
61
|
+
this.journal = journal;
|
|
62
|
+
}
|
|
63
|
+
journal;
|
|
64
|
+
};
|
|
58
65
|
var Engine = class _Engine {
|
|
59
66
|
host;
|
|
60
67
|
defaultSeed;
|
|
@@ -172,6 +179,7 @@ var Engine = class _Engine {
|
|
|
172
179
|
sharedVisits: /* @__PURE__ */ new Map(),
|
|
173
180
|
sharedSelectors: /* @__PURE__ */ new Map(),
|
|
174
181
|
stageBags: /* @__PURE__ */ new Map(),
|
|
182
|
+
journal: null,
|
|
175
183
|
customRng: options.rng,
|
|
176
184
|
onDryChoice: options.onDryChoice,
|
|
177
185
|
replayPromptOnChoose: options.replayPromptOnChoose ?? false,
|
|
@@ -235,6 +243,7 @@ var Engine = class _Engine {
|
|
|
235
243
|
* top of the scene it was in; the shared properties carry over.
|
|
236
244
|
*/
|
|
237
245
|
hotSwap(bundle) {
|
|
246
|
+
this.refuseInCheckpoint("hotSwap");
|
|
238
247
|
const snapshot = this.saveGame();
|
|
239
248
|
const carryOver = (next2) => {
|
|
240
249
|
next2.setLocale(this.currentLocale);
|
|
@@ -290,13 +299,28 @@ var Engine = class _Engine {
|
|
|
290
299
|
*
|
|
291
300
|
* Contrast {@link runFlow}, which REUSES a flow of the same name instead of replacing it - that is the
|
|
292
301
|
* call to reach for when you want a speaker's variation state to carry on.
|
|
302
|
+
*
|
|
303
|
+
* THROWS on an address that does not resolve (an unknown scene, or a block that is not in the named
|
|
304
|
+
* scene), before anything changes: nothing is opened, and a flow already open under `id` carries on
|
|
305
|
+
* exactly as it was.
|
|
293
306
|
*/
|
|
294
307
|
openFlow(id, opts = {}) {
|
|
295
308
|
this.assertExternalScopes();
|
|
296
|
-
const sceneId = this.
|
|
297
|
-
const
|
|
298
|
-
this.
|
|
309
|
+
const { sceneId, blockId } = this.resolveOpenAddress(opts.scene, opts.block);
|
|
310
|
+
const prior = this.flowsById.get(id);
|
|
311
|
+
const journal = this.host.journal;
|
|
312
|
+
if (journal && prior && !prior.isClosed) throw new Error(`openFlow would replace the open flow '${id}', which can't be undone while a checkpoint is open`);
|
|
313
|
+
prior?.close();
|
|
299
314
|
const flow = new Flow(id, this.host, opts.seed ?? this.defaultSeed);
|
|
315
|
+
if (journal) {
|
|
316
|
+
journal.flows.add(flow);
|
|
317
|
+
journal.opened.add(flow);
|
|
318
|
+
journal.undo.push(() => {
|
|
319
|
+
flow.close();
|
|
320
|
+
if (prior) this.flowsById.set(id, prior);
|
|
321
|
+
else this.flowsById.delete(id);
|
|
322
|
+
});
|
|
323
|
+
}
|
|
300
324
|
this.flowsById.set(id, flow);
|
|
301
325
|
flow.start(sceneId, blockId);
|
|
302
326
|
return flow;
|
|
@@ -343,6 +367,28 @@ var Engine = class _Engine {
|
|
|
343
367
|
}
|
|
344
368
|
}
|
|
345
369
|
}
|
|
370
|
+
/**
|
|
371
|
+
* `openFlow`'s address as internal ids, or a throw when it does not resolve. With a scene, the block is
|
|
372
|
+
* scene-scoped exactly as {@link Flow.goto} resolves it (a gameId address in that scene, or the internal
|
|
373
|
+
* id of a block in that scene), so a block from another scene does not resolve. With no scene, the block
|
|
374
|
+
* is an internal id from any scene. Neither: the bundle's first scene. The same rule on every runtime.
|
|
375
|
+
*/
|
|
376
|
+
resolveOpenAddress(scene, block) {
|
|
377
|
+
if (scene !== void 0) {
|
|
378
|
+
const sceneId = this.resolveSceneRef(scene);
|
|
379
|
+
if (!this.host.bundle.scenes[sceneId]) throw new Error(`unknown scene: ${scene}`);
|
|
380
|
+
if (block === void 0) return { sceneId };
|
|
381
|
+
const blockId = this.blockGameIdToId.get(sceneId)?.get(block) ?? (this.host.blockIndex.get(block)?.sceneId === sceneId ? block : void 0);
|
|
382
|
+
if (blockId === void 0) throw new Error(`unknown block: ${block}`);
|
|
383
|
+
return { sceneId, blockId };
|
|
384
|
+
}
|
|
385
|
+
if (block !== void 0) {
|
|
386
|
+
if (!this.host.blockIndex.has(block)) throw new Error(`unknown block: ${block}`);
|
|
387
|
+
return { blockId: block };
|
|
388
|
+
}
|
|
389
|
+
if (Object.keys(this.host.bundle.scenes).length === 0) throw new Error("no scenes in bundle");
|
|
390
|
+
return {};
|
|
391
|
+
}
|
|
346
392
|
/** Resolve a scene reference (a gameId address OR an internal id) to its internal id. */
|
|
347
393
|
resolveSceneRef(ref) {
|
|
348
394
|
if (ref == null) return void 0;
|
|
@@ -552,9 +598,64 @@ var Engine = class _Engine {
|
|
|
552
598
|
for (const [sceneId, bag] of this.host.stageBags) mounts.push({ bag, pathPrefix: `@scene:${sceneId}.` });
|
|
553
599
|
return mounts;
|
|
554
600
|
}
|
|
601
|
+
/**
|
|
602
|
+
* Open a checkpoint: from here until `rollback` or `commit`, the engine keeps what it needs to undo
|
|
603
|
+
* every change the story makes. `rollback` puts the game back exactly as it was at the checkpoint:
|
|
604
|
+
* property values (every scope, including a host's `@world`, which is written back through the
|
|
605
|
+
* game), visit counts, shuffle and sequence positions, `@scene` bags made since, every flow's cursor
|
|
606
|
+
* and PRNG, and any flow opened since (closed and forgotten). `commit` keeps everything and stops
|
|
607
|
+
* recording.
|
|
608
|
+
*
|
|
609
|
+
* The use is asking "would this say anything?" without consequences: move a flow to an address,
|
|
610
|
+
* advance it, and roll back if it had nothing to give, so its scene's `onEntry` effects, its visits,
|
|
611
|
+
* and its shuffle draws never happened.
|
|
612
|
+
*
|
|
613
|
+
* One checkpoint at a time. Inside one, the calls that replace or drop flows or their bags wholesale
|
|
614
|
+
* (`reset`, `loadGame`, `hotSwap`, `closeFlow`, `openFlow` on the name of an open flow, and a flow's own
|
|
615
|
+
* `start`, `reset`, and `restore`) throw. Not undone:
|
|
616
|
+
* trace and log events already emitted, and draws from a custom `rng` the game supplied.
|
|
617
|
+
*/
|
|
618
|
+
checkpoint() {
|
|
619
|
+
if (this.host.journal) throw new Error("a checkpoint is already open: roll it back or commit it first");
|
|
620
|
+
const journal = { undo: [], flows: /* @__PURE__ */ new Set(), opened: /* @__PURE__ */ new Set(), selectors: /* @__PURE__ */ new Set(), flowSelectors: /* @__PURE__ */ new Map() };
|
|
621
|
+
this.host.journal = journal;
|
|
622
|
+
return new Checkpoint(journal);
|
|
623
|
+
}
|
|
624
|
+
/** Undo everything since `checkpoint` (see there), and close it. */
|
|
625
|
+
rollback(checkpoint) {
|
|
626
|
+
this.endCheckpoint(checkpoint);
|
|
627
|
+
const undo = checkpoint.journal.undo;
|
|
628
|
+
for (let i = undo.length - 1; i >= 0; i--) undo[i]();
|
|
629
|
+
_Engine.forget(checkpoint.journal);
|
|
630
|
+
}
|
|
631
|
+
/** Keep everything since `checkpoint`, and close it. */
|
|
632
|
+
commit(checkpoint) {
|
|
633
|
+
this.endCheckpoint(checkpoint);
|
|
634
|
+
_Engine.forget(checkpoint.journal);
|
|
635
|
+
}
|
|
636
|
+
/** True while a checkpoint is open. */
|
|
637
|
+
get inCheckpoint() {
|
|
638
|
+
return this.host.journal !== null;
|
|
639
|
+
}
|
|
640
|
+
endCheckpoint(checkpoint) {
|
|
641
|
+
if (this.host.journal !== checkpoint.journal) throw new Error("that checkpoint is not the open one");
|
|
642
|
+
this.host.journal = null;
|
|
643
|
+
}
|
|
644
|
+
/** Let go of what a closed checkpoint recorded, so a handle the game keeps holds nothing. */
|
|
645
|
+
static forget(journal) {
|
|
646
|
+
journal.undo = [];
|
|
647
|
+
journal.flows.clear();
|
|
648
|
+
journal.opened.clear();
|
|
649
|
+
journal.selectors.clear();
|
|
650
|
+
journal.flowSelectors.clear();
|
|
651
|
+
}
|
|
652
|
+
refuseInCheckpoint(what) {
|
|
653
|
+
if (this.host.journal) throw new Error(`${what} can't be undone, so it isn't allowed while a checkpoint is open`);
|
|
654
|
+
}
|
|
555
655
|
/** Close (remove) a flow. The flow object is FINISHED, not merely unregistered, so a host still
|
|
556
656
|
* holding it cannot keep advancing it into the shared world (see {@link Flow.close}). */
|
|
557
657
|
closeFlow(id) {
|
|
658
|
+
this.refuseInCheckpoint("closeFlow");
|
|
558
659
|
this.flowsById.get(id)?.close();
|
|
559
660
|
this.flowsById.delete(id);
|
|
560
661
|
}
|
|
@@ -565,6 +666,7 @@ var Engine = class _Engine {
|
|
|
565
666
|
* After reset, open fresh flows with `openFlow`.
|
|
566
667
|
*/
|
|
567
668
|
reset() {
|
|
669
|
+
this.refuseInCheckpoint("reset");
|
|
568
670
|
for (const flow of this.flowsById.values()) flow.close();
|
|
569
671
|
this.flowsById.clear();
|
|
570
672
|
this.host.patterBag.reseed(this.host.patterSharedDecls);
|
|
@@ -602,7 +704,9 @@ var Engine = class _Engine {
|
|
|
602
704
|
* were each refused the one property they existed to move.) */
|
|
603
705
|
setProperty(ref, value) {
|
|
604
706
|
const { scope, name } = this.splitShared(ref);
|
|
707
|
+
const prev = this.host.journal ? this.host.registry.get(scope, name) : void 0;
|
|
605
708
|
this.host.registry.set(scope, name, value, { host: true });
|
|
709
|
+
if (this.host.journal && prev !== void 0) this.host.journal.undo.push(() => this.host.registry.set(scope, name, prev, { host: true }));
|
|
606
710
|
}
|
|
607
711
|
/** The shared `@patter` properties, for a live state inspector: each with its ref, type, current
|
|
608
712
|
* value, declared default (for reset), and enum options. Mirrors the Unity / Godot ports. */
|
|
@@ -689,6 +793,7 @@ var Engine = class _Engine {
|
|
|
689
793
|
* to the registry (values kept) and the restored flows claim them as they register.
|
|
690
794
|
*/
|
|
691
795
|
loadGame(save) {
|
|
796
|
+
this.refuseInCheckpoint("loadGame");
|
|
692
797
|
const version = save.version;
|
|
693
798
|
if (version !== 2 && version !== SAVE_VERSION) throw new Error(`unsupported save version: ${String(version)}`);
|
|
694
799
|
this.assertExternalScopes();
|
|
@@ -839,6 +944,7 @@ var Flow = class {
|
|
|
839
944
|
// -- Host API -------------------------------------------------------------
|
|
840
945
|
/** Begin this flow at a scene (and optionally a specific block within it). */
|
|
841
946
|
start(sceneId, blockId) {
|
|
947
|
+
if (this.host.journal && !this.host.journal.opened.has(this)) throw new Error("a flow can't be started or reset while a checkpoint is open");
|
|
842
948
|
this.releaseBags(false);
|
|
843
949
|
this.host.registry.discardParked(keys.flow(this.id));
|
|
844
950
|
this.mountLocal();
|
|
@@ -898,6 +1004,7 @@ var Flow = class {
|
|
|
898
1004
|
*/
|
|
899
1005
|
goto(scene, block) {
|
|
900
1006
|
if (this.closed) return false;
|
|
1007
|
+
this.touch();
|
|
901
1008
|
if (scene === "END") {
|
|
902
1009
|
this.started = true;
|
|
903
1010
|
this.pendingChoice = null;
|
|
@@ -951,6 +1058,19 @@ var Flow = class {
|
|
|
951
1058
|
this.pendingPromptBeat = null;
|
|
952
1059
|
this.pendingPromptOwnerId = null;
|
|
953
1060
|
}
|
|
1061
|
+
/** Inside a checkpoint, the first change to this flow records its cursor and PRNG, so a rollback can
|
|
1062
|
+
* put them back: a handful of fields and a copy of the call stack (a few frames), never its history.
|
|
1063
|
+
* Its visits, selector cursors, and property values are recorded change by change as they happen. */
|
|
1064
|
+
touch() {
|
|
1065
|
+
const journal = this.host.journal;
|
|
1066
|
+
if (!journal || journal.flows.has(this)) return;
|
|
1067
|
+
journal.flows.add(this);
|
|
1068
|
+
const { rngState, started, flowEnded, currentSceneId, activeSnippet, beatIndex, pendingChoice, pendingPromptBeat, pendingPromptOwnerId } = this;
|
|
1069
|
+
const stack = this.stack.map((f) => ({ ...f }));
|
|
1070
|
+
journal.undo.push(() => {
|
|
1071
|
+
Object.assign(this, { rngState, started, flowEnded, currentSceneId, activeSnippet, beatIndex, pendingChoice, pendingPromptBeat, pendingPromptOwnerId, stack });
|
|
1072
|
+
});
|
|
1073
|
+
}
|
|
954
1074
|
/** True once the engine has closed this flow (closed, dropped by `reset()`, or replaced by name). */
|
|
955
1075
|
get isClosed() {
|
|
956
1076
|
return this.closed;
|
|
@@ -965,6 +1085,7 @@ var Flow = class {
|
|
|
965
1085
|
advance() {
|
|
966
1086
|
if (this.closed) return { type: "end" };
|
|
967
1087
|
if (!this.started) throw new Error("flow has not been started");
|
|
1088
|
+
this.touch();
|
|
968
1089
|
if (this.pendingPromptBeat) {
|
|
969
1090
|
const b = this.pendingPromptBeat;
|
|
970
1091
|
this.pendingPromptBeat = null;
|
|
@@ -1084,6 +1205,7 @@ var Flow = class {
|
|
|
1084
1205
|
const option = choice.options.find((o) => o.id === id);
|
|
1085
1206
|
if (!option) throw new Error(`unknown choice option: ${id}`);
|
|
1086
1207
|
if (!option.eligible) throw new Error(`choice option is not eligible: ${id}`);
|
|
1208
|
+
this.touch();
|
|
1087
1209
|
const node = choice.byId.get(id);
|
|
1088
1210
|
this.emit({ type: "chose", group: choice.groupId, option: id });
|
|
1089
1211
|
this.pendingChoice = null;
|
|
@@ -1111,13 +1233,27 @@ var Flow = class {
|
|
|
1111
1233
|
* about: the story is refused, the game is not. */
|
|
1112
1234
|
writeProperty(ref, value, host) {
|
|
1113
1235
|
const { scope, name } = this.splitRef(ref);
|
|
1236
|
+
const journal = this.host.journal;
|
|
1114
1237
|
if (scope === "patter") {
|
|
1238
|
+
if (journal) {
|
|
1239
|
+
const shared = this.host.patterSharedNames.has(name);
|
|
1240
|
+
const prev = shared ? this.host.patterBag.get(name) : this.local.get(name);
|
|
1241
|
+
const bag = this.local;
|
|
1242
|
+
if (prev !== void 0) journal.undo.push(shared ? () => this.host.registry.set("patter", name, prev) : () => bag.set(name, prev));
|
|
1243
|
+
}
|
|
1115
1244
|
this.patterResolver.set(name, value);
|
|
1116
1245
|
} else if (scope === "scene") {
|
|
1117
1246
|
if (this.currentSceneId === null) throw new Error(`'${ref}': the flow has not entered a scene yet`);
|
|
1247
|
+
if (journal) {
|
|
1248
|
+
const bag = this.sceneBagFor(this.currentSceneId, name);
|
|
1249
|
+
const prev = bag?.get(name);
|
|
1250
|
+
if (bag && prev !== void 0) journal.undo.push(() => bag.set(name, prev));
|
|
1251
|
+
}
|
|
1118
1252
|
this.sceneResolver.set(name, value);
|
|
1119
1253
|
} else {
|
|
1254
|
+
const prev = journal ? this.host.registry.get(scope, name) : void 0;
|
|
1120
1255
|
this.host.registry.set(scope, name, value, host ? { host: true } : void 0);
|
|
1256
|
+
if (journal && prev !== void 0) journal.undo.push(() => this.host.registry.set(scope, name, prev, { host: true }));
|
|
1121
1257
|
}
|
|
1122
1258
|
}
|
|
1123
1259
|
// -- Save / restore (engine-driven) --------------------------------------
|
|
@@ -1144,8 +1280,19 @@ var Flow = class {
|
|
|
1144
1280
|
}
|
|
1145
1281
|
};
|
|
1146
1282
|
}
|
|
1147
|
-
/** @internal Restore this flow from a snapshot.
|
|
1283
|
+
/** @internal Restore this flow from a snapshot: its cursor, PRNG, visits, and selector cursors. Its
|
|
1284
|
+
* property values are the registry's and stay as they are (a load puts the saved ones there first). */
|
|
1148
1285
|
restore(snap) {
|
|
1286
|
+
if (this.host.journal) throw new Error("a flow can't be restored while a checkpoint is open");
|
|
1287
|
+
this.restoreCursor(snap);
|
|
1288
|
+
this.releaseBags(true);
|
|
1289
|
+
this.mountLocal();
|
|
1290
|
+
for (const s of /* @__PURE__ */ new Set([this.currentSceneId, ...this.stack.map((f) => f.sceneId)])) {
|
|
1291
|
+
if (s !== null && this.host.bundle.scenes[s]) this.ensureSceneBags(s);
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
/** The cursor half of `restore`, without touching the bags (a rollback has already put those back). */
|
|
1295
|
+
restoreCursor(snap) {
|
|
1149
1296
|
this.rngState = toUint32(snap.rngState);
|
|
1150
1297
|
this.visitCounts = new Map(Object.entries(snap.visits ?? {}));
|
|
1151
1298
|
const c = snap.cursor;
|
|
@@ -1161,11 +1308,6 @@ var Flow = class {
|
|
|
1161
1308
|
}
|
|
1162
1309
|
return { ...frame };
|
|
1163
1310
|
});
|
|
1164
|
-
this.releaseBags(false);
|
|
1165
|
-
this.mountLocal();
|
|
1166
|
-
for (const s of /* @__PURE__ */ new Set([c.currentSceneId, ...this.stack.map((f) => f.sceneId)])) {
|
|
1167
|
-
if (s !== null && this.host.bundle.scenes[s]) this.ensureSceneBags(s);
|
|
1168
|
-
}
|
|
1169
1311
|
this.activeSnippet = null;
|
|
1170
1312
|
if (c.activeSnippetId !== null) {
|
|
1171
1313
|
const node = this.host.nodeIndex.get(c.activeSnippetId);
|
|
@@ -1442,6 +1584,24 @@ var Flow = class {
|
|
|
1442
1584
|
/** A selector's cursor state - shared across flows (`group.shared`) or this flow's own. */
|
|
1443
1585
|
selectorState(group) {
|
|
1444
1586
|
const map = group.shared ? this.host.sharedSelectors : this.selectors;
|
|
1587
|
+
const journal = this.host.journal;
|
|
1588
|
+
let seen;
|
|
1589
|
+
if (journal) {
|
|
1590
|
+
seen = group.shared ? journal.selectors : journal.flowSelectors.get(this);
|
|
1591
|
+
if (!seen) {
|
|
1592
|
+
seen = /* @__PURE__ */ new Set();
|
|
1593
|
+
journal.flowSelectors.set(this, seen);
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
if (journal && seen && !seen.has(group.id)) {
|
|
1597
|
+
seen.add(group.id);
|
|
1598
|
+
const was = map.get(group.id);
|
|
1599
|
+
const copy = was ? { ...was, ...was.bag ? { bag: [...was.bag] } : {} } : void 0;
|
|
1600
|
+
journal.undo.push(() => {
|
|
1601
|
+
if (copy) map.set(group.id, copy);
|
|
1602
|
+
else map.delete(group.id);
|
|
1603
|
+
});
|
|
1604
|
+
}
|
|
1445
1605
|
let st = map.get(group.id);
|
|
1446
1606
|
if (!st) {
|
|
1447
1607
|
st = {};
|
|
@@ -1477,8 +1637,18 @@ var Flow = class {
|
|
|
1477
1637
|
}
|
|
1478
1638
|
/** Record an entry of a node (entered-only; spec §7): bumps the flow + world counts. */
|
|
1479
1639
|
enter(id) {
|
|
1480
|
-
|
|
1481
|
-
|
|
1640
|
+
const own = this.visitCounts;
|
|
1641
|
+
const ownBefore = own.get(id);
|
|
1642
|
+
own.set(id, (ownBefore ?? 0) + 1);
|
|
1643
|
+
const shared = this.host.sharedVisits;
|
|
1644
|
+
const before = shared.get(id);
|
|
1645
|
+
shared.set(id, (before ?? 0) + 1);
|
|
1646
|
+
if (this.host.journal) this.host.journal.undo.push(() => {
|
|
1647
|
+
if (ownBefore === void 0) own.delete(id);
|
|
1648
|
+
else own.set(id, ownBefore);
|
|
1649
|
+
if (before === void 0) shared.delete(id);
|
|
1650
|
+
else shared.set(id, before);
|
|
1651
|
+
});
|
|
1482
1652
|
}
|
|
1483
1653
|
/** Next float in [0, 1): the shared custom PRNG, or this flow's serialisable mulberry32.
|
|
1484
1654
|
*
|
|
@@ -1621,17 +1791,29 @@ var Flow = class {
|
|
|
1621
1791
|
ensureSceneBags(s) {
|
|
1622
1792
|
const shared = this.host.sceneSharedNames.get(s) ?? /* @__PURE__ */ new Set();
|
|
1623
1793
|
const props = this.host.bundle.scenes[s]?.sceneProps ?? [];
|
|
1794
|
+
const journal = this.host.journal;
|
|
1624
1795
|
if (!this.sceneBags.has(s)) {
|
|
1625
1796
|
const bag = new PropertyBag(props.filter((d) => !shared.has(d.name.toLowerCase())), { pathPrefix: "@scene." });
|
|
1626
1797
|
const key = keys.flowScene(this.id, s);
|
|
1627
1798
|
this.host.registry.mountOwned(key, bag, { owner: OWNER });
|
|
1628
1799
|
this.registered.add(key);
|
|
1629
1800
|
this.sceneBags.set(s, bag);
|
|
1801
|
+
if (journal) journal.undo.push(() => {
|
|
1802
|
+
if (this.sceneBags.get(s) !== bag) return;
|
|
1803
|
+
this.host.registry.remove(key);
|
|
1804
|
+
this.registered.delete(key);
|
|
1805
|
+
this.sceneBags.delete(s);
|
|
1806
|
+
});
|
|
1630
1807
|
}
|
|
1631
1808
|
if (!this.host.stageBags.has(s)) {
|
|
1632
1809
|
const bag = new PropertyBag(props.filter((d) => shared.has(d.name.toLowerCase())), { pathPrefix: "@scene." });
|
|
1633
1810
|
this.host.registry.mountOwned(keys.stage(s), bag, { owner: OWNER });
|
|
1634
1811
|
this.host.stageBags.set(s, bag);
|
|
1812
|
+
if (journal) journal.undo.push(() => {
|
|
1813
|
+
if (this.host.stageBags.get(s) !== bag) return;
|
|
1814
|
+
this.host.registry.remove(keys.stage(s));
|
|
1815
|
+
this.host.stageBags.delete(s);
|
|
1816
|
+
});
|
|
1635
1817
|
}
|
|
1636
1818
|
}
|
|
1637
1819
|
/**
|
|
@@ -1648,7 +1830,10 @@ var Flow = class {
|
|
|
1648
1830
|
if (!decl.temporary) continue;
|
|
1649
1831
|
const name = decl.name.toLowerCase();
|
|
1650
1832
|
const bag = shared.has(name) ? this.host.stageBags.get(scene.id) : this.sceneBags.get(scene.id);
|
|
1651
|
-
if (bag)
|
|
1833
|
+
if (!bag) continue;
|
|
1834
|
+
const prev = this.host.journal ? bag.get(name) : void 0;
|
|
1835
|
+
if (prev !== void 0) this.host.journal.undo.push(() => bag.set(name, prev));
|
|
1836
|
+
bag.set(name, defaultFor(decl));
|
|
1652
1837
|
}
|
|
1653
1838
|
}
|
|
1654
1839
|
};
|
|
@@ -1847,6 +2032,7 @@ function effectiveGameData(fields, node) {
|
|
|
1847
2032
|
return out;
|
|
1848
2033
|
}
|
|
1849
2034
|
export {
|
|
2035
|
+
Checkpoint,
|
|
1850
2036
|
Engine,
|
|
1851
2037
|
Flow,
|
|
1852
2038
|
buildTagIndex,
|