narraleaf-react 1.0.1 → 1.1.1

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.
@@ -16,6 +16,7 @@ import type { Transition } from "../elements/transition/transition";
16
16
  import type { ImageTransition } from "../elements/transition/transitions/image/imageTransition";
17
17
  import type { Layer } from "../elements/layer";
18
18
  import type { VfxFadeOptions } from "../elements/vfx";
19
+ import type { VideoFadeOptions } from "../elements/video";
19
20
  import type { PuppetCommandOptions } from "../elements/displayable/puppet";
20
21
  export declare const DisplayableActionTypes: {
21
22
  readonly action: "displayable:action";
@@ -162,7 +163,7 @@ export declare const VideoActionTypes: {
162
163
  readonly seek: "video:seek";
163
164
  };
164
165
  export type VideoActionContentType = {
165
- [K in typeof VideoActionTypes[keyof typeof VideoActionTypes]]: K extends "video:action" ? any : K extends "video:preload" | "video:show" | "video:hide" | "video:play" | "video:pause" | "video:stop" | "video:resume" ? [] : K extends "video:seek" ? [number] : any;
166
+ [K in typeof VideoActionTypes[keyof typeof VideoActionTypes]]: K extends "video:action" ? any : K extends "video:show" | "video:hide" ? [VideoFadeOptions?] : K extends "video:preload" | "video:play" | "video:pause" | "video:stop" | "video:resume" ? [] : K extends "video:seek" ? [number] : any;
166
167
  };
167
168
  export declare const VfxActionTypes: {
168
169
  readonly action: "vfx:action";
@@ -20,7 +20,28 @@ export declare class VideoAction<T extends Values<typeof VideoActionTypes> = Val
20
20
  readonly seek: "video:seek";
21
21
  };
22
22
  executeAction(gameState: GameState, injection: ActionExecutionInjection): Awaitable<CalledActionResult>;
23
+ /**
24
+ * A transport row - pause, resume, seek, stop - aimed at a clip that is no longer on the stage.
25
+ *
26
+ * There is nothing to talk to, and nothing it could have done: a clip that has left has stopped,
27
+ * and the next show puts it back from the start. Which side of the clip leaving such a row lands
28
+ * on is not always the author's to decide - a clip played without waiting for it leaves when it
29
+ * ends, at a moment set by how fast the player reads - so a row that would have worked a click
30
+ * earlier must not stop the story a click later. The same answer `hide` gives.
31
+ */
32
+ private skipOffStage;
23
33
  private changeStateBase;
34
+ /**
35
+ * {@link changeStateBase} for a fade, which the player can walk away from halfway.
36
+ *
37
+ * Aborting the action - stepping back, loading a save, starting a new game - abandons the fade
38
+ * where it stands, and the element goes back to showing what `video.state.display` says: the undo
39
+ * that follows the abort then decides what that is. The handler is told through `signal`, so the
40
+ * part of it that records the change never runs for a change that did not happen.
41
+ */
42
+ private changeStateFading;
43
+ /** Whether a show or hide was asked to fade, rather than to happen at once. */
44
+ private static fades;
24
45
  private changeState;
25
46
  private changeStateAsync;
26
47
  stringify(_story: Story, _seen: Set<LogicAction.Actions>, _strict: boolean): string;
@@ -23,6 +23,7 @@ import { Puppet } from "../elements/displayable/puppet";
23
23
  import { NVLToken } from "../elements/nvl";
24
24
  export { Character, Narrator, Condition, Control, Image, Lambda, Menu, NVLToken, Scene, Script, Sentence, Sound, Story, Transform, Word, Text, Pause, TextEvent, Persistent, Service, Layer, Camera, Video, Vfx, Puppet, };
25
25
  export type { VfxConfig, VfxBlendMode, VfxFadeOptions } from "../elements/vfx";
26
+ export type { VideoFadeOptions } from "../elements/video";
26
27
  export type { IPuppetUserConfig, PuppetConfig, PuppetCommandOptions, } from "../elements/displayable/puppet";
27
28
  export type { PuppetBackend, PuppetDescription, PuppetInstance, PuppetMountContext, PuppetSize, PuppetState, PuppetStatus, } from "../game/puppet/puppetBackend";
28
29
  export type { LayeredDefinition, LayerGroupDefinition, LayerResolver, LayerSlot, LayerTagsOf, LayerVariants, } from "../elements/displayable/image";
@@ -1,10 +1,27 @@
1
1
  import { Actionable } from "../action/actionable";
2
2
  import { Chained, Proxied } from "../action/chain";
3
3
  import { LogicAction } from "../game";
4
+ import type { TransformDefinitions } from "../elements/transform/type";
4
5
  export type VideoConfig = {
5
6
  src: string;
6
7
  muted: boolean;
7
8
  };
9
+ /**
10
+ * How {@link Video.show} or {@link Video.hide} fades the clip in or out.
11
+ *
12
+ * Omitted, or with no positive `duration`, the clip appears or disappears at once - which is what
13
+ * `show()` and `hide()` have always done.
14
+ */
15
+ export type VideoFadeOptions = {
16
+ /** How long the fade takes, in milliseconds. */
17
+ duration?: number;
18
+ /**
19
+ * The curve of the fade: a named easing (`"linear"`, `"easeIn"`, `"easeOut"`, `"easeInOut"`) or a
20
+ * cubic-bezier as four numbers. Linear when omitted. A named easing with no CSS equivalent falls
21
+ * back to `"ease"`, as it does for a `Vfx` fade.
22
+ */
23
+ easing?: TransformDefinitions.EasingDefinition;
24
+ };
8
25
  export type VideoState = {
9
26
  display: boolean;
10
27
  };
@@ -33,15 +50,38 @@ export declare class Video extends Actionable<VideoStateRaw> {
33
50
  */
34
51
  preload(): Proxied<Video, Chained<LogicAction.Actions>>;
35
52
  /**
36
- * Show the video element.
53
+ * Show the video element, putting it on the stage first if it is not there yet.
54
+ *
55
+ * With `options.duration`, the clip fades in over that many milliseconds and the action waits for
56
+ * the fade to finish; it starts once the clip can present a frame, so what fades in is the picture
57
+ * and not an empty rectangle. Without it the clip appears at once.
37
58
  * @chainable
59
+ * @example
60
+ * ```ts
61
+ * video.show({duration: 500});
62
+ * ```
38
63
  */
39
- show(): Proxied<Video, Chained<LogicAction.Actions>>;
64
+ show(options?: VideoFadeOptions): Proxied<Video, Chained<LogicAction.Actions>>;
40
65
  /**
41
- * Hide the video element.
66
+ * Hide the video element and take it off the stage.
67
+ *
68
+ * With `options.duration`, the clip fades out over that many milliseconds - holding whatever frame
69
+ * it is on, which after {@link play} is its last - and leaves the stage once the fade is over; the
70
+ * action waits for that. Without it the clip disappears at once.
71
+ *
72
+ * A clip that is not on the stage has nothing to hide, and the call does nothing.
42
73
  * @chainable
74
+ * @example
75
+ * ```ts
76
+ * // A cutscene that clears itself away when it ends
77
+ * scene.action([
78
+ * video.show(),
79
+ * video.play(),
80
+ * video.hide({duration: 600}),
81
+ * ]);
82
+ * ```
43
83
  */
44
- hide(): Proxied<Video, Chained<LogicAction.Actions>>;
84
+ hide(options?: VideoFadeOptions): Proxied<Video, Chained<LogicAction.Actions>>;
45
85
  /**
46
86
  * Play the video and wait until it finishes.
47
87
  * @chainable
@@ -133,6 +133,14 @@ export type PlayerStateData = {
133
133
  * { [layerName]: [displayableId][] }
134
134
  */
135
135
  layers: Record<string, string[]>;
136
+ /**
137
+ * The ids of the clips this scene put on the stage. Their state is in
138
+ * {@link PlayerStateData.videos}; this says which scene each one leaves with.
139
+ *
140
+ * Written only when the scene has any. Absent in saves written before clips belonged to
141
+ * scenes, and a clip no scene names is given to the scene the story is running in.
142
+ */
143
+ videos?: string[];
136
144
  };
137
145
  }[];
