narraleaf-react 0.39.1 → 0.39.2

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.
@@ -22,6 +22,17 @@ export declare class Action<ContentNodeType = any, Callee = LogicAction.GameElem
22
22
  readonly __stack: string;
23
23
  constructor(callee: Callee, type: Type, contentNode: ContentNode<ContentNodeType>);
24
24
  executeAction(_state: GameState, _injection: ActionExecutionInjection): ExecutedActionResult;
25
+ /**
26
+ * Give up whatever this action was still holding, without running it and without going on to
27
+ * the next one.
28
+ *
29
+ * Called on an action that is dropped from an execution stack rather than executed - today
30
+ * only when a concurrent branch is abandoned (see {@link StackModel.abandon}). Almost every
31
+ * action holds nothing outside its own stack and so does nothing here; the exception is a
32
+ * scene call's return address, which is a promise made to a scene that is sitting suspended on
33
+ * the stage waiting for it.
34
+ */
35
+ abandon(_state: GameState): void;
25
36
  getId(): string;
26
37
  setId(id: string): void;
27
38
  getStaticId(): string | null;
@@ -84,10 +84,24 @@ export declare class SceneAction<T extends typeof SceneActionTypes[keyof typeof
84
84
  /** Put back what {@link SceneAction.unwindCallStack} took away, in the order it took it. */
85
85
  static rewindCallStack(unwound: [scene: Scene, snapshot: SceneSnapshot][], state: GameState): void;
86
86
  exit(state: GameState): void;
87
+ /**
88
+ * Give up the call this return address stands for.
89
+ *
90
+ * Reached when the stack holding a `scene:resume` is thrown away rather than run to it - a
91
+ * `Control.any` branch that lost, today. The stage half of `scene:resume` and nothing else: the
92
+ * scene the call entered leaves, and the scene the call had suspended is running again. What is
93
+ * deliberately missing is the rest of it - a return address nobody is coming back to has no
94
+ * next action, and no history entry either, because the branch it was on is not a place a step
95
+ * back can land.
96
+ *
97
+ * Every other scene action holds nothing once it is off the stack, so this is the only override.
98
+ */
99
+ abandon(state: GameState): void;
87
100
  applyNvlVisibility(gameState: GameState, visible: boolean, options: Partial<TransformDefinitions.CommonTransformProps> | undefined, injection: ActionExecutionInjection): CalledActionResult | Awaitable<CalledActionResult, CalledActionResult>;
88
101
  executeAction(gameState: GameState, injection: ActionExecutionInjection): ExecutedActionResult;
89
102
  getFutureActions(story: Story, searchOptions?: ActionSearchOptions): LogicAction.Actions[];
90
103
  _sceneNotFoundError(sceneId: string): Error;
104
+ _callerAlreadyParkedError(target: Scene | string): Error;
91
105
  getSceneName(scene: Scene | string): string;
92
106
  stringify(story: Story, seen: Set<LogicAction.Actions>, _strict: boolean): string;
93
107
  }
