narraleaf-react 0.39.0 → 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.
- package/dist/game/nlcore/action/action.d.ts +11 -0
- package/dist/game/nlcore/action/actions/sceneAction.d.ts +14 -0
- package/dist/game/nlcore/action/stackModel.d.ts +37 -0
- package/dist/game/nlcore/common/Utils.d.ts +10 -0
- package/dist/game/player/elements/say/UIDialog.d.ts +15 -0
- package/dist/game/player/elements/scene/dialogPresentation.d.ts +101 -0
- package/dist/game/player/gameState.d.ts +29 -0
- package/dist/game/player/lib/AudioManager.d.ts +12 -0
- package/dist/main.js +44 -48
- package/package.json +115 -115
|
@@ -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;
|
|
@@ -146,6 +146,14 @@ export type PresentationSnapshot = {
|
|
|
146
146
|
export type PlayerStateElementSnapshot = {
|
|
147
147
|
scene: Scene;
|
|
148
148
|
layers: Map<Layer, [LogicAction.DisplayableElements, Record<string, any>][]>;
|
|
149
|
+
/**
|
|
150
|
+
* Whether this scene was a caller parked behind a returnable jump when the snapshot was taken.
|
|
151
|
+
*
|
|
152
|
+
* A snapshot is restored by rebuilding the element, so anything the element carries and the
|
|
153
|
+
* snapshot does not is silently dropped on the way back. This one is the difference between a
|
|
154
|
+
* scene that is on the stage and one that is on the stage waiting for a call to return.
|
|
155
|
+
*/
|
|
156
|
+
suspended?: boolean;
|
|
149
157
|
};
|
|
150
158
|
export type PlayerAction = CalledActionResult;
|
|
151
159
|
interface StageUtils {
|
|
@@ -229,6 +237,27 @@ export declare class GameState {
|
|
|
229
237
|
addScene(scene: Scene): this;
|
|
230
238
|
flush(): this;
|
|
231
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;
|
|
232
261
|
removeScene(scene: Scene): this;
|
|
233
262
|
getSceneElements(): PlayerStateElement[];
|
|
234
263
|
/**
|
|
@@ -154,6 +154,18 @@ export declare class AudioManager {
|
|
|
154
154
|
stop(sound: SoundElement, duration?: number): Awaitable<void>;
|
|
155
155
|
setVolume(sound: SoundElement, volume: number, duration?: number): Awaitable<void>;
|
|
156
156
|
mute(sound: SoundElement, muted?: boolean): Awaitable<void>;
|
|
157
|
+
/**
|
|
158
|
+
* Arm a transport change that will only act once a fade has finished, and hand back the test
|
|
159
|
+
* for whether it is still wanted by then.
|
|
160
|
+
*
|
|
161
|
+
* `FadeToken.finished` resolves when the fade is **cancelled** as well as when it runs out, and
|
|
162
|
+
* every later transport call on the same token cancels it - `resume` writes a volume, which is
|
|
163
|
+
* what cancels a pause's fade-out. Without this test, a scene call that returned while the
|
|
164
|
+
* caller's pause fade was still in flight landed the pause *after* the resume, with nothing
|
|
165
|
+
* left to undo it: the caller's music stayed silent for the rest of the scene while
|
|
166
|
+
* `sound.state.paused` said it was playing, so the next save recorded a stopped clip.
|
|
167
|
+
*/
|
|
168
|
+
private arm;
|
|
157
169
|
pause(sound: SoundElement, duration?: number): Awaitable<void>;
|
|
158
170
|
resume(sound: SoundElement, duration?: number): Awaitable<void>;
|
|
159
171
|
/**
|