narraleaf-react 0.13.2 → 0.15.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.
@@ -150,5 +150,5 @@ export declare const VideoActionTypes: {
150
150
  readonly seek: "video:seek";
151
151
  };
152
152
  export type VideoActionContentType = {
153
- [K in typeof VideoActionTypes[keyof typeof VideoActionTypes]]: K extends "video:action" ? any : K extends "video:show" | "video:hide" | "video:play" | "video:pause" | "video:stop" ? [] : K extends "video:seek" ? [number] : any;
153
+ [K in typeof VideoActionTypes[keyof typeof VideoActionTypes]]: K extends "video:action" ? any : K extends "video:show" | "video:hide" | "video:play" | "video:pause" | "video:stop" | "video:resume" ? [] : K extends "video:seek" ? [number] : any;
154
154
  };
@@ -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;
@@ -32,4 +32,30 @@ export declare class Video extends Actionable<VideoStateRaw> {
32
32
  * ```
33
33
  */
34
34
  play(): ChainedVideo;
35
+ /**
36
+ * Pause the video, keeping its current position.
37
+ * @chainable
38
+ */
39
+ pause(): ChainedVideo;
40
+ /**
41
+ * Resume playback from the current position.
42
+ *
43
+ * Unlike {@link play}, this does not wait for the video to finish.
44
+ * @chainable
45
+ */
46
+ resume(): ChainedVideo;
47
+ /**
48
+ * Stop the video: pause it and end any pending {@link play} so the story continues.
49
+ * @chainable
50
+ */
51
+ stop(): ChainedVideo;
52
+ /**
53
+ * Seek to a specific time (in seconds).
54
+ * @chainable
55
+ * @example
56
+ * ```ts
57
+ * video.seek(3);
58
+ * ```
59
+ */
60
+ seek(time: number): ChainedVideo;
35
61
  }
@@ -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
- store: {
43
- [key: string]: SerializedNamespaceData;
44
- };
45
- elementStates: RawData<ElementStateRaw>[];
46
- stage: PlayerStateData;
47
- services: {
48
- [key: string]: unknown;
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;