narraleaf-react 1.2.0 → 1.3.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.
@@ -0,0 +1 @@
1
+ export {};
@@ -1,4 +1,4 @@
1
- import { ServiceHandlerCtx } from "../elements/service";
1
+ import { ServiceHandlerCtx, ServiceSkipRequest, ServiceSkipSignal } from "../elements/service";
2
2
  import { Origins } from "../elements/story";
3
3
  import { TransformDefinitions } from "../elements/transform/type";
4
4
  import { IGamePluginRegistry } from "../game/plugin/plugin";
@@ -14,5 +14,5 @@ import { StorableType } from "../elements/persistent/type";
14
14
  import { ScriptCtx } from "../elements/script";
15
15
  import { IStoryConfig } from "../elements/story";
16
16
  export * from "../elements/type";
17
- export type { GameHistory, IGamePluginRegistry, LiveGameEventToken, Origins, ServiceHandlerCtx, TransformDefinitions, GameConfig, SavedGame, NotificationToken, SavedGameMetaData, LayoutRouter, KeyBindingValue, WebKeyboardKey, StorableType, ScriptCtx, IStoryConfig, SoundBusId };
17
+ export type { GameHistory, IGamePluginRegistry, LiveGameEventToken, Origins, ServiceHandlerCtx, ServiceSkipRequest, ServiceSkipSignal, TransformDefinitions, GameConfig, SavedGame, NotificationToken, SavedGameMetaData, LayoutRouter, KeyBindingValue, WebKeyboardKey, StorableType, ScriptCtx, IStoryConfig, SoundBusId };
18
18
  export { KeyBindingType, SoundType, };
@@ -1,18 +1,101 @@
1
1
  import { Actionable } from "../action/actionable";
2
- import { SerializableData, StringKeyOf } from "../../../util/data";
2
+ import { EventToken, SerializableData, StringKeyOf } from "../../../util/data";
3
3
  import { Chained, Proxied } from "../action/chain";
4
4
  import { ServiceAction } from "../action/serviceAction";
5
5
  import { ScriptCtx } from "../elements/script";
6
6
  type ServiceContentType = {
7
7
  [K in string]: any[];
8
8
  };
9
+ /**
10
+ * One request from the player to hurry a running service handler.
11
+ */
12
+ export interface ServiceSkipRequest {
13
+ /**
14
+ * `true` for the skip mode - the skip key held, {@link LiveGame.skipDialog}, and
15
+ * {@link LiveGame.fastForward} - and `false` for a single press of the skip key.
16
+ *
17
+ * The same distinction a line of dialogue draws: a single press reveals a line up to its next
18
+ * pause, the skip mode walks past the pauses too.
19
+ */
20
+ readonly forced: boolean;
21
+ }
22
+ /**
23
+ * The player asking a running handler to hurry, and the handler's answer.
24
+ *
25
+ * A request does not end the handler. The handler decides what being skipped means for it:
26
+ * - **finish now** - put whatever it was doing into its final state and return, in
27
+ * {@link onRequest};
28
+ * - **ignore it** - carry on; the skip mode waits for the handler, and
29
+ * {@link LiveGame.fastForward} waits up to its `stepTimeout` before reporting `"stalled"`.
30
+ * This is what a handler that never looks at `ctx.skip` does;
31
+ * - **refuse** - {@link refuse}: the skip mode still waits, and a fast-forward stops at this step
32
+ * at once and reports `"refused"`.
33
+ *
34
+ * Requests are broadcast: a handler running in a `Control.all` branch or an async branch is asked
35
+ * whenever the player skips anything, the way a sprite's transition running beside a line is.
36
+ *
37
+ * Only an asynchronous handler is ever asked. A synchronous one has already finished by the time
38
+ * the player could ask.
39
+ */
40
+ export interface ServiceSkipSignal {
41
+ /** Whether the player has asked this handler to hurry yet. */
42
+ readonly requested: boolean;
43
+ /** Whether the player has asked it in the skip mode yet. */
44
+ readonly forced: boolean;
45
+ /**
46
+ * Whether the game is in {@link LiveGame.fastForward} right now. A handler that checks it on
47
+ * entry can go straight to its final state.
48
+ */
49
+ readonly fastForwarding: boolean;
50
+ /**
51
+ * Listen for the player asking this handler to hurry.
52
+ *
53
+ * Called once for the first request, and once more the first time a request is
54
+ * {@link ServiceSkipRequest.forced | forced} - not for every repeat, which the skip mode sends
55
+ * many times a second. Registering after a request has already arrived calls the listener at
56
+ * once.
57
+ */
58
+ onRequest(listener: (request: ServiceSkipRequest) => void): EventToken;
59
+ /**
60
+ * Refuse to be fast-forwarded past until the returned function is called.
61
+ *
62
+ * Refusals stack: the step can be skipped again once every one has been released. The message
63
+ * is for people - it is logged, and handed back by {@link LiveGame.fastForward} for a host to
64
+ * show - and nothing in the engine reads it to decide anything.
65
+ */
66
+ refuse(message?: string): () => void;
67
+ }
9
68
  export type ServiceHandlerCtx = ScriptCtx & {
69
+ /**
70
+ * Aborted when this run is cancelled: the player stepped back past it, the story jumped away
71
+ * from it, a `Control.any` it ran in was won by another branch, or the game was reset or loaded.
72
+ *
73
+ * A cancellation cannot be refused, and it does not wait for the handler: by the time the
74
+ * signal fires the game has already moved on. Pass the signal to whatever the handler is
75
+ * waiting on (`fetch(url, {signal})`), and check `signal.aborted` after each `await` before
76
+ * touching the game again.
77
+ */
78
+ signal: AbortSignal;
79
+ /** The player asking this handler to hurry. See {@link ServiceSkipSignal}. */
80
+ skip: ServiceSkipSignal;
81
+ /**
82
+ * Run `handler` when this run is cancelled. Registering after the cancellation runs it at once.
83
+ * @deprecated Use {@link ServiceHandlerCtx.signal}, which fires on the same occasions.
84
+ */
10
85
  onAbort: (handler: () => void) => void;
11
86
  };
