@moku-labs/game 0.0.2 → 0.0.3

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