@moku-labs/game 0.4.2 → 0.4.4
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/{commands-DYO22lzi.d.mts → commands-C83eQ5B9.d.mts} +8 -1
- package/dist/control.d.mts +1 -1
- package/dist/control.mjs +2 -2
- package/dist/{dev-B5p9_3fz.mjs → dev-wKLgOw7c.mjs} +8 -5
- package/dist/index.d.mts +9 -9
- package/dist/index.mjs +2 -1
- package/dist/inspect.d.mts +5 -2
- package/dist/inspect.mjs +1 -1
- package/dist/{run-C7O77UQR.mjs → run-D_R0oJ6g.mjs} +36 -1
- package/dist/{sources-CTGuY3Fe.mjs → sources-DVWJ8nfr.mjs} +26 -3
- package/dist/testing.d.mts +2 -2
- package/dist/testing.mjs +2 -2
- package/dist/{types-CVUHPvMB.d.mts → types-C-4f2jD_.d.mts} +669 -18
- package/dist/types-w646dMeA.d.mts +648 -0
- package/llms.txt +3 -3
- package/package.json +13 -11
- package/dist/types-BnWH-75b.d.mts +0 -1284
|
@@ -1,1284 +0,0 @@
|
|
|
1
|
-
import { D as Point, E as FilterSlot, Gt as Events$2, K as Owner, Kt as Json, L as ComponentHandle, Pt as Descriptor, Rt as Api$4, Ut as Api$5, V as Entity, _ as PixiModule, et as LayerSort, g as PixiFilter, k as Api$1, m as Api$2, o as Events$1, sn as Events$3, t as Api$3, tt as LayerSpec, ut as Api, v as PixiSprite, vn as Require, y as PixiTexture, z as ComponentType } from "./types-Dy1xB68L.mjs";
|
|
2
|
-
import { t as AnimApi } from "./types-CVUHPvMB.mjs";
|
|
3
|
-
import { Log } from "@moku-labs/common/browser";
|
|
4
|
-
import { PluginCtx } from "@moku-labs/core";
|
|
5
|
-
|
|
6
|
-
//#region src/plugins/effects/filters/types.d.ts
|
|
7
|
-
/**
|
|
8
|
-
* One uniform of a custom filter, as declared: a number is an `f32` (`float` in GLSL), `{ color }`
|
|
9
|
-
* a `0xrrggbb` hex sent as a `vec3<f32>` (`vec3`), a tuple of 2 to 4 numbers a `vecN<f32>`
|
|
10
|
-
* (`vecN`).
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* ```ts
|
|
14
|
-
* const uniforms: Record<string, UniformDeclaration> = { amount: 0.5, color: { color: 0xffd700 }, offset: [2, 2] };
|
|
15
|
-
* ```
|
|
16
|
-
*/
|
|
17
|
-
type UniformDeclaration = number | {
|
|
18
|
-
readonly color: number;
|
|
19
|
-
} | readonly [number, number] | readonly [number, number, number] | readonly [number, number, number, number];
|
|
20
|
-
/**
|
|
21
|
-
* The component field a uniform declaration becomes: a number for a number and for a colour, a
|
|
22
|
-
* list of numbers for a tuple.
|
|
23
|
-
*
|
|
24
|
-
* @example
|
|
25
|
-
* ```ts
|
|
26
|
-
* type Amount = UniformValue<0.5>; // number
|
|
27
|
-
* ```
|
|
28
|
-
*/
|
|
29
|
-
type UniformValue<Declaration> = Declaration extends number ? number : Declaration extends {
|
|
30
|
-
readonly color: number;
|
|
31
|
-
} ? number : readonly number[];
|
|
32
|
-
/**
|
|
33
|
-
* The value of a filter component made by `defineFilter`: one field per uniform, then `enabled`
|
|
34
|
-
* and `order`.
|
|
35
|
-
*
|
|
36
|
-
* @example
|
|
37
|
-
* ```ts
|
|
38
|
-
* type TintValue = FilterValueOf<{ amount: number; color: { color: number } }>;
|
|
39
|
-
* // { amount: number; color: number; enabled: boolean; order: number }
|
|
40
|
-
* ```
|
|
41
|
-
*/
|
|
42
|
-
type FilterValueOf<Uniforms extends Record<string, UniformDeclaration>> = { -readonly [Name in keyof Uniforms]: UniformValue<Uniforms[Name]> } & {
|
|
43
|
-
enabled: boolean;
|
|
44
|
-
order: number;
|
|
45
|
-
};
|
|
46
|
-
/**
|
|
47
|
-
* What `defineFilter` takes besides the id. Both bodies are required, so the filter draws on
|
|
48
|
-
* WebGPU and on the WebGL fallback: `wgsl` is the WGSL fragment body only, the engine prepends the
|
|
49
|
-
* vertex stage, the input bindings and the `FilterUniforms` struct; `glsl` is its GLSL ES 3.0 twin
|
|
50
|
-
* over the same uniform names, the engine prepends the version, the inputs, `finalColor` and one
|
|
51
|
-
* `uniform` per declared uniform.
|
|
52
|
-
*
|
|
53
|
-
* @example
|
|
54
|
-
* ```ts
|
|
55
|
-
* const spec: FilterSpec<{ amount: number }> = {
|
|
56
|
-
* wgsl: "@fragment fn mainFragment(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> { return textureSample(uTexture, uSampler, uv) * fu.amount; }",
|
|
57
|
-
* glsl: "void main() { finalColor = texture(uTexture, vTextureCoord) * amount; }",
|
|
58
|
-
* uniforms: { amount: 1 }
|
|
59
|
-
* };
|
|
60
|
-
* ```
|
|
61
|
-
*/
|
|
62
|
-
type FilterSpec<Uniforms extends Record<string, UniformDeclaration>> = {
|
|
63
|
-
/** The WGSL fragment body: `fn mainFragment` and its helpers, without the engine's header. */readonly wgsl: string;
|
|
64
|
-
/**
|
|
65
|
-
* The GLSL ES 3.0 fragment body: `void main()`, which sets `finalColor`, and its helpers, without
|
|
66
|
-
* the engine's header. It reads each uniform under its bare name: `amount`, not `fu.amount`.
|
|
67
|
-
*/
|
|
68
|
-
readonly glsl: string; /** The uniforms in struct order. Default `{}`. */
|
|
69
|
-
readonly uniforms?: Uniforms; /** Render passes one apply of the filter costs, an integer of at least 1. Default `1`. */
|
|
70
|
-
readonly passes?: number; /** Pixels around the view's bounds, or the name of a number uniform whose live value they are. Default `0`. */
|
|
71
|
-
readonly padding?: number | string;
|
|
72
|
-
};
|
|
73
|
-
/**
|
|
74
|
-
* How one uniform travels to the GPU: its name, its WGSL type and the form it was declared in.
|
|
75
|
-
*
|
|
76
|
-
* @example
|
|
77
|
-
* ```ts
|
|
78
|
-
* const spec: UniformSpec = { name: "color", type: "vec3<f32>", form: "color", size: 3 };
|
|
79
|
-
* ```
|
|
80
|
-
*/
|
|
81
|
-
type UniformSpec = {
|
|
82
|
-
readonly name: string;
|
|
83
|
-
readonly type: "f32" | "vec2<f32>" | "vec3<f32>" | "vec4<f32>";
|
|
84
|
-
readonly form: "number" | "color" | "vector";
|
|
85
|
-
readonly size: number;
|
|
86
|
-
};
|
|
87
|
-
/**
|
|
88
|
-
* The filter a component made by `defineFilter` carries: the assembled WGSL in `source`, the
|
|
89
|
-
* assembled GLSL in `glsl`, the uniforms in struct order, the passes one apply costs and the
|
|
90
|
-
* padding.
|
|
91
|
-
*
|
|
92
|
-
* @example
|
|
93
|
-
* ```ts
|
|
94
|
-
* Tint.filter.source.includes("fn mainFragment"); // true: the WGSL, drawn on WebGPU
|
|
95
|
-
* Tint.filter.glsl.startsWith("#version 300 es"); // true: the GLSL, drawn on WebGL
|
|
96
|
-
* Tint.filter.uniforms.map(uniform => uniform.name); // ["amount", "color"]
|
|
97
|
-
* ```
|
|
98
|
-
*/
|
|
99
|
-
type FilterDefinition = {
|
|
100
|
-
readonly id: string; /** The assembled WGSL: the engine's header, the uniform struct, the body. */
|
|
101
|
-
readonly source: string; /** The assembled GLSL ES 3.0: the engine's header, one `uniform` per uniform, the body. */
|
|
102
|
-
readonly glsl: string;
|
|
103
|
-
readonly uniforms: readonly UniformSpec[];
|
|
104
|
-
readonly passes: number;
|
|
105
|
-
readonly padding: number | string;
|
|
106
|
-
};
|
|
107
|
-
/**
|
|
108
|
-
* A filter component type: the `component()` of its id, with the filter definition attached. Its
|
|
109
|
-
* numeric fields are tweenable like any component.
|
|
110
|
-
*
|
|
111
|
-
* @example
|
|
112
|
-
* ```ts
|
|
113
|
-
* const Tint: FilterComponent<{ amount: number; enabled: boolean; order: number }> =
|
|
114
|
-
* defineFilter("fx.tint", { wgsl: tintWgsl, glsl: tintGlsl, uniforms: { amount: 0 } });
|
|
115
|
-
* Tint.componentName; // "fx.tint"
|
|
116
|
-
* ```
|
|
117
|
-
*/
|
|
118
|
-
type FilterComponent<Value extends object> = ComponentType<Value> & {
|
|
119
|
-
readonly filter: FilterDefinition;
|
|
120
|
-
};
|
|
121
|
-
/**
|
|
122
|
-
* One field of a stored filter value: a number, a switch, an asset key or a vector.
|
|
123
|
-
*
|
|
124
|
-
* @example
|
|
125
|
-
* ```ts
|
|
126
|
-
* const field: FilterField = 0.5;
|
|
127
|
-
* ```
|
|
128
|
-
*/
|
|
129
|
-
type FilterField = number | boolean | string | readonly number[];
|
|
130
|
-
/**
|
|
131
|
-
* The stored value of any filter component, with the kind erased: the filter sync reads every
|
|
132
|
-
* kind through it.
|
|
133
|
-
*/
|
|
134
|
-
type FilterFields = {
|
|
135
|
-
readonly enabled: boolean;
|
|
136
|
-
readonly order: number;
|
|
137
|
-
readonly [field: string]: FilterField;
|
|
138
|
-
};
|
|
139
|
-
/**
|
|
140
|
-
* `Glow`: a soft halo of `color` around the opaque pixels of the view.
|
|
141
|
-
*
|
|
142
|
-
* @example
|
|
143
|
-
* ```ts
|
|
144
|
-
* const value: GlowValue = { strength: 2, distance: 10, color: 0xffffff, alpha: 1, enabled: true, order: 0 };
|
|
145
|
-
* ```
|
|
146
|
-
*/
|
|
147
|
-
type GlowValue = {
|
|
148
|
-
strength: number;
|
|
149
|
-
distance: number;
|
|
150
|
-
color: number;
|
|
151
|
-
alpha: number;
|
|
152
|
-
enabled: boolean;
|
|
153
|
-
order: number;
|
|
154
|
-
};
|
|
155
|
-
/**
|
|
156
|
-
* `Outline`: a ring of `thickness` pixels in `color` around the opaque pixels of the view.
|
|
157
|
-
*
|
|
158
|
-
* @example
|
|
159
|
-
* ```ts
|
|
160
|
-
* const value: OutlineValue = { thickness: 2, color: 0x000000, alpha: 1, enabled: true, order: 0 };
|
|
161
|
-
* ```
|
|
162
|
-
*/
|
|
163
|
-
type OutlineValue = {
|
|
164
|
-
thickness: number;
|
|
165
|
-
color: number;
|
|
166
|
-
alpha: number;
|
|
167
|
-
enabled: boolean;
|
|
168
|
-
order: number;
|
|
169
|
-
};
|
|
170
|
-
/**
|
|
171
|
-
* `Blur`: Pixi's blur. `quality: 0` means `config.blur.quality`; `resolution: 0` means
|
|
172
|
-
* `config.blur.phoneResolution` on a phone, otherwise 1.
|
|
173
|
-
*
|
|
174
|
-
* @example
|
|
175
|
-
* ```ts
|
|
176
|
-
* const value: BlurValue = { strength: 8, quality: 0, resolution: 0, repeatEdgePixels: true, enabled: true, order: 0 };
|
|
177
|
-
* ```
|
|
178
|
-
*/
|
|
179
|
-
type BlurValue = {
|
|
180
|
-
strength: number;
|
|
181
|
-
quality: number;
|
|
182
|
-
resolution: number;
|
|
183
|
-
repeatEdgePixels: boolean;
|
|
184
|
-
enabled: boolean;
|
|
185
|
-
order: number;
|
|
186
|
-
};
|
|
187
|
-
/**
|
|
188
|
-
* `ColorMatrix`: brightness, saturation, contrast, hue and grayscale composed in that order.
|
|
189
|
-
* Every default is the identity.
|
|
190
|
-
*
|
|
191
|
-
* @example
|
|
192
|
-
* ```ts
|
|
193
|
-
* const value: ColorMatrixValue = { brightness: 1, saturation: 0, contrast: 0, hue: 0, grayscale: 1, enabled: true, order: 0 };
|
|
194
|
-
* ```
|
|
195
|
-
*/
|
|
196
|
-
type ColorMatrixValue = {
|
|
197
|
-
brightness: number;
|
|
198
|
-
saturation: number;
|
|
199
|
-
contrast: number;
|
|
200
|
-
hue: number;
|
|
201
|
-
grayscale: number;
|
|
202
|
-
enabled: boolean;
|
|
203
|
-
order: number;
|
|
204
|
-
};
|
|
205
|
-
/**
|
|
206
|
-
* `Noise`: Pixi's noise with its seed always given, so two runs draw the same grain.
|
|
207
|
-
*
|
|
208
|
-
* @example
|
|
209
|
-
* ```ts
|
|
210
|
-
* const value: NoiseValue = { amount: 0.5, seed: 7, enabled: true, order: 0 };
|
|
211
|
-
* ```
|
|
212
|
-
*/
|
|
213
|
-
type NoiseValue = {
|
|
214
|
-
amount: number;
|
|
215
|
-
seed: number;
|
|
216
|
-
enabled: boolean;
|
|
217
|
-
order: number;
|
|
218
|
-
};
|
|
219
|
-
/**
|
|
220
|
-
* `Displacement`: Pixi's displacement over the texture of an asset key.
|
|
221
|
-
*
|
|
222
|
-
* @example
|
|
223
|
-
* ```ts
|
|
224
|
-
* const value: DisplacementValue = { map: "fx.ripple", scaleX: 20, scaleY: 20, enabled: true, order: 0 };
|
|
225
|
-
* ```
|
|
226
|
-
*/
|
|
227
|
-
type DisplacementValue = {
|
|
228
|
-
map: string;
|
|
229
|
-
scaleX: number;
|
|
230
|
-
scaleY: number;
|
|
231
|
-
enabled: boolean;
|
|
232
|
-
order: number;
|
|
233
|
-
};
|
|
234
|
-
/**
|
|
235
|
-
* `Alpha`: Pixi's alpha over the whole subtree, as one layer.
|
|
236
|
-
*
|
|
237
|
-
* @example
|
|
238
|
-
* ```ts
|
|
239
|
-
* const value: AlphaValue = { alpha: 0.5, enabled: true, order: 0 };
|
|
240
|
-
* ```
|
|
241
|
-
*/
|
|
242
|
-
type AlphaValue = {
|
|
243
|
-
alpha: number;
|
|
244
|
-
enabled: boolean;
|
|
245
|
-
order: number;
|
|
246
|
-
};
|
|
247
|
-
/**
|
|
248
|
-
* The five built-in kinds drawn by a Pixi core filter.
|
|
249
|
-
*
|
|
250
|
-
* @example
|
|
251
|
-
* ```ts
|
|
252
|
-
* const kind: CoreKind = "blur";
|
|
253
|
-
* ```
|
|
254
|
-
*/
|
|
255
|
-
type CoreKind = "blur" | "colorMatrix" | "noise" | "displacement" | "alpha";
|
|
256
|
-
/**
|
|
257
|
-
* Where a filter kind comes from: our or a game's shaders (`"wgsl"`, with the GLSL twin beside
|
|
258
|
-
* it), or a Pixi core filter.
|
|
259
|
-
*/
|
|
260
|
-
type KindSource = {
|
|
261
|
-
readonly source: "wgsl";
|
|
262
|
-
readonly definition: FilterDefinition;
|
|
263
|
-
} | {
|
|
264
|
-
readonly source: "core";
|
|
265
|
-
readonly core: CoreKind;
|
|
266
|
-
};
|
|
267
|
-
/**
|
|
268
|
-
* A filter kind before it is registered: its id, its component and its source.
|
|
269
|
-
*/
|
|
270
|
-
type KindEntry = KindSource & {
|
|
271
|
-
readonly id: string;
|
|
272
|
-
readonly component: ComponentHandle<FilterFields>;
|
|
273
|
-
};
|
|
274
|
-
/**
|
|
275
|
-
* One registered filter kind. `index` is the registration order: the built-ins first, then the
|
|
276
|
-
* filters of every feature in feature order.
|
|
277
|
-
*/
|
|
278
|
-
type FilterKind = KindEntry & {
|
|
279
|
-
readonly index: number;
|
|
280
|
-
};
|
|
281
|
-
/**
|
|
282
|
-
* The values of the uniform group of one of our filter instances: a number per `f32`, a vector
|
|
283
|
-
* per colour and tuple. Written every frame.
|
|
284
|
-
*/
|
|
285
|
-
type UniformValues = Record<string, number | Float32Array>;
|
|
286
|
-
/**
|
|
287
|
-
* The uniform group of one of our filter instances, as the instance writes it.
|
|
288
|
-
*/
|
|
289
|
-
type FilterUniforms = {
|
|
290
|
-
readonly uniforms: UniformValues;
|
|
291
|
-
};
|
|
292
|
-
/**
|
|
293
|
-
* The Pixi object of one kind on one view, with the passes one apply of it costs now: what the
|
|
294
|
-
* builders of `instance.ts` make before the instance gets its sort keys.
|
|
295
|
-
*/
|
|
296
|
-
type FilterBody = {
|
|
297
|
-
readonly kind: "wgsl";
|
|
298
|
-
readonly filter: PixiFilter;
|
|
299
|
-
readonly group: FilterUniforms | undefined;
|
|
300
|
-
readonly definition: FilterDefinition;
|
|
301
|
-
passes: number;
|
|
302
|
-
} | {
|
|
303
|
-
readonly kind: "blur";
|
|
304
|
-
readonly filter: InstanceType<PixiModule["BlurFilter"]>;
|
|
305
|
-
passes: number;
|
|
306
|
-
} | {
|
|
307
|
-
readonly kind: "colorMatrix";
|
|
308
|
-
readonly filter: InstanceType<PixiModule["ColorMatrixFilter"]>;
|
|
309
|
-
passes: number;
|
|
310
|
-
} | {
|
|
311
|
-
readonly kind: "noise";
|
|
312
|
-
readonly filter: InstanceType<PixiModule["NoiseFilter"]>;
|
|
313
|
-
passes: number;
|
|
314
|
-
} | {
|
|
315
|
-
readonly kind: "displacement";
|
|
316
|
-
readonly filter: InstanceType<PixiModule["DisplacementFilter"]>;
|
|
317
|
-
readonly sprite: PixiSprite; /** The asset key the map sprite shows now. */
|
|
318
|
-
map: string;
|
|
319
|
-
passes: number;
|
|
320
|
-
} | {
|
|
321
|
-
readonly kind: "alpha";
|
|
322
|
-
readonly filter: InstanceType<PixiModule["AlphaFilter"]>;
|
|
323
|
-
passes: number;
|
|
324
|
-
};
|
|
325
|
-
/**
|
|
326
|
-
* The instance of one kind on one view: its Pixi object, and what it sorts by on the view, the
|
|
327
|
-
* `order` of its component (written every frame) and the `index` of its kind.
|
|
328
|
-
*/
|
|
329
|
-
type FilterInstance = FilterBody & {
|
|
330
|
-
order: number;
|
|
331
|
-
readonly index: number;
|
|
332
|
-
};
|
|
333
|
-
/**
|
|
334
|
-
* What the filter sync keeps for one entity: the kinds it carries, one instance per kind, the
|
|
335
|
-
* instances waiting to be destroyed after the next assignment, the slots last handed to
|
|
336
|
-
* `renderer.sync.filters.set`, the core kinds whose component changed this frame, and the
|
|
337
|
-
* instances of this frame in their sorted order (one array, reused every frame).
|
|
338
|
-
*/
|
|
339
|
-
type FilteredView = {
|
|
340
|
-
readonly kinds: Set<string>;
|
|
341
|
-
readonly instances: Map<string, FilterInstance>;
|
|
342
|
-
readonly retired: FilterInstance[];
|
|
343
|
-
assigned: readonly FilterSlot[];
|
|
344
|
-
readonly changed: Set<string>;
|
|
345
|
-
readonly placed: FilterInstance[];
|
|
346
|
-
};
|
|
347
|
-
//#endregion
|
|
348
|
-
//#region src/plugins/effects/particles/types.d.ts
|
|
349
|
-
/**
|
|
350
|
-
* An inclusive `[min, max]` range; every particle draws its own value from it.
|
|
351
|
-
*
|
|
352
|
-
* @example
|
|
353
|
-
* ```ts
|
|
354
|
-
* const lifeMs: Range = [500, 900];
|
|
355
|
-
* ```
|
|
356
|
-
*/
|
|
357
|
-
type Range = readonly [min: number, max: number];
|
|
358
|
-
/**
|
|
359
|
-
* A value over the life of a particle, linear from its birth (`from`) to its death (`to`).
|
|
360
|
-
*
|
|
361
|
-
* @example
|
|
362
|
-
* ```ts
|
|
363
|
-
* const shrink: Curve = { from: 1, to: 0.2 };
|
|
364
|
-
* ```
|
|
365
|
-
*/
|
|
366
|
-
type Curve = {
|
|
367
|
-
readonly from: number;
|
|
368
|
-
readonly to: number;
|
|
369
|
-
};
|
|
370
|
-
/**
|
|
371
|
-
* Where a particle is born, around the origin of its emitter, in reference units. `circle` is
|
|
372
|
-
* uniform over the disc, `ring` uniform between `radius − width / 2` and `radius + width / 2`,
|
|
373
|
-
* `rect` uniform over the centred box.
|
|
374
|
-
*
|
|
375
|
-
* @example
|
|
376
|
-
* ```ts
|
|
377
|
-
* const burstRing: Shape = { kind: "ring", radius: 48, width: 16 };
|
|
378
|
-
* ```
|
|
379
|
-
*/
|
|
380
|
-
type Shape = {
|
|
381
|
-
readonly kind: "point";
|
|
382
|
-
} | {
|
|
383
|
-
readonly kind: "circle";
|
|
384
|
-
readonly radius: number;
|
|
385
|
-
} | {
|
|
386
|
-
readonly kind: "rect";
|
|
387
|
-
readonly w: number;
|
|
388
|
-
readonly h: number;
|
|
389
|
-
} | {
|
|
390
|
-
readonly kind: "ring";
|
|
391
|
-
readonly radius: number;
|
|
392
|
-
readonly width: number;
|
|
393
|
-
};
|
|
394
|
-
/**
|
|
395
|
-
* A particle effect as data, what `defineEmitter` takes. Every field is optional except
|
|
396
|
-
* `textures` and exactly one of `burst` and `rate`. Speeds are reference units per second, angles
|
|
397
|
-
* degrees with 0 to the right and 90 down, spins radians per second.
|
|
398
|
-
*
|
|
399
|
-
* @example
|
|
400
|
-
* ```ts
|
|
401
|
-
* const stars: EmitterConfig = {
|
|
402
|
-
* textures: ["fx.star", "fx.sparkle"], burst: 40, lifeMs: [500, 900], speed: [300, 700],
|
|
403
|
-
* gravity: 900, drag: 0.2, shape: { kind: "circle", radius: 24 }, alpha: { from: 1, to: 0 }
|
|
404
|
-
* };
|
|
405
|
-
* ```
|
|
406
|
-
*/
|
|
407
|
-
type EmitterConfig = {
|
|
408
|
-
/** 1 to 16 asset keys of one atlas page; a particle picks one at random. */readonly textures: readonly string[]; /** Particles emitted once, when the component appears or its `effect` changes. */
|
|
409
|
-
readonly burst?: number; /** Particles per second while `active` is true and the entity lives. */
|
|
410
|
-
readonly rate?: number; /** Life of a particle in milliseconds. Default `[600, 1000]`. */
|
|
411
|
-
readonly lifeMs?: Range; /** Start speed in reference units per second. Default `[0, 0]`. */
|
|
412
|
-
readonly speed?: Range; /** Start direction in degrees, 0 right, 90 down. Default `[0, 360]`. */
|
|
413
|
-
readonly angle?: Range; /** Downward acceleration in reference units per second². Default `0`. */
|
|
414
|
-
readonly gravity?: number; /** Velocity lost per second, 0..1: `v *= (1 − drag) ^ seconds`. Default `0`. */
|
|
415
|
-
readonly drag?: number; /** Spin in radians per second. Default `[0, 0]`. */
|
|
416
|
-
readonly spin?: Range; /** Where particles are born around the origin. Default `{ kind: "point" }`. */
|
|
417
|
-
readonly shape?: Shape; /** Scale over life. Default `{ from: 1, to: 1 }`. */
|
|
418
|
-
readonly scale?: Curve; /** Alpha over life, 0..1. Default `{ from: 1, to: 1 }`. */
|
|
419
|
-
readonly alpha?: Curve; /** Tint over life, `0xrrggbb`, linear per channel. Default white to white. */
|
|
420
|
-
readonly tint?: Curve; /** `"world"`: particles outlive the entity. `"local"`: they move and die with it. Default `"world"`. */
|
|
421
|
-
readonly space?: "world" | "local"; /** Milliseconds simulated before the first frame, for streams, 0..5000. Default `0`. */
|
|
422
|
-
readonly prewarmMs?: number; /** Hard ceiling of live particles of one instance, 1..5000. Default `256`. */
|
|
423
|
-
readonly maxParticles?: number; /** Blend mode of the particle container. Default `"normal"`. */
|
|
424
|
-
readonly blend?: "normal" | "add";
|
|
425
|
-
};
|
|
426
|
-
/**
|
|
427
|
-
* An `EmitterConfig` with every default filled in. `burst` and `rate` stay as given: exactly one
|
|
428
|
-
* of them is present.
|
|
429
|
-
*
|
|
430
|
-
* @example
|
|
431
|
-
* ```ts
|
|
432
|
-
* defineEmitter("fx.steam", { textures: ["fx.puff"], rate: 12 }).config.lifeMs; // [600, 1000]
|
|
433
|
-
* ```
|
|
434
|
-
*/
|
|
435
|
-
type ResolvedEmitterConfig = Required<Omit<EmitterConfig, "burst" | "rate">> & Pick<EmitterConfig, "burst" | "rate">;
|
|
436
|
-
/**
|
|
437
|
-
* A particle effect as `defineEmitter` returns it: frozen, registered through the `emitters` key
|
|
438
|
-
* of a feature and started by an `Emitter` component that names its id.
|
|
439
|
-
*
|
|
440
|
-
* @example
|
|
441
|
-
* ```ts
|
|
442
|
-
* const steam: EmitterDefinition = defineEmitter("fx.steam", { textures: ["fx.puff"], rate: 12 });
|
|
443
|
-
* steam.config.rate; // 12
|
|
444
|
-
* ```
|
|
445
|
-
*/
|
|
446
|
-
type EmitterDefinition = {
|
|
447
|
-
readonly id: string;
|
|
448
|
-
readonly config: ResolvedEmitterConfig;
|
|
449
|
-
};
|
|
450
|
-
/**
|
|
451
|
-
* The value of the `Emitter` component: which effect runs on the entity and whether a stream
|
|
452
|
-
* emits. `""` draws nothing.
|
|
453
|
-
*
|
|
454
|
-
* @example
|
|
455
|
-
* ```ts
|
|
456
|
-
* const value: EmitterValue = { effect: "fx.steam", active: true };
|
|
457
|
-
* ```
|
|
458
|
-
*/
|
|
459
|
-
type EmitterValue = {
|
|
460
|
-
effect: string;
|
|
461
|
-
active: boolean;
|
|
462
|
-
};
|
|
463
|
-
/**
|
|
464
|
-
* The cosmetic random source of one instance: xorshift32 over its own seed, never `Math.random`
|
|
465
|
-
* and never the model's rng.
|
|
466
|
-
*/
|
|
467
|
-
type Rng = {
|
|
468
|
-
/** The next number in `[0, 1)`. */next(): number; /** An integer in `[0, count)`. */
|
|
469
|
-
int(count: number): number; /** A number in `[min, max]`. */
|
|
470
|
-
between(min: number, max: number): number;
|
|
471
|
-
};
|
|
472
|
-
/**
|
|
473
|
-
* A Pixi particle container: what one emitter instance draws in.
|
|
474
|
-
*/
|
|
475
|
-
type PixiParticleContainer = InstanceType<PixiModule["ParticleContainer"]>;
|
|
476
|
-
/**
|
|
477
|
-
* One particle of a container, as the container keeps it.
|
|
478
|
-
*/
|
|
479
|
-
type PixiParticle = PixiParticleContainer["particleChildren"][number];
|
|
480
|
-
/**
|
|
481
|
-
* What one effect id bakes to on first use: the 64-entry curve tables, the reach rectangle's half
|
|
482
|
-
* side and the resolved textures. Dropped when a bundle with one of its textures leaves.
|
|
483
|
-
*/
|
|
484
|
-
type BakedEmitter = {
|
|
485
|
-
readonly id: string;
|
|
486
|
-
readonly config: ResolvedEmitterConfig; /** Scale per 1/64 of the life. */
|
|
487
|
-
readonly scaleTable: Float32Array; /** Alpha per 1/64 of the life, 0..1. */
|
|
488
|
-
readonly alphaTable: Float32Array; /** Tint per 1/64 of the life, packed as Pixi's `0xbbggrr`. */
|
|
489
|
-
readonly tintTable: Uint32Array; /** Half side of the box no particle leaves, in reference units. */
|
|
490
|
-
readonly reach: number;
|
|
491
|
-
readonly textures: readonly PixiTexture[]; /** The texture source of the first texture: every texture of the effect samples it. */
|
|
492
|
-
readonly source: PixiTexture["source"] | undefined; /** The largest side of the textures, in pixels. */
|
|
493
|
-
readonly maxSide: number;
|
|
494
|
-
};
|
|
495
|
-
/**
|
|
496
|
-
* The per-particle numbers a Pixi particle has no field for, one entry per live particle, in the
|
|
497
|
-
* order of `particleChildren`.
|
|
498
|
-
*/
|
|
499
|
-
type BornFields = {
|
|
500
|
-
readonly age: Float32Array;
|
|
501
|
-
readonly life: Float32Array;
|
|
502
|
-
readonly vx: Float32Array;
|
|
503
|
-
readonly vy: Float32Array;
|
|
504
|
-
readonly spin: Float32Array;
|
|
505
|
-
readonly variant: Float32Array;
|
|
506
|
-
};
|
|
507
|
-
/**
|
|
508
|
-
* One running effect: its container, the plugin-owned entity that carries it, the particle pool
|
|
509
|
-
* and the numbers the step needs.
|
|
510
|
-
*/
|
|
511
|
-
type EmitterInstance = {
|
|
512
|
-
readonly id: string;
|
|
513
|
-
readonly baked: BakedEmitter; /** The entity whose `Emitter` started it. */
|
|
514
|
-
readonly host: Entity; /** The plugin-owned entity whose `Display` is the container. */
|
|
515
|
-
readonly entity: Entity;
|
|
516
|
-
readonly container: PixiParticleContainer; /** Retired particles, reused before a new one is made. */
|
|
517
|
-
readonly pool: PixiParticle[];
|
|
518
|
-
readonly born: BornFields; /** Fraction of a particle owed to the next frame of a stream. */
|
|
519
|
-
carry: number;
|
|
520
|
-
readonly rng: Rng; /** The point the container was placed at, in root space (world space only). */
|
|
521
|
-
readonly origin: Readonly<Point>;
|
|
522
|
-
readonly space: "world" | "local"; /** Pixi's particle class, from the module the renderer loaded. */
|
|
523
|
-
readonly particleClass: PixiModule["Particle"];
|
|
524
|
-
};
|
|
525
|
-
declare namespace types_d_exports$2 {
|
|
526
|
-
export { AlphaValue, BlurValue, BundleUnloaded$1 as BundleUnloaded, ColorMatrixValue, Config$2 as Config, CoreKind, Curve, Deps$2 as Deps, DisplacementValue, EffectsApi, EffectsCtx, EffectsStats, EmitterConfig, EmitterDefinition, EmitterValue, FilterComponent, FilterDefinition, FilterSpec, FilterValueOf, GlowValue, KernelSlice$2 as KernelSlice, NoiseValue, OutlineValue, Range, ResolvedEmitterConfig, Shape, State$2 as State, UniformDeclaration, UniformSpec, UniformValue };
|
|
527
|
-
}
|
|
528
|
-
/**
|
|
529
|
-
* effects plugin config. `maxParticles` here is the global warning line; the `maxParticles` of an
|
|
530
|
-
* emitter config caps one instance.
|
|
531
|
-
*
|
|
532
|
-
* @example
|
|
533
|
-
* ```ts
|
|
534
|
-
* createApp({
|
|
535
|
-
* plugins: [...screen, effectsPlugin],
|
|
536
|
-
* pluginConfigs: { effects: { maxPasses: 16, phone: false } }
|
|
537
|
-
* });
|
|
538
|
-
* ```
|
|
539
|
-
*/
|
|
540
|
-
type Config$2 = {
|
|
541
|
-
/** Live particles over all instances above which one dev warning fires per crossing. */maxParticles: number; /** Render passes per frame above which one dev warning fires per crossing. */
|
|
542
|
-
maxPasses: number; /** Whether this device is a phone. `"auto"`: a coarse pointer and a short side of at most 820 CSS px, read once in `onStart`. */
|
|
543
|
-
phone: boolean | "auto";
|
|
544
|
-
/**
|
|
545
|
-
* What a `Blur` with `quality: 0` and `resolution: 0` resolves to. Shallow merge: a game that
|
|
546
|
-
* sets `blur` gives both fields.
|
|
547
|
-
*/
|
|
548
|
-
blur: {
|
|
549
|
-
quality: number;
|
|
550
|
-
phoneResolution: number;
|
|
551
|
-
};
|
|
552
|
-
};
|
|
553
|
-
/**
|
|
554
|
-
* effects plugin state: the particle half, the filter half, and what both share.
|
|
555
|
-
*/
|
|
556
|
-
type State$2 = {
|
|
557
|
-
/** Resolved in `onStart`. */phone: boolean; /** The `emitters` of every feature, read in `onStart`. */
|
|
558
|
-
emitters: Map<string, EmitterDefinition>; /** Per effect id, built on first use. */
|
|
559
|
-
baked: Map<string, BakedEmitter>; /** Host entity to its running instance. */
|
|
560
|
-
instances: Map<Entity, EmitterInstance>; /** World-space instances whose host left, stepped until empty. */
|
|
561
|
-
orphans: Set<EmitterInstance>; /** Ordinal of the next instance: the cosmetic seed. */
|
|
562
|
-
seedCounter: number; /** Live particles after the last step. */
|
|
563
|
-
particles: number; /** Filter kinds whose shader failed the dev check on the running backend. */
|
|
564
|
-
broken: Set<string>; /** The filter kinds, built-ins first, then the filters of every feature. */
|
|
565
|
-
kinds: Map<string, FilterKind>; /** The dev shader check per kind; `broken` holds the failed ones. */
|
|
566
|
-
checks: Map<string, "pending" | "ok">; /** Entity to the filters the sync keeps for it. */
|
|
567
|
-
views: Map<Entity, FilteredView>; /** Keys of the one-shot warnings: `"emitter:fx.x"`, `"texture:board.cell"`, `"atlas:fx.x"`. */
|
|
568
|
-
warned: Set<string>; /** Whether each budget warning is in force, so a crossing warns once. */
|
|
569
|
-
over: {
|
|
570
|
-
particles: boolean;
|
|
571
|
-
passes: boolean;
|
|
572
|
-
fullScreen: boolean;
|
|
573
|
-
}; /** The teardown closure, the two systems and the world hooks. */
|
|
574
|
-
removers: Array<() => void>;
|
|
575
|
-
};
|
|
576
|
-
/**
|
|
577
|
-
* What `app.effects.stats()` answers: live particles, running emitter instances, filter instances
|
|
578
|
-
* on views, and the render passes of the frame as the renderer counts them.
|
|
579
|
-
*
|
|
580
|
-
* @example
|
|
581
|
-
* ```ts
|
|
582
|
-
* const stats: EffectsStats = { particles: 120, emitters: 3, filters: 25, renderPasses: 49 };
|
|
583
|
-
* ```
|
|
584
|
-
*/
|
|
585
|
-
type EffectsStats = {
|
|
586
|
-
/** Live particles over every instance and orphan after the last step. */particles: number; /** Running instances plus the orphans still flying. */
|
|
587
|
-
emitters: number; /** Filter instances over every view. */
|
|
588
|
-
filters: number; /** `renderer.sync.renderPasses()`, read at call time. */
|
|
589
|
-
renderPasses: number;
|
|
590
|
-
};
|
|
591
|
-
/**
|
|
592
|
-
* effects plugin API, `app.effects`. Everything a game does with effects is data on entities;
|
|
593
|
-
* the API only counts.
|
|
594
|
-
*
|
|
595
|
-
* @example
|
|
596
|
-
* ```ts
|
|
597
|
-
* // A test waits for a merge burst to land before it compares the board.
|
|
598
|
-
* while (app.effects.stats().particles > 0) app.time.step(16);
|
|
599
|
-
* app.effects.stats().particles; // 0
|
|
600
|
-
* ```
|
|
601
|
-
*/
|
|
602
|
-
type EffectsApi = {
|
|
603
|
-
/**
|
|
604
|
-
* Counts what runs now. `renderPasses` is the renderer's own number, read at call time; the
|
|
605
|
-
* others are the plugin's. Headless every number is 0.
|
|
606
|
-
*
|
|
607
|
-
* @returns A fresh object: live particles, emitter instances with orphans, filter instances
|
|
608
|
-
* and render passes.
|
|
609
|
-
* @example
|
|
610
|
-
* ```ts
|
|
611
|
-
* // After the board rests: one steam stream, 24 enabled glows on 24 views, one disabled blur.
|
|
612
|
-
* app.effects.stats();
|
|
613
|
-
* // { particles: 18, emitters: 1, filters: 25, renderPasses: 49 }: 1 + 24 × (1 + 1), the disabled blur costs 0
|
|
614
|
-
* ```
|
|
615
|
-
*/
|
|
616
|
-
stats(): EffectsStats;
|
|
617
|
-
};
|
|
618
|
-
/**
|
|
619
|
-
* Resolved dependency APIs.
|
|
620
|
-
*/
|
|
621
|
-
type Deps$2 = {
|
|
622
|
-
flow: Api;
|
|
623
|
-
world: Api$1;
|
|
624
|
-
renderer: Api$2;
|
|
625
|
-
assets: Api$3;
|
|
626
|
-
anim: AnimApi;
|
|
627
|
-
};
|
|
628
|
-
/**
|
|
629
|
-
* What the kernel context offers before the deps are attached. `effects` owns no event, so
|
|
630
|
-
* `emit` is the kernel's and never called here.
|
|
631
|
-
*/
|
|
632
|
-
type KernelSlice$2 = PluginCtx<Config$2, State$2> & {
|
|
633
|
-
readonly global: object;
|
|
634
|
-
readonly log: Log.LogApi;
|
|
635
|
-
readonly require: Require;
|
|
636
|
-
};
|
|
637
|
-
/**
|
|
638
|
-
* Domain context shared by both modules: the kernel slice plus the resolved deps.
|
|
639
|
-
*/
|
|
640
|
-
type EffectsCtx = KernelSlice$2 & {
|
|
641
|
-
readonly deps: Deps$2;
|
|
642
|
-
};
|
|
643
|
-
/**
|
|
644
|
-
* Payload of the `assets:bundle-unloaded` hook: `keys` names every texture that is gone.
|
|
645
|
-
*/
|
|
646
|
-
type BundleUnloaded$1 = Events$1["assets:bundle-unloaded"];
|
|
647
|
-
declare namespace types_d_exports$1 {
|
|
648
|
-
export { AnySceneProjection, CheckedProjections, Config$1 as Config, DefineScene, Deps$1 as Deps, Events, KernelSlice$1 as KernelSlice, LayerMap, RunContext, SceneDefinition, SceneLayer, SceneOwner, SceneProjection, SceneSpec, ScenesApi, ScenesCtx, SortRule, State$1 as State, UiLayer };
|
|
649
|
-
}
|
|
650
|
-
/**
|
|
651
|
-
* How a layer orders what is drawn in it. The scene's name for the rule `world` stores.
|
|
652
|
-
*
|
|
653
|
-
* @example
|
|
654
|
-
* ```ts
|
|
655
|
-
* const sort: SortRule = "y";
|
|
656
|
-
* ```
|
|
657
|
-
*/
|
|
658
|
-
type SortRule = LayerSort;
|
|
659
|
-
/**
|
|
660
|
-
* What a scene declares about its layers: the key is the layer name, the order of the keys is
|
|
661
|
-
* draw order, bottom first. An omitted `sort` means `"none"`.
|
|
662
|
-
*
|
|
663
|
-
* @example
|
|
664
|
-
* ```ts
|
|
665
|
-
* const layers: LayerMap = { background: {}, cells: {}, items: { sort: "y" }, lifted: {} };
|
|
666
|
-
* ```
|
|
667
|
-
*/
|
|
668
|
-
type LayerMap = Record<string, {
|
|
669
|
-
sort?: SortRule;
|
|
670
|
-
}>;
|
|
671
|
-
/**
|
|
672
|
-
* The name of the layer every scene gets on top of the layers it declared, where popups and the
|
|
673
|
-
* HUD are mounted. A scene that declares it keeps the place it wrote it in.
|
|
674
|
-
*
|
|
675
|
-
* @example
|
|
676
|
-
* ```ts
|
|
677
|
-
* const layer: UiLayer = "ui";
|
|
678
|
-
* ```
|
|
679
|
-
*/
|
|
680
|
-
type UiLayer = "ui";
|
|
681
|
-
/**
|
|
682
|
-
* The layer names the projections of a scene may use: the keys the scene declared and the
|
|
683
|
-
* appended `ui`, which is there whether the scene wrote it or not.
|
|
684
|
-
*
|
|
685
|
-
* @example
|
|
686
|
-
* ```ts
|
|
687
|
-
* const layer: SceneLayer<{ cells: Record<never, never> }> = "ui"; // or "cells"
|
|
688
|
-
* ```
|
|
689
|
-
*/
|
|
690
|
-
type SceneLayer<Layers extends LayerMap> = (keyof Layers & string) | UiLayer;
|
|
691
|
-
/**
|
|
692
|
-
* Any projection as a scene reads it. `projection()` widens `lift` to `string` when the author
|
|
693
|
-
* declared none, so this is the loosest shape `defineScene` accepts.
|
|
694
|
-
*
|
|
695
|
-
* @example
|
|
696
|
-
* ```ts
|
|
697
|
-
* const spec: AnySceneProjection = { name: "board.items", layer: "items", lift: "lifted" };
|
|
698
|
-
* ```
|
|
699
|
-
*/
|
|
700
|
-
type AnySceneProjection = {
|
|
701
|
-
readonly name: string;
|
|
702
|
-
readonly layer: string;
|
|
703
|
-
readonly lift?: string;
|
|
704
|
-
};
|
|
705
|
-
/**
|
|
706
|
-
* The three fields of a `ProjectionSpec` a scene reads, with both layers checked against the
|
|
707
|
-
* layers the scene declares. It is what a wrong `layer` or `lift` is reported against.
|
|
708
|
-
*
|
|
709
|
-
* @example
|
|
710
|
-
* ```ts
|
|
711
|
-
* const spec: SceneProjection<"items" | "lifted"> = { name: "board.items", layer: "items" };
|
|
712
|
-
* ```
|
|
713
|
-
*/
|
|
714
|
-
type SceneProjection<Layer extends string> = {
|
|
715
|
-
readonly name: string;
|
|
716
|
-
readonly layer: Layer;
|
|
717
|
-
readonly lift?: Layer;
|
|
718
|
-
};
|
|
719
|
-
/**
|
|
720
|
-
* Checks the `lift` of one projection. A projection built without a `lift` carries the widened
|
|
721
|
-
* `string` of `projection()`, which is the one lift that is always accepted.
|
|
722
|
-
*/
|
|
723
|
-
type CheckedLift<Layer extends string, Spec> = Spec extends {
|
|
724
|
-
readonly lift?: infer Lift;
|
|
725
|
-
} ? string extends Exclude<Lift, undefined> ? Spec : Exclude<Lift, undefined> extends Layer ? Spec : SceneProjection<Layer> : Spec;
|
|
726
|
-
/**
|
|
727
|
-
* Checks one projection of a scene: first its `layer`, then its `lift`. A projection that fails
|
|
728
|
-
* becomes `SceneProjection<Layer>`, so the compiler prints the layer names it accepts.
|
|
729
|
-
*/
|
|
730
|
-
type CheckedProjection<Layer extends string, Spec> = Spec extends {
|
|
731
|
-
readonly layer: infer Declared;
|
|
732
|
-
} ? Declared extends Layer ? CheckedLift<Layer, Spec> : SceneProjection<Layer> : SceneProjection<Layer>;
|
|
733
|
-
/**
|
|
734
|
-
* Checking type: validates the projections of a scene one by one against its layer names, so the
|
|
735
|
-
* error sits on the projection that is wrong and not on the whole list.
|
|
736
|
-
*
|
|
737
|
-
* @example
|
|
738
|
-
* ```ts
|
|
739
|
-
* type Checked = CheckedProjections<"items", readonly [{ name: "board.items"; layer: "items" }]>;
|
|
740
|
-
* ```
|
|
741
|
-
*/
|
|
742
|
-
type CheckedProjections<Layer extends string, List> = { readonly [Index in keyof List]: CheckedProjection<Layer, List[Index]> };
|
|
743
|
-
/**
|
|
744
|
-
* What the author passes to `defineScene`. The layer names come from the keys of `layers` plus
|
|
745
|
-
* the appended `ui`; `NoInfer` keeps `projections` out of that inference, so a wrong name is
|
|
746
|
-
* reported on the projection and never widens the layer union. `music` is an asset key of the
|
|
747
|
-
* game, narrowed to its audio keys once the scanner splits them.
|
|
748
|
-
*
|
|
749
|
-
* @example
|
|
750
|
-
* ```ts
|
|
751
|
-
* const spec: SceneSpec<{ items: Record<never, never> }, readonly [], string, "board"> = {
|
|
752
|
-
* bundle: "board",
|
|
753
|
-
* layers: { items: {} },
|
|
754
|
-
* projections: []
|
|
755
|
-
* };
|
|
756
|
-
* ```
|
|
757
|
-
*/
|
|
758
|
-
type SceneSpec<Layers extends LayerMap, Projections extends readonly AnySceneProjection[], Asset extends string, Bundle extends string> = {
|
|
759
|
-
bundle: Bundle;
|
|
760
|
-
layers: Layers;
|
|
761
|
-
projections: Projections & CheckedProjections<NoInfer<SceneLayer<Layers>>, NoInfer<Projections>>;
|
|
762
|
-
music?: Asset;
|
|
763
|
-
};
|
|
764
|
-
/**
|
|
765
|
-
* The signature of `defineScene` bound to the asset and bundle keys of one game.
|
|
766
|
-
* `defineGame<Types>()` returns it; the loose export accepts any string.
|
|
767
|
-
*
|
|
768
|
-
* @example
|
|
769
|
-
* ```ts
|
|
770
|
-
* const defineGameScene: DefineScene<"board.music", "board"> = defineScene;
|
|
771
|
-
* ```
|
|
772
|
-
*/
|
|
773
|
-
type DefineScene<Asset extends string, Bundle extends string> = <Layers extends LayerMap, const Projections extends readonly AnySceneProjection[]>(id: string, scene: SceneSpec<Layers, Projections, Asset, Bundle>) => SceneDefinition;
|
|
774
|
-
/**
|
|
775
|
-
* A scene as `defineScene` returns it: plain frozen data, ready for the `scenes` key of a feature.
|
|
776
|
-
*
|
|
777
|
-
* @example
|
|
778
|
-
* ```ts
|
|
779
|
-
* // What defineScene("board", { bundle: "board", layers: { items: { sort: "y" } }, projections: [] })
|
|
780
|
-
* // returns: { id: "board", bundle: "board", music: undefined,
|
|
781
|
-
* // layers: [{ name: "items", sort: "y" }, { name: "ui", sort: "none" }], projections: [] }
|
|
782
|
-
* ```
|
|
783
|
-
*/
|
|
784
|
-
type SceneDefinition = {
|
|
785
|
-
readonly id: string;
|
|
786
|
-
readonly bundle: string;
|
|
787
|
-
readonly music: string | undefined; /** Draw order, bottom first. */
|
|
788
|
-
readonly layers: readonly LayerSpec[]; /** Names of the projections the scene mounts. */
|
|
789
|
-
readonly projections: readonly string[];
|
|
790
|
-
};
|
|
791
|
-
/**
|
|
792
|
-
* The one owner every mount of this plugin carries. One is enough: one scene is mounted at a
|
|
793
|
-
* time, and `unmount` is what flushes.
|
|
794
|
-
*
|
|
795
|
-
* @example
|
|
796
|
-
* ```ts
|
|
797
|
-
* const owner: SceneOwner = { kind: "plugin", name: "scenes" };
|
|
798
|
-
* ```
|
|
799
|
-
*/
|
|
800
|
-
type SceneOwner = Owner & {
|
|
801
|
-
kind: "plugin";
|
|
802
|
-
name: "scenes";
|
|
803
|
-
};
|
|
804
|
-
/**
|
|
805
|
-
* scenes plugin config: none. A scene is data in a feature, and a transition effect is V3 work
|
|
806
|
-
* of `anim`, so a key here would have no reader.
|
|
807
|
-
*
|
|
808
|
-
* @example
|
|
809
|
-
* ```ts
|
|
810
|
-
* const config: Config = {};
|
|
811
|
-
* ```
|
|
812
|
-
*/
|
|
813
|
-
type Config$1 = Record<string, never>;
|
|
814
|
-
/**
|
|
815
|
-
* scenes plugin state.
|
|
816
|
-
*/
|
|
817
|
-
type State$1 = {
|
|
818
|
-
/** Scene id to its declaration, filled in `onStart` from `flow.features`. */scenes: Map<string, SceneDefinition>; /** Id of the scene that is mounted now. */
|
|
819
|
-
current: string | undefined; /** Fast mode: the scene a transit node named, applied at the next rest node. */
|
|
820
|
-
pending: string | undefined;
|
|
821
|
-
owner: SceneOwner; /** The remover of the `onEnter` callback: set in `onStart`, run in `onStop`. */
|
|
822
|
-
teardown: (() => void) | undefined;
|
|
823
|
-
};
|
|
824
|
-
/**
|
|
825
|
-
* scenes plugin events.
|
|
826
|
-
*
|
|
827
|
-
* @example
|
|
828
|
-
* ```ts
|
|
829
|
-
* // A game plugin starts the music of the scene the graph switched to.
|
|
830
|
-
* createPlugin("music", {
|
|
831
|
-
* depends: [scenesPlugin],
|
|
832
|
-
* hooks: ctx => ({ "scenes:changed": ({ music }) => ctx.log.info("scene music", { music }) })
|
|
833
|
-
* }); // entering the board logs { music: "board.theme" }
|
|
834
|
-
* ```
|
|
835
|
-
*/
|
|
836
|
-
type Events = {
|
|
837
|
-
/** The mounted scene changed. */"scenes:changed": {
|
|
838
|
-
from: string | undefined;
|
|
839
|
-
to: string;
|
|
840
|
-
music: string | undefined;
|
|
841
|
-
};
|
|
842
|
-
};
|
|
843
|
-
/**
|
|
844
|
-
* scenes plugin API, `app.scenes`. A scene is switched by the graph, never by a call, so the
|
|
845
|
-
* plugin answers one question and takes no orders.
|
|
846
|
-
*
|
|
847
|
-
* @example
|
|
848
|
-
* ```ts
|
|
849
|
-
* // A headless test walks from the menu onto the board and asks what is mounted.
|
|
850
|
-
* await app.flow.walk([{ at: "home", intent: "play" }]);
|
|
851
|
-
* app.scenes.current(); // "board"
|
|
852
|
-
* ```
|
|
853
|
-
*/
|
|
854
|
-
type ScenesApi = {
|
|
855
|
-
/**
|
|
856
|
-
* The id of the mounted scene.
|
|
857
|
-
*
|
|
858
|
-
* @returns The scene id, `undefined` before the first switch and after the app stopped.
|
|
859
|
-
* @example
|
|
860
|
-
* ```ts
|
|
861
|
-
* // A dev overlay writes the scene next to the frame counter.
|
|
862
|
-
* app.scenes.current(); // "board", or undefined while the first bundle still loads
|
|
863
|
-
* ```
|
|
864
|
-
*/
|
|
865
|
-
current(): string | undefined;
|
|
866
|
-
};
|
|
867
|
-
/**
|
|
868
|
-
* Resolved dependency APIs.
|
|
869
|
-
*/
|
|
870
|
-
type Deps$1 = {
|
|
871
|
-
flow: Api;
|
|
872
|
-
world: Api$1;
|
|
873
|
-
assets: Api$3;
|
|
874
|
-
time: Api$4;
|
|
875
|
-
};
|
|
876
|
-
/**
|
|
877
|
-
* What the kernel context offers before the deps are attached.
|
|
878
|
-
*
|
|
879
|
-
* `index.ts` writes `events` with an annotated `register` (core spec `14-EVENT-REGISTRATION.md`
|
|
880
|
-
* row 8), so the own event reaches the context the kernel hands the factories and `emit` is the
|
|
881
|
-
* kernel's own, scenes-typed one. No member of the context is cast.
|
|
882
|
-
*/
|
|
883
|
-
type KernelSlice$1 = PluginCtx<Config$1, State$1, Events> & {
|
|
884
|
-
readonly global: object;
|
|
885
|
-
readonly log: Log.LogApi;
|
|
886
|
-
readonly require: Require;
|
|
887
|
-
};
|
|
888
|
-
/**
|
|
889
|
-
* Domain context shared by the files of the plugin.
|
|
890
|
-
*/
|
|
891
|
-
type ScenesCtx = KernelSlice$1 & {
|
|
892
|
-
readonly deps: Deps$1;
|
|
893
|
-
};
|
|
894
|
-
/**
|
|
895
|
-
* The mode and the abort signal `flow.onEnter` hands every callback.
|
|
896
|
-
*
|
|
897
|
-
* @example
|
|
898
|
-
* ```ts
|
|
899
|
-
* const run: RunContext = { mode: "live", signal: new AbortController().signal };
|
|
900
|
-
* ```
|
|
901
|
-
*/
|
|
902
|
-
type RunContext = {
|
|
903
|
-
mode: "live" | "fast";
|
|
904
|
-
signal: AbortSignal;
|
|
905
|
-
};
|
|
906
|
-
declare namespace types_d_exports {
|
|
907
|
-
export { AudioApi, AudioContextLike, AudioCtx, AudioSessionLike, AudioSessionType, BundleUnloaded, Bus, BusState, Config, Deps, KernelSlice, LifecycleChanged, ModelCommitted, MusicDescriptor, MusicMode, MusicOptions, MusicRequest, MusicSwitch, MusicTrack, RetiringTrack, SceneChanged, SfxRequest, SoundEntry, State, StreamTrack, Volumes };
|
|
908
|
-
}
|
|
909
|
-
/**
|
|
910
|
-
* The three volume buses. `master` carries the whole game, `music` the scene track, `sfx` every
|
|
911
|
-
* sound a node or a timeline fires.
|
|
912
|
-
*
|
|
913
|
-
* @example
|
|
914
|
-
* ```ts
|
|
915
|
-
* const bus: Bus = "music";
|
|
916
|
-
* ```
|
|
917
|
-
*/
|
|
918
|
-
type Bus = "master" | "music" | "sfx";
|
|
919
|
-
/**
|
|
920
|
-
* The part of `AudioContext` the plugin uses. A test passes its own through `config.context`, so
|
|
921
|
-
* nothing in the unit tests touches a sound card. `state` adds Safari's `"interrupted"`, which a
|
|
922
|
-
* phone call or the lock screen puts a context into.
|
|
923
|
-
*
|
|
924
|
-
* @example
|
|
925
|
-
* ```ts
|
|
926
|
-
* // A dev tool builds the engine's context itself and hands it to the plugin.
|
|
927
|
-
* const context: AudioContextLike = new AudioContext();
|
|
928
|
-
* createApp({ plugins: [...screen, audioPlugin], pluginConfigs: { audio: { context: () => context } } });
|
|
929
|
-
* ```
|
|
930
|
-
*/
|
|
931
|
-
type AudioContextLike = Pick<AudioContext, "destination" | "currentTime" | "createGain" | "createBufferSource" | "decodeAudioData" | "createMediaElementSource" | "resume" | "close"> & {
|
|
932
|
-
readonly state: AudioContext["state"] | "interrupted";
|
|
933
|
-
};
|
|
934
|
-
/**
|
|
935
|
-
* What `music` accepts besides the key. An absent `fadeMs` means `config.musicFadeMs` at play time.
|
|
936
|
-
*
|
|
937
|
-
* @example
|
|
938
|
-
* ```ts
|
|
939
|
-
* const options: MusicOptions = { fadeMs: 200 };
|
|
940
|
-
* ```
|
|
941
|
-
*/
|
|
942
|
-
type MusicOptions = {
|
|
943
|
-
fadeMs?: number;
|
|
944
|
-
};
|
|
945
|
-
/**
|
|
946
|
-
* `music` bound to the audio asset keys of one game. `defineGame` returns it through `audioFor`;
|
|
947
|
-
* the loose export accepts any string.
|
|
948
|
-
*
|
|
949
|
-
* @example
|
|
950
|
-
* ```ts
|
|
951
|
-
* const gameMusic: MusicDescriptor<"board.theme"> = music;
|
|
952
|
-
* ```
|
|
953
|
-
*/
|
|
954
|
-
type MusicDescriptor<Asset extends string> = (key: Asset | null, options?: MusicOptions) => Descriptor;
|
|
955
|
-
/**
|
|
956
|
-
* Reads the player's volume choice out of the committed player state. Every bus it names is
|
|
957
|
-
* applied; the ones it leaves out keep the value they had.
|
|
958
|
-
*
|
|
959
|
-
* @example
|
|
960
|
-
* ```ts
|
|
961
|
-
* const volumes: Volumes = player => (player as { settings: { audio: { music: number } } }).settings.audio;
|
|
962
|
-
* ```
|
|
963
|
-
*/
|
|
964
|
-
type Volumes = (player: Json) => Partial<Record<Bus, number>>;
|
|
965
|
-
/**
|
|
966
|
-
* How a music track is played. `"decode"`: one AudioBuffer per track, gapless, about 58 MB per
|
|
967
|
-
* 150 s at 48 kHz. `"stream"`: an `<audio>` element on a `blob:` URL of the bytes `assets` holds,
|
|
968
|
-
* about 12 MB, and a gap at every loop: 5 to 49 ms in Chromium, about 0.4 s in WebKit. A tight
|
|
969
|
-
* loop keeps `"decode"`; a long track that tolerates a seam takes `"stream"`.
|
|
970
|
-
*
|
|
971
|
-
* @example
|
|
972
|
-
* ```ts
|
|
973
|
-
* const mode: MusicMode = "stream";
|
|
974
|
-
* ```
|
|
975
|
-
*/
|
|
976
|
-
type MusicMode = "decode" | "stream";
|
|
977
|
-
/**
|
|
978
|
-
* What the page asks iOS for. `"ambient"`: mixes with other apps, the silent switch mutes it.
|
|
979
|
-
* `"playback"`: stops other apps, plays through the switch. `"auto"`: WebKit picks.
|
|
980
|
-
*
|
|
981
|
-
* @example
|
|
982
|
-
* ```ts
|
|
983
|
-
* const session: AudioSessionType = "playback";
|
|
984
|
-
* ```
|
|
985
|
-
*/
|
|
986
|
-
type AudioSessionType = "ambient" | "playback" | "auto";
|
|
987
|
-
/**
|
|
988
|
-
* The part of `navigator.audioSession` the plugin writes. `lib.dom` has no type for it, and only
|
|
989
|
-
* WebKit has the object at all.
|
|
990
|
-
*
|
|
991
|
-
* @example
|
|
992
|
-
* ```ts
|
|
993
|
-
* // What Safari answers before the plugin started: no override.
|
|
994
|
-
* const session: AudioSessionLike = { type: "auto" };
|
|
995
|
-
* ```
|
|
996
|
-
*/
|
|
997
|
-
type AudioSessionLike = {
|
|
998
|
-
type: string;
|
|
999
|
-
};
|
|
1000
|
-
/**
|
|
1001
|
-
* One music track played by an element at `music: "stream"`: the element, its source node in the
|
|
1002
|
-
* graph, the track gain every level goes through, and the `blob:` URL the element reads.
|
|
1003
|
-
*
|
|
1004
|
-
* @example
|
|
1005
|
-
* ```ts
|
|
1006
|
-
* // `state.music.stream` once "board.theme" plays at `music: "stream"`.
|
|
1007
|
-
* const { element, url }: StreamTrack = track;
|
|
1008
|
-
* element.loop; // true
|
|
1009
|
-
* url.startsWith("blob:"); // true
|
|
1010
|
-
* ```
|
|
1011
|
-
*/
|
|
1012
|
-
type StreamTrack = {
|
|
1013
|
-
element: HTMLAudioElement;
|
|
1014
|
-
node: MediaElementAudioSourceNode;
|
|
1015
|
-
gain: GainNode;
|
|
1016
|
-
url: string;
|
|
1017
|
-
};
|
|
1018
|
-
/**
|
|
1019
|
-
* A streamed track that fades out. Its timer frees the element when the fade ends: an element has
|
|
1020
|
-
* no `stop(when)` on the context clock.
|
|
1021
|
-
*
|
|
1022
|
-
* @example
|
|
1023
|
-
* ```ts
|
|
1024
|
-
* // A switch at `musicFadeMs: 600` keeps the old track here for 600 ms.
|
|
1025
|
-
* const entry: RetiringTrack = { stream: oldTrack, timer: setTimeout(free, 600) };
|
|
1026
|
-
* ```
|
|
1027
|
-
*/
|
|
1028
|
-
type RetiringTrack = {
|
|
1029
|
-
stream: StreamTrack;
|
|
1030
|
-
timer: ReturnType<typeof setTimeout>;
|
|
1031
|
-
};
|
|
1032
|
-
/**
|
|
1033
|
-
* audio plugin config.
|
|
1034
|
-
*
|
|
1035
|
-
* @example
|
|
1036
|
-
* ```ts
|
|
1037
|
-
* createApp({
|
|
1038
|
-
* plugins: [...screen, audioPlugin],
|
|
1039
|
-
* pluginConfigs: {
|
|
1040
|
-
* audio: { musicFadeMs: 400, music: "stream", session: "playback", volumes: player => player.settings.audio }
|
|
1041
|
-
* }
|
|
1042
|
-
* });
|
|
1043
|
-
* ```
|
|
1044
|
-
*/
|
|
1045
|
-
type Config = {
|
|
1046
|
-
/** Start gain of each bus, 0..1. Overridden per commit when `volumes` is set. */buses: {
|
|
1047
|
-
master: number;
|
|
1048
|
-
music: number;
|
|
1049
|
-
sfx: number;
|
|
1050
|
-
}; /** Cross-fade of a music switch, in real milliseconds of the context clock. */
|
|
1051
|
-
musicFadeMs: number; /** Reads the player's choice from the committed player. `undefined`: the buses stay at `buses`. */
|
|
1052
|
-
volumes: Volumes | undefined; /** Context factory, the test seam. `undefined`: `new AudioContext()` when the global exists, otherwise headless. */
|
|
1053
|
-
context: (() => AudioContextLike) | undefined; /** How many started sounds `journal()` keeps, newest last. `0` turns the journal off. */
|
|
1054
|
-
journal: number; /** How music plays. `"stream"` needs MP3 or AAC: WebM Opus is silent through a media element in WebKit (bug 276813). Sounds always decode. */
|
|
1055
|
-
music: MusicMode; /** Set on `navigator.audioSession.type` in `onStart`, before the context exists. No-op where the API is missing (every Chromium, Firefox). */
|
|
1056
|
-
session: AudioSessionType;
|
|
1057
|
-
};
|
|
1058
|
-
/**
|
|
1059
|
-
* One sound that started: a play of an `sfx` or a music track that began. `at` is the elapsed
|
|
1060
|
-
* game time in milliseconds, `app.time.snapshot().elapsed` at the start.
|
|
1061
|
-
*
|
|
1062
|
-
* @example
|
|
1063
|
-
* ```ts
|
|
1064
|
-
* const entry: SoundEntry = { key: "ui.click", bus: "sfx", kind: "sfx", at: 1600 };
|
|
1065
|
-
* ```
|
|
1066
|
-
*/
|
|
1067
|
-
type SoundEntry = {
|
|
1068
|
-
readonly key: string;
|
|
1069
|
-
readonly bus: Bus;
|
|
1070
|
-
readonly kind: "sfx" | "music";
|
|
1071
|
-
readonly at: number;
|
|
1072
|
-
};
|
|
1073
|
-
/**
|
|
1074
|
-
* What the plugin keeps for one bus: its gain node while a context exists, the volume a game or
|
|
1075
|
-
* the player chose, and whether it is muted.
|
|
1076
|
-
*
|
|
1077
|
-
* @example
|
|
1078
|
-
* ```ts
|
|
1079
|
-
* const entry: BusState = { gain: undefined, volume: 0.6, muted: false };
|
|
1080
|
-
* ```
|
|
1081
|
-
*/
|
|
1082
|
-
type BusState = {
|
|
1083
|
-
gain: GainNode | undefined;
|
|
1084
|
-
volume: number;
|
|
1085
|
-
muted: boolean;
|
|
1086
|
-
};
|
|
1087
|
-
/**
|
|
1088
|
-
* The music that is playing, or the key remembered until the first touch unlocks the context.
|
|
1089
|
-
* `source` plays a decoded track, `stream` a streamed one; they are never both set. Both
|
|
1090
|
-
* `undefined`: the key is only remembered (a locked context, or a refused `play()`).
|
|
1091
|
-
*
|
|
1092
|
-
* @example
|
|
1093
|
-
* ```ts
|
|
1094
|
-
* const track: MusicTrack = { key: "board.theme", gain: undefined, source: undefined, stream: undefined };
|
|
1095
|
-
* ```
|
|
1096
|
-
*/
|
|
1097
|
-
type MusicTrack = {
|
|
1098
|
-
key: string;
|
|
1099
|
-
gain: GainNode | undefined;
|
|
1100
|
-
source: AudioBufferSourceNode | undefined;
|
|
1101
|
-
stream: StreamTrack | undefined;
|
|
1102
|
-
};
|
|
1103
|
-
/**
|
|
1104
|
-
* A music switch that waits for its buffer to decode. Its identity is the token: a later request
|
|
1105
|
-
* replaces it, and the replaced switch neither starts nor fades anything when its buffer arrives.
|
|
1106
|
-
*
|
|
1107
|
-
* @example
|
|
1108
|
-
* ```ts
|
|
1109
|
-
* const waiting: MusicSwitch = { key: "ui.theme" };
|
|
1110
|
-
* ```
|
|
1111
|
-
*/
|
|
1112
|
-
type MusicSwitch = {
|
|
1113
|
-
key: string;
|
|
1114
|
-
};
|
|
1115
|
-
/**
|
|
1116
|
-
* audio plugin state.
|
|
1117
|
-
*/
|
|
1118
|
-
type State = {
|
|
1119
|
-
/** `undefined` means headless: no graph, no sound, every member still keeps its value. */context: AudioContextLike | undefined;
|
|
1120
|
-
buses: Record<Bus, BusState>; /** A lifecycle push is in force: every bus is held at zero. */
|
|
1121
|
-
paused: boolean;
|
|
1122
|
-
unlocked: boolean; /** A gesture called `resume()` and it has not settled yet. */
|
|
1123
|
-
resuming: boolean; /** Sounds fired while `resuming`, one per key; played when the context runs, dropped when it does not. */
|
|
1124
|
-
pendingSfx: Map<string, SfxRequest>; /** Per key, evicted per key on `assets:bundle-unloaded`. */
|
|
1125
|
-
decoded: Map<string, Promise<AudioBuffer>>; /** Asset keys that already got their one warning. */
|
|
1126
|
-
warned: Set<string>; /** `config.volumes` threw once and was reported; it is not reported again this run. */
|
|
1127
|
-
warnedVolumes: boolean;
|
|
1128
|
-
music: MusicTrack | undefined; /** The latest switch still decoding; `undefined` once it started or a later request replaced it. */
|
|
1129
|
-
musicPending: MusicSwitch | undefined; /** Removes the two window listeners of the unlock. */
|
|
1130
|
-
unlock: (() => void) | undefined; /** The removers of the two fx handlers. */
|
|
1131
|
-
removers: Array<() => void>; /** The `navigator.audioSession` that took `config.session`; `stop` writes `"auto"` back to it. */
|
|
1132
|
-
session: AudioSessionLike | undefined; /** Streamed tracks that fade out, each freed by its timer. */
|
|
1133
|
-
retiring: Set<RetiringTrack>;
|
|
1134
|
-
/**
|
|
1135
|
-
* The sounds that started, oldest first, at most `config.journal` of them. Frozen: a write
|
|
1136
|
-
* replaces the whole list, so `journal()` hands it out without a copy.
|
|
1137
|
-
*/
|
|
1138
|
-
journal: readonly SoundEntry[];
|
|
1139
|
-
};
|
|
1140
|
-
/**
|
|
1141
|
-
* audio plugin API, `app.audio`. Three buses and the unlock flag: what a game changes about sound
|
|
1142
|
-
* it commits to the player state, which this plugin applies on the next commit.
|
|
1143
|
-
*
|
|
1144
|
-
* @example
|
|
1145
|
-
* ```ts
|
|
1146
|
-
* // A dev overlay pulls the music down and reads the value back.
|
|
1147
|
-
* app.audio.setVolume("music", 0.2);
|
|
1148
|
-
* app.audio.volume("music"); // 0.2
|
|
1149
|
-
* ```
|
|
1150
|
-
*/
|
|
1151
|
-
type AudioApi = {
|
|
1152
|
-
/**
|
|
1153
|
-
* Sets the volume of one bus and schedules the new gain at once. The value is clamped to 0..1
|
|
1154
|
-
* and `NaN` becomes 0. Nothing is persisted: a game stores the choice in the player state and
|
|
1155
|
-
* lets `config.volumes` apply it, so the next commit overwrites what this call set.
|
|
1156
|
-
*
|
|
1157
|
-
* @param bus - The bus to change.
|
|
1158
|
-
* @param value - The new volume, 0..1.
|
|
1159
|
-
* @throws {Error} When the bus does not exist.
|
|
1160
|
-
* @example
|
|
1161
|
-
* ```ts
|
|
1162
|
-
* // A test turns the music off before it walks the graph, so nothing rings.
|
|
1163
|
-
* app.audio.setVolume("music", 0);
|
|
1164
|
-
* app.audio.volume("music"); // 0
|
|
1165
|
-
* ```
|
|
1166
|
-
*/
|
|
1167
|
-
setVolume(bus: Bus, value: number): void;
|
|
1168
|
-
/**
|
|
1169
|
-
* The stored volume of a bus, never the live gain: a muted or paused bus still answers the
|
|
1170
|
-
* value the player chose.
|
|
1171
|
-
*
|
|
1172
|
-
* @param bus - The bus to read.
|
|
1173
|
-
* @returns The stored volume, 0..1.
|
|
1174
|
-
* @throws {Error} When the bus does not exist.
|
|
1175
|
-
* @example
|
|
1176
|
-
* ```ts
|
|
1177
|
-
* // The settings screen draws the slider of the sfx bus at its stored place.
|
|
1178
|
-
* app.audio.mute("sfx", true);
|
|
1179
|
-
* app.audio.volume("sfx"); // 1: muting does not move the slider
|
|
1180
|
-
* ```
|
|
1181
|
-
*/
|
|
1182
|
-
volume(bus: Bus): number;
|
|
1183
|
-
/**
|
|
1184
|
-
* Mutes or unmutes one bus. The flag is independent of the pause: a bus muted here stays muted
|
|
1185
|
-
* when the game comes back from the background.
|
|
1186
|
-
*
|
|
1187
|
-
* @param bus - The bus to change.
|
|
1188
|
-
* @param on - `true` mutes it, `false` lets it play again.
|
|
1189
|
-
* @throws {Error} When the bus does not exist.
|
|
1190
|
-
* @example
|
|
1191
|
-
* ```ts
|
|
1192
|
-
* // A dev overlay silences the whole game while a profiler trace runs.
|
|
1193
|
-
* app.audio.mute("master", true);
|
|
1194
|
-
* app.audio.volume("master"); // 1, and nothing is heard
|
|
1195
|
-
* ```
|
|
1196
|
-
*/
|
|
1197
|
-
mute(bus: Bus, on: boolean): void;
|
|
1198
|
-
/**
|
|
1199
|
-
* Tells whether the browser let the audio context run. It is false until the first pointer
|
|
1200
|
-
* event on the page resumed it, and false for the whole of a headless run.
|
|
1201
|
-
*
|
|
1202
|
-
* @returns True once the context is running.
|
|
1203
|
-
* @example
|
|
1204
|
-
* ```ts
|
|
1205
|
-
* // A "tap to start" screen stays up until the browser allowed sound.
|
|
1206
|
-
* app.audio.unlocked(); // false before the first touch, true after it
|
|
1207
|
-
* ```
|
|
1208
|
-
*/
|
|
1209
|
-
unlocked(): boolean;
|
|
1210
|
-
/**
|
|
1211
|
-
* The sounds that started, oldest first: every play of an `sfx` and every music track that
|
|
1212
|
-
* began, at most `config.journal` of them. A sound dropped before the unlock, a missing file
|
|
1213
|
-
* and a music switch to the track that already plays are not in it. Empty while
|
|
1214
|
-
* `config.journal` is 0, the default, and after the app stopped.
|
|
1215
|
-
*
|
|
1216
|
-
* @returns The journal, frozen; a later sound replaces the list and leaves this one as it was.
|
|
1217
|
-
* @example
|
|
1218
|
-
* ```ts
|
|
1219
|
-
* // A test composed with `pluginConfigs.audio = { journal: 200 }` taps "deliver" after the
|
|
1220
|
-
* // first touch and checks the chime was heard.
|
|
1221
|
-
* app.audio.journal();
|
|
1222
|
-
* // [{ key: "board.theme", bus: "music", kind: "music", at: 0 },
|
|
1223
|
-
* // { key: "orders.complete", bus: "sfx", kind: "sfx", at: 1600 }]
|
|
1224
|
-
* ```
|
|
1225
|
-
*/
|
|
1226
|
-
journal(): readonly SoundEntry[];
|
|
1227
|
-
};
|
|
1228
|
-
/**
|
|
1229
|
-
* Resolved dependency APIs.
|
|
1230
|
-
*/
|
|
1231
|
-
type Deps = {
|
|
1232
|
-
flow: Api;
|
|
1233
|
-
assets: Api$3;
|
|
1234
|
-
model: Api$5;
|
|
1235
|
-
time: Api$4;
|
|
1236
|
-
};
|
|
1237
|
-
/**
|
|
1238
|
-
* What the kernel context offers before the deps are attached.
|
|
1239
|
-
*
|
|
1240
|
-
* `audio` owns no event, so `emit` is the kernel's and never called here.
|
|
1241
|
-
*/
|
|
1242
|
-
type KernelSlice = PluginCtx<Config, State> & {
|
|
1243
|
-
readonly global: object;
|
|
1244
|
-
readonly log: Log.LogApi;
|
|
1245
|
-
readonly require: Require;
|
|
1246
|
-
};
|
|
1247
|
-
/**
|
|
1248
|
-
* Domain context shared by the files of the plugin: the kernel slice plus the resolved deps.
|
|
1249
|
-
*/
|
|
1250
|
-
type AudioCtx = KernelSlice & {
|
|
1251
|
-
readonly deps: Deps;
|
|
1252
|
-
};
|
|
1253
|
-
/**
|
|
1254
|
-
* Payload of the `model:committed` hook: the source of every volume a game sets.
|
|
1255
|
-
*/
|
|
1256
|
-
type ModelCommitted = Events$2["model:committed"];
|
|
1257
|
-
/**
|
|
1258
|
-
* Payload of the `scenes:changed` hook: `music` is the track the next scene declared.
|
|
1259
|
-
*/
|
|
1260
|
-
type SceneChanged = Events["scenes:changed"];
|
|
1261
|
-
/**
|
|
1262
|
-
* Payload of the `lifecycle:changed` hook: the pause that holds every bus at zero.
|
|
1263
|
-
*/
|
|
1264
|
-
type LifecycleChanged = Events$3["lifecycle:changed"];
|
|
1265
|
-
/**
|
|
1266
|
-
* Payload of the `assets:bundle-unloaded` hook: `keys` names every decoded sound that is gone.
|
|
1267
|
-
*/
|
|
1268
|
-
type BundleUnloaded = Events$1["assets:bundle-unloaded"];
|
|
1269
|
-
/**
|
|
1270
|
-
* What one play of a sound asks for: the key and the bus the sound goes to.
|
|
1271
|
-
*/
|
|
1272
|
-
type SfxRequest = {
|
|
1273
|
-
key: string;
|
|
1274
|
-
bus: string;
|
|
1275
|
-
};
|
|
1276
|
-
/**
|
|
1277
|
-
* What one music switch asks for: the key, or `null` to fade the current track out.
|
|
1278
|
-
*/
|
|
1279
|
-
type MusicRequest = {
|
|
1280
|
-
key: string | null;
|
|
1281
|
-
fadeMs: number;
|
|
1282
|
-
};
|
|
1283
|
-
//#endregion
|
|
1284
|
-
export { FilterValueOf as A, EmitterValue as C, DisplacementValue as D, ColorMatrixValue as E, NoiseValue as M, OutlineValue as N, FilterComponent as O, UniformDeclaration as P, EmitterDefinition as S, BlurValue as T, EffectsApi as _, SoundEntry as a, types_d_exports$2 as b, AnySceneProjection as c, SceneDefinition as d, SceneSpec as f, Config$2 as g, types_d_exports$1 as h, MusicOptions as i, GlowValue as j, FilterSpec as k, DefineScene as l, State$1 as m, Config as n, State as o, ScenesApi as p, MusicDescriptor as r, types_d_exports as s, AudioApi as t, LayerMap as u, EffectsStats as v, AlphaValue as w, EmitterConfig as x, State$2 as y };
|