12
87
  export type ServiceHandler<Args extends any[]> = (ctx: ServiceHandlerCtx, ...args: Args) => void | Promise<void>;
13
88
  export declare class ServiceSkeleton<Content extends ServiceContentType = ServiceContentType, RawData extends Record<string, SerializableData> | null = never> extends Actionable<RawData, ServiceSkeleton> {
14
89
  /**
15
90
  * Register an action handler.
91
+ *
92
+ * A handler that returns a promise holds the story until the promise settles, the way a line
93
+ * of dialogue does; one that returns anything else lets it carry on at once. If the promise
94
+ * rejects, the story stops at this action and the error is reported.
95
+ *
96
+ * While it holds the story, a handler hears about the player through its context:
97
+ * `ctx.skip` when the player asks it to hurry, which it may answer or refuse, and `ctx.signal`
98
+ * when its run is cancelled, which it may not.
16
99
  * @param key - The action key to handle.
17
100
  * @param handler - Callback invoked when the action fires.
18
101
  * @example
@@ -20,6 +103,25 @@ export declare class ServiceSkeleton<Content extends ServiceContentType = Servic
20
103
  * this.on("add", (ctx, name) => {
21
104
  * console.log("Adding", name);
22
105
  * });
106
+ *
107
+ * this.on("countdown", async (ctx, seconds: number) => {
108
+ * // Skipping finishes the countdown at once.
109
+ * const done = new Promise<void>(resolve => {
110
+ * const timer = setTimeout(resolve, seconds * 1000);
111
+ * ctx.skip.onRequest(() => {
112
+ * clearTimeout(timer);
113
+ * resolve();
114
+ * });
115
+ * ctx.signal.addEventListener("abort", () => clearTimeout(timer));
116
+ * });
117
+ * await done;
118
+ * });
119
+ *
120
+ * this.on("minigame", async (ctx) => {
121
+ * // A fast-forward cannot play the minigame for the player, so it stops here.
122
+ * ctx.skip.refuse("waiting for the minigame");
123
+ * await playMinigame(ctx.signal);
124
+ * });
23
125
  * ```
24
126
  */
25
127
  on<K extends StringKeyOf<Content>>(key: K, handler: ServiceHandler<Content[K]>): this;
@@ -184,7 +184,8 @@ export declare class LiveGame {
184
184
  * Skipping a line is a *request* to the renderer, not a synchronous state change: it is
185
185
  * re-issued until the line settles. A line that never responds (an unskippable in-flight
186
186
  * media/transition step) ends the run with `"stalled"` rather than hanging — this method always
187
- * settles.
187
+ * settles. A step that answers by refusing to be skipped (a user service calling
188
+ * `ctx.skip.refuse()`) ends it at once with `"refused"`, and is left running where it is.
188
189
  *
189
190
  * Because history accumulates the whole way, {@link getHistory} and
190
191
  * {@link restoreToHistory} cover the fast-forwarded span just like normal play.
@@ -207,9 +208,11 @@ export declare class LiveGame {
207
208
  * reports `"stalled"`. Defaults to 10000. Raise it if the story
208
209
  * fast-forwards through long unskippable media.
209
210
  * @returns why it stopped: `"action"` (reached `until.actionId`), `"menu"`, `"end"` (the stack
210
- * drained), `"maxSteps"`, or `"stalled"` (a line refused to settle). When an
211
- * `actionId` target was requested, `reachedTarget` is also set (`true` only for reason
212
- * `"action"`).
211
+ * drained), `"maxSteps"`, `"stalled"` (a line did not settle within `stepTimeout`), or
212
+ * `"refused"` (a line refused to be skipped). When an `actionId` target was requested,
213
+ * `reachedTarget` is also set (`true` only for reason `"action"`). With `"refused"`,
214
+ * `refusal` carries the message the step gave, if it gave one: it is for showing to a
215
+ * person, not for deciding what to do next.
213
216
  *
214
217
  * Note: only the root execution stack is scanned for the target — an id buried inside an
215
218
  * in-flight parallel (`Control.all`/`any`) or async branch is not a stop point.
@@ -221,8 +224,9 @@ export declare class LiveGame {
221
224
  maxSteps?: number;
222
225
  stepTimeout?: number;
223
226
  }): Promise<{
224
- reason: "menu" | "end" | "maxSteps" | "action" | "stalled";
227
+ reason: "menu" | "end" | "maxSteps" | "action" | "stalled" | "refused";
225
228
  reachedTarget?: boolean;
229
+ refusal?: string;
226
230
  }>;
227
231
  private assertScreenshot;
228
232
  /**