138
146
  audio: AudioManagerDataRaw;
@@ -156,6 +164,14 @@ export type PlayerStateElementSnapshot = {
156
164
  * scene that is on the stage and one that is on the stage waiting for a call to return.
157
165
  */
158
166
  suspended?: boolean;
167
+ /**
168
+ * The clips this scene had on the stage, each with its state, in stage order.
169
+ *
170
+ * A clip is drawn over the scene rather than inside one of its layers, but it belongs to the
171
+ * scene that put it there all the same, and leaves with it. Absent in snapshots taken before
172
+ * clips belonged to scenes; read as none.
173
+ */
174
+ videos?: [Video, VideoStateRaw][];
159
175
  };
160
176
  export type PlayerAction = CalledActionResult;
161
177
  interface StageUtils {
@@ -210,6 +226,16 @@ export declare class GameState {
210
226
  * See {@link VideoWarmQueue}.
211
227
  */
212
228
  private readonly videoWarmQueue;
229
+ /**
230
+ * Which scene each of the story's clips belongs to: the one running when the clip went on.
231
+ *
232
+ * A clip is not on one of the scene's layers - it is a sibling of the scene's root, so that a clip
233
+ * the preloader warmed can be taken over without being remounted - so nothing about where it sits
234
+ * says whose it is. And it has to be somebody's, because a scene takes everything it put on the
235
+ * stage with it when it leaves. Kept beside {@link PlayerState.videos} rather than in it, because
236
+ * the stage order of clips is not grouped by scene.
237
+ */
238
+ private videoOwners;
213
239
  private videoMissingReporter;
214
240
  /** Sources already reported as unwarmed, so one clip in a loop does not report every pass. */
215
241
  private readonly reportedUnwarmedVideos;
@@ -547,6 +573,21 @@ export declare class GameState {
547
573
  * excluded explicitly rather than by trusting that route to stay closed.
548
574
  */
549
575
  private resetLayers;
576
+ /**
577
+ * The clip half of leaving a scene: every clip the scene put on the stage leaves with it, and
578
+ * goes back to its authored state, exactly as the sprites on its layers do in
579
+ * {@link resetLayers}.
580
+ *
581
+ * Without this a clip outlived its scene, and since a clip paints over its scene's sprites, one
582
+ * that stopped on its last frame covered the whole of the next scene - where nothing could take it
583
+ * down, because a host that names stage objects per scene cannot even name it.
584
+ *
585
+ * Every path that takes a scene off the stage comes through here - a plain jump's exit, a call
586
+ * returning, a jump giving up the callers parked behind it, stepping back over a scene's entrance
587
+ * - and each of them snapshots the scene first (see {@link createElementSnapshot}), so the clips
588
+ * come back with it when the step is undone.
589
+ */
590
+ private releaseVideosOf;
550
591
  private syncNvlDerivedState;
551
592
  private emitNvlStateChange;
552
593
  private resolveNvlAdvance;
@@ -0,0 +1 @@
1
+ export {};
@@ -11,7 +11,7 @@ import { Text } from "../nlcore/elements/displayable/text";
11
11
  import { Displayable } from "../nlcore/elements/displayable/displayable";
12
12
  import { Scene } from "../nlcore/elements/scene";
13
13
  import { Sound } from "../nlcore/elements/sound";
14
- import { Video } from "../nlcore/elements/video";
14
+ import { Video, VideoFadeOptions } from "../nlcore/elements/video";
15
15
  import { Vfx, VfxFadeOptions } from "../nlcore/elements/vfx";
16
16
  import { Puppet } from "../nlcore/elements/displayable/puppet";
17
17
  import { Timeline } from "./Tasks";
@@ -80,8 +80,12 @@ export type ExposedState = {
80
80
  setBackgroundMusic: (music: Sound | null, fade: number) => Promise<void>;
81
81
  };
82
82
  [ExposedStateType.video]: {
83
- show: () => void;
84
- hide: () => void;
83
+ /** Resolves once the clip is showing: at once, or when a fade in ends, is skipped or is abandoned. */
84
+ show: (options?: VideoFadeOptions) => Promise<void>;
85
+ /** Resolves once the clip is hidden: at once, or when a fade out ends, is skipped or is abandoned. */
86
+ hide: (options?: VideoFadeOptions) => Promise<void>;
87
+ /** Abandon a fade in flight and show the clip as `video.state.display` says. */
88
+ cancelFade: () => void;
85
89
  play: () => Promise<void>;
86
90
  pause: () => void;
87
91
  resume: () => Promise<void>;