narraleaf-react 0.13.2 → 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.
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SerializedGameHistory, SerializedGameState } from "../gameTypes";
|
|
1
2
|
import { Action } from "./action";
|
|
2
3
|
import { ActionHistoryManager } from "./actionHistory";
|
|
3
4
|
type GameHistoryAction = {
|
|
@@ -5,7 +6,7 @@ type GameHistoryAction = {
|
|
|
5
6
|
action: Action;
|
|
6
7
|
isPending?: boolean;
|
|
7
8
|
};
|
|
8
|
-
type GameElementHistory = {
|
|
9
|
+
export type GameElementHistory = {
|
|
9
10
|
type: "say";
|
|
10
11
|
text: string;
|
|
11
12
|
voice: string | null;
|
|
@@ -17,6 +18,12 @@ type GameElementHistory = {
|
|
|
17
18
|
};
|
|
18
19
|
export type GameHistory = GameHistoryAction & {
|
|
19
20
|
element: GameElementHistory;
|
|
21
|
+
/**
|
|
22
|
+
* Self-contained core state captured when this line was reached, used to restore the game
|
|
23
|
+
* back to this backlog line (see {@link LiveGame.restoreToHistory}). Optional because a
|
|
24
|
+
* capture can fail, and legacy in-memory entries created before a snapshot was taken.
|
|
25
|
+
*/
|
|
26
|
+
snapshot?: SerializedGameState | null;
|
|
20
27
|
};
|
|
21
28
|
export declare class GameHistoryManager {
|
|
22
29
|
private history;
|
|
@@ -24,7 +31,28 @@ export declare class GameHistoryManager {
|
|
|
24
31
|
constructor(actionHistoryMgr: ActionHistoryManager);
|
|
25
32
|
push(action: GameHistory): this;
|
|
26
33
|
getHistory(): GameHistory[];
|
|
34
|
+
getByToken(token: string): GameHistory | null;
|
|
35
|
+
/**
|
|
36
|
+
* Serialize the whole backlog for persistence (save format v2).
|
|
37
|
+
*/
|
|
38
|
+
serialize(): SerializedGameHistory[];
|
|
39
|
+
/**
|
|
40
|
+
* Serialize the backlog up to and including the entry with the given token.
|
|
41
|
+
*
|
|
42
|
+
* Used by restore-to-history to trim the backlog back to the restored line.
|
|
43
|
+
* Returns an empty array if the token is not found.
|
|
44
|
+
*/
|
|
45
|
+
serializeUntil(token: string): SerializedGameHistory[];
|
|
46
|
+
/**
|
|
47
|
+
* Rebuild the backlog from persisted entries.
|
|
48
|
+
*
|
|
49
|
+
* Each entry is re-bound to a live {@link Action} through `actionMap`; entries whose action no
|
|
50
|
+
* longer exists (the script changed since the save) are dropped rather than throwing, so a
|
|
51
|
+
* still-loadable save degrades to a shorter backlog instead of failing.
|
|
52
|
+
*/
|
|
53
|
+
load(entries: SerializedGameHistory[], actionMap: ReadonlyMap<string, Action>): void;
|
|
27
54
|
reset(): void;
|
|
55
|
+
private static toSerialized;
|
|
28
56
|
updateByToken(id: string, handler: (result: GameHistory | null) => void): void;
|
|
29
57
|
resolvePending(id: string): void;
|
|
30
58
|
private crossFilter;
|
|
@@ -59,6 +59,18 @@ export declare class LiveGame {
|
|
|
59
59
|
* - If the id is not provided, it will undo **the last action**
|
|
60
60
|
*/
|
|
61
61
|
undo(id?: string): void;
|
|
62
|
+
/**
|
|
63
|
+
* Restore the game to a past backlog line.
|
|
64
|
+
*
|
|
65
|
+
* Unlike {@link undo}, this works **after loading a save**: it does not rely on the
|
|
66
|
+
* (non-serializable) undo stack. Every backlog entry carries a self-contained state snapshot,
|
|
67
|
+
* so restoring re-applies that snapshot and trims the backlog back to that line.
|
|
68
|
+
*
|
|
69
|
+
* @param token - the backlog entry token (as returned by {@link getHistory})
|
|
70
|
+
* @returns `true` if the line was restored, `false` if the token is unknown or the entry has
|
|
71
|
+
* no restore snapshot.
|
|
72
|
+
*/
|
|
73
|
+
restoreToHistory(token: string): boolean;
|
|
62
74
|
/**
|
|
63
75
|
* Notify the player with a message
|
|
64
76
|
*
|
|
@@ -74,6 +86,35 @@ export declare class LiveGame {
|
|
|
74
86
|
* Skip the current dialog
|
|
75
87
|
*/
|
|
76
88
|
skipDialog(): void;
|
|
89
|
+
/**
|
|
90
|
+
* Fast-forward playback to the next menu (or the end of the story).
|
|
91
|
+
*
|
|
92
|
+
* Every line in between is executed for real, so the backlog and its restore snapshots
|
|
93
|
+
* accumulate exactly as in normal play — only faster and silent. Audio is muted for the
|
|
94
|
+
* duration, in-flight transitions are settled immediately, and timed pauses (`Control.sleep`,
|
|
95
|
+
* auto-forward) resolve at once. It stops as soon as a menu is waiting for a choice, so the
|
|
96
|
+
* choice itself is always left to the player.
|
|
97
|
+
*
|
|
98
|
+
* Because history accumulates the whole way, {@link getHistory} and
|
|
99
|
+
* {@link restoreToHistory} cover the fast-forwarded span just like normal play.
|
|
100
|
+
*
|
|
101
|
+
* ```typescript
|
|
102
|
+
* // Jump ahead to the next decision point.
|
|
103
|
+
* await game.getLiveGame().fastForward();
|
|
104
|
+
* ```
|
|
105
|
+
*
|
|
106
|
+
* @param options.until - `"menu"` (default) stops at the next menu; `"end"` runs until the
|
|
107
|
+
* story finishes.
|
|
108
|
+
* @param options.maxSteps - safety bound on the number of advance steps (defaults to the
|
|
109
|
+
* `maxStackModelLoop` config).
|
|
110
|
+
* @returns why it stopped: `"menu"`, `"end"` (the stack drained), or `"maxSteps"`.
|
|
111
|
+
*/
|
|
112
|
+
fastForward(options?: {
|
|
113
|
+
until?: "menu" | "end";
|
|
114
|
+
maxSteps?: number;
|
|
115
|
+
}): Promise<{
|
|
116
|
+
reason: "menu" | "end" | "maxSteps";
|
|
117
|
+
}>;
|
|
77
118
|
private assertScreenshot;
|
|
78
119
|
/**
|
|
79
120
|
* Capture the game screenshot, will only include the player element
|
|
@@ -7,8 +7,17 @@ import { PlayerStateData } from "../player/gameState";
|
|
|
7
7
|
import { GuardConfig } from "../player/guard";
|
|
8
8
|
import React from "react";
|
|
9
9
|
import { StackModel, StackModelRawData } from "./action/stackModel";
|
|
10
|
+
import type { GameElementHistory } from "./action/gameHistory";
|
|
10
11
|
import { MenuComponent, NotificationComponent, NvlDialogComponent, SayComponent } from "./common/player";
|
|
11
12
|
import { LiveGameEventToken } from "./types";
|
|
13
|
+
/**
|
|
14
|
+
* Current save format version.
|
|
15
|
+
*
|
|
16
|
+
* - v1 (undefined on the save): core resume state only, no backlog history.
|
|
17
|
+
* - v2: adds `game.history`, a full backlog where every entry carries a self-contained
|
|
18
|
+
* restore snapshot, so loading a save keeps the backlog and any past line can be restored.
|
|
19
|
+
*/
|
|
20
|
+
export declare const SAVE_FORMAT_VERSION = 2;
|
|
12
21
|
export interface SavedGameMetaData {
|
|
13
22
|
/**
|
|
14
23
|
* The timestamp of when the game was created
|
|
@@ -34,21 +43,63 @@ export interface SavedGameMetaData {
|
|
|
34
43
|
* The hash of the story is used to check whether the stories are compatible.
|
|
35
44
|
*/
|
|
36
45
|
storyHash: string;
|
|
46
|
+
/**
|
|
47
|
+
* The save format version (see {@link SAVE_FORMAT_VERSION}).
|
|
48
|
+
*
|
|
49
|
+
* Absent on legacy (v1) saves written before backlog history existed.
|
|
50
|
+
*/
|
|
51
|
+
version?: number;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The core, resumable game state — everything needed to restore the game to a point,
|
|
55
|
+
* **without** the backlog history.
|
|
56
|
+
*
|
|
57
|
+
* This is the unit captured per backlog entry (see {@link SerializedGameHistory}); it is
|
|
58
|
+
* deliberately history-free so that per-entry snapshots do not nest the whole backlog inside
|
|
59
|
+
* themselves (which would make saves grow exponentially).
|
|
60
|
+
*/
|
|
61
|
+
export interface SerializedGameState {
|
|
62
|
+
store: {
|
|
63
|
+
[key: string]: SerializedNamespaceData;
|
|
64
|
+
};
|
|
65
|
+
elementStates: RawData<ElementStateRaw>[];
|
|
66
|
+
stage: PlayerStateData;
|
|
67
|
+
services: {
|
|
68
|
+
[key: string]: unknown;
|
|
69
|
+
};
|
|
70
|
+
stackModel: StackModelRawData;
|
|
71
|
+
asyncStackModels: StackModelRawData[];
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* A single persisted backlog line: the rendered say/menu content, a stable action anchor, and a
|
|
75
|
+
* self-contained snapshot that restores the game to exactly this line.
|
|
76
|
+
*/
|
|
77
|
+
export interface SerializedGameHistory {
|
|
78
|
+
/**
|
|
79
|
+
* Stable action id anchor (`action.getId()`). Used to re-bind the entry to the live story on
|
|
80
|
+
* load; entries whose action no longer exists (script changed) are dropped from the backlog.
|
|
81
|
+
*/
|
|
82
|
+
actionId: string | null;
|
|
83
|
+
element: GameElementHistory;
|
|
84
|
+
isPending?: boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Core game state captured when this line was reached. Restoring it returns the game to this
|
|
87
|
+
* exact line. `null` when a snapshot could not be captured (the line stays visible but is not
|
|
88
|
+
* restorable).
|
|
89
|
+
*/
|
|
90
|
+
snapshot: SerializedGameState | null;
|
|
37
91
|
}
|
|
38
92
|
export interface SavedGame {
|
|
39
93
|
name: string;
|
|
40
94
|
meta: SavedGameMetaData;
|
|
41
|
-
game: {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
};
|
|
50
|
-
stackModel: StackModelRawData;
|
|
51
|
-
asyncStackModels: StackModelRawData[];
|
|
95
|
+
game: SerializedGameState & {
|
|
96
|
+
/**
|
|
97
|
+
* Full backlog with per-entry restore snapshots (save format v2+).
|
|
98
|
+
*
|
|
99
|
+
* Absent on legacy saves; a missing/empty history simply means loading starts with an
|
|
100
|
+
* empty backlog, exactly as before this feature existed.
|
|
101
|
+
*/
|
|
102
|
+
history?: SerializedGameHistory[];
|
|
52
103
|
};
|
|
53
104
|
}
|
|
54
105
|
export type GameConfig = {
|
|
@@ -178,6 +178,7 @@ export declare class GameState {
|
|
|
178
178
|
private stageClickBuffer;
|
|
179
179
|
private readonly nvlAdvanceWaiters;
|
|
180
180
|
private advDialogState;
|
|
181
|
+
private _fastForwarding;
|
|
181
182
|
constructor(game: Game, stage: StageUtils);
|
|
182
183
|
get deps(): number;
|
|
183
184
|
addVideo(video: Video): this;
|