@@ -114,6 +114,14 @@ export declare class StackModel {
114
114
  static isStackModelsAwaiting(type: StackModelWaiting["type"], stackModels: StackModel[]): boolean;
115
115
  private stack;
116
116
  private waitingAction;
117
+ /**
118
+ * True only for as long as {@link executeActions} is running {@link waitingAction}.
119
+ *
120
+ * `waitingAction` is the last action popped off the stack, and it stays set after that action
121
+ * has run to completion - so on its own it cannot say whether the action is still the one the
122
+ * model is on. {@link serialize} needs that distinction and nothing else does.
123
+ */
124
+ private executingWaitingAction;
117
125
  private loopConfig;
118
126
  private loopBodyActions;
119
127
  private loopCondition;
@@ -192,9 +200,38 @@ export declare class StackModel {
192
200
  * @returns Snapshot that can be passed to StackModel.deserialize.
193
201
  */
194
202
  serialize(frozen?: boolean): StackModelRawData;
203
+ /**
204
+ * Give up everything this stack still holds, unwinding the scene calls it opened on the way.
205
+ *
206
+ * {@link reset} clears stacks; it does not put the stage back. A branch cut mid-call is holding
207
+ * a `scene:resume` - the promise to come back to the scene it suspended - and dropping that
208
+ * promise without keeping it leaves the caller parked on the stage with nothing able to return
209
+ * to it, and the called scene mounted with nothing pointing at it. Both are permanent: no later
210
+ * action names either scene. So the call is given up, innermost first, which is what
211
+ * `SceneAction.unwindCallStack` does when a plain jump walks away from a call stack.
212
+ *
213
+ * Only the call frames are unwound. A scene the branch merely *entered* - what a plain jump
214
+ * does - belongs to the main stack from the moment the jump re-pointed it at that scene, so it
215
+ * is the story's scene by then rather than the branch's, and taking it off the stage here would
216
+ * unload the scene the story is now in.
217
+ */
218
+ abandon(): this;
219
+ /**
220
+ * Every scene-call return address this stack is holding, innermost first.
221
+ *
222
+ * Nested groups are searched too: a branch can itself be running a `Control.all` whose own
223
+ * branch opened a call, and that call is held just as firmly.
224
+ */
225
+ private collectCallFrames;
195
226
  reset(): void;
196
227
  deserialize(data: StackModelRawData, actionMap: Map<string, LogicAction.Actions>): this;
197
228
  isEmpty(): boolean;
229
+ /**
230
+ * Whether the top of the stack is a `Control.all` / `Control.any` link whose branches have not
231
+ * all drained - the one state in which the stack accepts nothing on top of it, because the
232
+ * group has to finish before whatever queued behind it can run.
233
+ */
234
+ private topIsGroupStillRunning;
198
235
  /**
199
236
  * Drop everything above the innermost scene-call return address, or the whole stack if there
200
237
  * is none.
@@ -11,6 +11,16 @@ export declare class RGBColor {
11
11
  toString(): string;
12
12
  toHex(): string;
13
13
  }
14
+ /**
15
+ * The action a {@link RuntimeScriptError} is about, reduced to what a host can put on screen:
16
+ * which action it was, and what kind of action it was.
17
+ */
18
+ export type RuntimeScriptErrorAction = {
19
+ /** The action's id, as {@link Action.getId} reports it. */
20
+ id: string;
21
+ /** The action's type, e.g. `scene:preSuspend`. */
22
+ type: string;
23
+ };
14
24
  /**
15
25
  * Alias for {@link Word.color}
16
26
  */
@@ -50,6 +50,21 @@ export declare class DialogState {
50
50
  isIdle(): boolean;
51
51
  setIdle(idle: boolean): void;
52
52
  isActive(): boolean;
53
+ /**
54
+ * Hand the line to a box, or take it away from one.
55
+ *
56
+ * A box that becomes active over a line whose text has already finished revealing is idle at
57
+ * once. `_idle` is the latch between "there is nothing left to reveal" and "the next advance
58
+ * settles the line", and the `complete` event that used to be its only source is answered only
59
+ * by a box that was active at the instant the text finished. A line that finished while its box
60
+ * was displaced - another scene's dialog on top of it, a panel over the stage, the moment of
61
+ * retention after the line before it - therefore came back with the latch down, and the first
62
+ * advance the player spent went on raising it instead of settling the line. The latch now
63
+ * follows the fact rather than who was watching when it happened.
64
+ *
65
+ * A line that has *not* finished revealing is untouched: its box coming back must still reveal
66
+ * the rest of it before a click can settle it.
67
+ */
53
68
  setActive(active: boolean): this;
54
69
  /**
55
70
  * Only for dialog component to call
@@ -0,0 +1,101 @@
1
+ import { PlayerStateElement } from "../../gameState";
2
+ import { DialogAction } from "../say/type";
3
+ /**
4
+ * How long a dialog box is kept on screen after the line in it has been settled.
5
+ *
6
+ * A line is usually replaced by the next one within the same tick, and the grace is what lets that
7
+ * happen in the same box rather than as an exit followed by an entrance. It is a hold for a line
8
+ * that is *finished*; see {@link resolveDialogPresentation} for why a line that is still waiting can
9
+ * never be held this way.
10
+ */
11
+ export declare const DIRECT_DIALOG_REPLACEMENT_GRACE_MS = 120;
12
+ /**
13
+ * The presence bookkeeping one scene's dialog layer carries between renders.
14
+ *
15
+ * `slotKeys` maps a slot - the index a line has in the scene's `texts` - to the React key of the
16
+ * box showing it, so a line replaced in place keeps its box. `exitingKeys` are the keys
17
+ * `AnimatePresence` is still animating out; a slot that is handed one of those has to be given a
18
+ * fresh key instead, or the new line would be mounted into a box that is on its way off screen.
19
+ */
20
+ export type DialogPresenceState = {
21
+ slotKeys: Map<number, string>;
22
+ exitingKeys: Set<string>;
23
+ menuPromptIds: WeakMap<PlayerStateElement["menus"][number], string>;
24
+ nextKey: number;
25
+ };
26
+ export type DialogRenderItem = {
27
+ action: DialogAction;
28
+ onFinished?: (skiped?: boolean) => void;
29
+ useTypeEffect: boolean;
30
+ presenceKey: string;
31
+ slot: number;
32
+ /**
33
+ * Whether this box is showing a line that is still waiting for the player.
34
+ *
35
+ * An inactive box ignores clicks, the advance key and auto-forward: it is a picture of a line
36
+ * that is already over. Only a retained item is ever inactive.
37
+ */
38
+ active: boolean;
39
+ };
40
+ /** One line (or one menu prompt) the scene wants a box for, before a box has been assigned. */
41
+ export type DialogSource = {
42
+ action: DialogAction;
43
+ onFinished?: (skiped?: boolean) => void;
44
+ useTypeEffect: boolean;
45
+ slot: number;
46
+ };
47
+ export type DialogPresentationInput = {
48
+ /**
49
+ * Every line the scene is still waiting on, in slot order - or, when it is waiting on none, the
50
+ * prompt of a menu it is showing. Empty means the scene has nothing of its own to say.
51
+ */
52
+ sources: DialogSource[];
53
+ /** How many menus the scene is showing. A menu is interactive whether or not it has a prompt. */
54
+ menuCount: number;
55
+ /** Mutated: the slot/key bookkeeping this layer carries between renders. */
56
+ presence: DialogPresenceState;
57
+ /** The snapshot currently being held on screen, if the grace is running. */
58
+ retained: DialogRenderItem[] | null;
59
+ /** The items the last render with something to say produced. */
60
+ lastActive: DialogRenderItem[];
61
+ sceneId: string;
62
+ };
63
+ export type DialogPresentation = {
64
+ /** What to render, in order. */
65
+ items: DialogRenderItem[];
66
+ /** The snapshot to keep holding, or null to stop holding one. */
67
+ retained: DialogRenderItem[] | null;
68
+ /** The snapshot to remember as the last live one. */
69
+ lastActive: DialogRenderItem[];
70
+ /** Whether the retention grace should be counting after this render. */
71
+ retaining: boolean;
72
+ /**
73
+ * Whether this scene's dialog layer should take pointer events.
74
+ *
75
+ * A scene's dialog layer covers the whole stage, and every scene on the stage has one -
76
+ * including a caller parked behind a returnable jump, which has nothing to show at all. The
77
+ * layers are stacked in the order the scenes are held, so a parked caller's empty layer is
78
+ * drawn over the box of the scene the story is actually in. A layer with nothing live in it
79
+ * that still took pointer events therefore swallowed every click aimed at the line underneath:
80
+ * the box's own click handler, an inline word's, anything a line had drawn over itself.
81
+ */
82
+ interactive: boolean;
83
+ };
84
+ /**
85
+ * Decide what one scene's dialog layer renders this frame.
86
+ *
87
+ * Deterministic given its input; it starts no timers and touches no React. `presence` is the one
88
+ * thing it writes to, because slot/key assignment is bookkeeping that has to survive the render
89
+ * that made it.
90
+ *
91
+ * Two rules the layer is built on:
92
+ *
93
+ * - **A line that is still waiting is always live.** Retention describes a line that is over and is
94
+ * being held on screen for a moment; a line whose click callback has not been called yet is not
95
+ * that, whatever came and went over its box in the meantime. So a snapshot is only ever taken
96
+ * when the scene has nothing left to say, and any snapshot in hand is dropped the moment it has
97
+ * something again.
98
+ * - **A layer only takes the pointer when it has something to take it for.** See
99
+ * {@link DialogPresentation.interactive}.
100
+ */
101
+ export declare function resolveDialogPresentation({ sources, menuCount, presence, retained, lastActive, sceneId, }: DialogPresentationInput): DialogPresentation;
@@ -237,6 +237,27 @@ export declare class GameState {
237
237
  addScene(scene: Scene): this;
238
238
  flush(): this;
239
239
  popScene(): this;
240
+ /**
241
+ * Advance every line still waiting for a click on `scene`, because the scene is leaving.
242
+ *
243
+ * A pending line lives as a `Clickable` in the scene's own stage entry, and the click callback
244
+ * in it is the only thing that settles the action waiting on it. Taking the entry away used to
245
+ * drop the line with it: nothing rendered the line any more, so no click could reach it, and
246
+ * whatever was waiting on it waited for ever.
247
+ *
248
+ * On the main stack that never showed, because every path that unloads a scene also clears the
249
+ * stack it would have blocked. A concurrent branch has a stack of its own and `Control.all`
250
+ * waits for every branch, so a single line left behind by a returning scene call stopped the
251
+ * story with no error and every click inert.
252
+ *
253
+ * Advanced rather than abandoned, because advancing is the only handle a pending line offers -
254
+ * its click callback is what settles it. The line is over either way, since it is no longer on
255
+ * screen; this way the branch that queued it carries on instead of stopping where it stood.
256
+ *
257
+ * Menus are deliberately not touched: choosing on the player's behalf would decide the story,
258
+ * which is worse than any of this.
259
+ */
260
+ settlePendingLines(scene: Scene): this;
240
261
  removeScene(scene: Scene): this;
241
262
  getSceneElements(): PlayerStateElement[];
242
263
  /**