@adatechnology/game-destruction-three 0.12.1 → 0.14.0

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/README.md CHANGED
@@ -7,7 +7,7 @@ physics, loose pieces after a part comes off, and pooled particles (fire, smoke,
7
7
  - Nothing is changed in place: geometry and materials are cloned on the first visual change of a mesh, and `restore` / `clear` give the originals back.
8
8
  - Blasts and the fires of burning parts are drawn by the effects world of [`@adatechnology/game-vfx`](../game-vfx) (an `explode` event plays `ev.effect ?? 'generic'`, scaled by the radius when it names none). Pass the world the host shares in `vfx`; without it the renderer makes its own world and its own `game-vfx-three` layer and steps them in `update`. Debris, sparks, drips and leaks still use two pooled `THREE.Points` (additive and normal blending), with a soft sprite computed in the shader (no texture asset); they move to the shared sprite pool when `game-vfx` gets one.
9
9
  - `three` is a peer dependency used for types only: you pass your own `THREE` instance at runtime.
10
- - The renderer plays nothing: you get keys (`impact_metal`, `shatter_glass`, `burn_loop`, `burn_end`, ...) through `onSound`. `createDestructionSound(engine)` is the sound bank for them on a [`@adatechnology/game-audio`](../game-audio) engine: the Cena de Arte's material sounds (clang, wood, glass, crumble, thud, boom...) and the game's explosion thump, a fire loop that follows how many parts burn, and a key table (`style: 'art_scene' | 'game'`, `keys` to replace) so a game changes what each key plays.
10
+ - The renderer plays nothing: you get keys (`impact_metal`, `shatter_glass`, `burn_loop`, `burn_end`, ...) through `onSound`. `createDestructionSound(engine)` is the sound bank for them on a [`@adatechnology/game-audio`](../game-audio) engine: the Cena de Arte's material sounds (clang, wood, glass, crumble, thud, boom...) and the game's explosion thump, a fire loop that follows how many parts burn, and a key table (`style: 'art_scene' | 'game'`, `keys` to replace) so a game changes what each key plays. An impact, a break or a ricochet also sends `detail.keys`, the keys to try first, most specific first: `impact_<material>_<blow>`, `impact_<material>`, `impact_<family>_<blow>`, `impact_<family>` (`blow` is the hit's `DamageType`, carried by every destruction event as `blow`; the family comes from `impactFamily`: metal, glass, ceramic, soil, masonry, wood, plant, plastic, rubber, wax, soft). `IMPACT_SOUND_KEYS` gives a bullet, a blow and a blade their own sound on each family; a game replaces or silences any key in `keys`.
11
11
 
12
12
  Contracts live in [`@adatechnology/game-contracts`](../game-contracts); the types are re-exported here, so existing imports keep working.
13
13
 
package/dist/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- import { Fragment, Vec3, DebrisKind, HitResult, DestructionWorld, MaterialId, DestructibleSpec, MaterialDef } from '@adatechnology/game-destruction';
1
+ import { Fragment, Vec3, DebrisKind, HitResult, DestructionWorld, MaterialId, DestructibleSpec, MaterialDef, DamageType } from '@adatechnology/game-destruction';
2
2
  export { Quat, Vec3 } from '@adatechnology/game-destruction';
3
+ import { DeferredQueue, WarmUpProbes, BlastShoveOptions, Vec3 as Vec3$1 } from '@adatechnology/game-contracts';
3
4
  import * as THREE_NS from 'three';
4
- import { WarmUpProbes, BlastShoveOptions, Vec3 as Vec3$1 } from '@adatechnology/game-contracts';
5
5
  import { VfxWorld, SpriteKind } from '@adatechnology/game-vfx';
6
- import { LoopRecipe, SoundBank, SoundRecipe, AudioEngine } from '@adatechnology/game-audio';
6
+ import { LoopRecipe, SoundBank, SoundRecipe, Falloff, AudioEngine, SampleDefaults, SfxManifest } from '@adatechnology/game-audio';
7
7
 
8
8
  /** What a fragment's resting time can depend on: the fragment (its material, size, shape) and the id of the object it came from. */
9
9
  type FragmentLifeRule = (fragment: Fragment, objectId: string) => number;
@@ -20,6 +20,19 @@ type ThreeNamespace = typeof THREE_NS;
20
20
  type Object3D = THREE_NS.Object3D;
21
21
  type Mesh = THREE_NS.Mesh;
22
22
  type Quality = 'low' | 'medium' | 'high';
23
+ /** What a sound key carries besides the point and the volume: the named explosion and its size, how enclosed it was, how long a leak lasts. */
24
+ interface SoundDetail {
25
+ /** An impact or a break: the keys to try before the key itself, most specific first (material and blow, material, family and blow, family). */
26
+ readonly keys?: readonly string[];
27
+ /** `explode`: the named explosion (`explosions.json` of game-vfx: `m67`, `c4`, `propane_p13`...). */
28
+ readonly effect?: string;
29
+ /** `explode`: blast radius in metres. */
30
+ readonly radius?: number;
31
+ /** `explode`: how enclosed the blast was (1 in the open, more in a room; from the blast report). */
32
+ readonly confinement?: number;
33
+ /** `primed`: seconds until the vessel bursts (the gas leak lasts that long). */
34
+ readonly seconds?: number;
35
+ }
23
36
  interface RendererOptions {
24
37
  /** Scene that receives fragments, loose pieces, decals and particles. */
25
38
  scene: THREE_NS.Scene;
@@ -60,8 +73,12 @@ interface RendererOptions {
60
73
  shoveable?: (objectId: string) => boolean;
61
74
  /** Called when the renderer made a bound object a piece on its own: a blast shoved it (`blast`, see `shoveable`), or furniture came apart and what stayed is loose too (`apart`). */
62
75
  onLoosened?: (objectId: string, piece: Piece, cause: 'blast' | 'apart') => void;
63
- /** Called when a sound should play; the host maps keys to audio (e.g. `impact_metal`, `shatter_glass`, `ignite`, `explode`). `burn_loop` is sent when a part starts to burn and `burn_end` when it burns out or is put out. */
64
- onSound?: (key: string, point: Vec3, volume: number) => void;
76
+ /**
77
+ * Called when a sound should play; the host maps keys to audio (e.g. `impact_metal`, `shatter_glass`, `ignite`, `explode`). `burn_loop` is sent when a part starts to burn and `burn_end` when it burns out or is put out.
78
+ * An impact or a break also sends `detail.keys`, the keys to try before `key`, most specific first (`impact_concrete_ballistic`, `impact_concrete`, `impact_masonry_ballistic`, `impact_masonry`).
79
+ * An explosion sends its named effect, radius and confinement; `primed` sends the seconds of its leak.
80
+ */
81
+ onSound?: (key: string, point: Vec3, volume: number, detail?: SoundDetail) => void;
65
82
  /**
66
83
  * The effects world that draws blasts and fires. Pass the one the host shares with the other renderers (and step it, through its
67
84
  * `VfxRenderer.update`, once per frame); without it the renderer makes its own world and its own three layer and steps them in `update`.
@@ -175,6 +192,11 @@ interface DestructionRenderer {
175
192
  impulse(objectId: string, impulse: Vec3, point?: Vec3): void;
176
193
  /** Integrate fragments, pieces and particles; apply pending visual changes. Call once per frame after `world.update`. */
177
194
  update(dt: number): void;
195
+ /**
196
+ * The waiting events and the presentation slices they left, as a `DeferredQueue` for a shared frame budget (id `destruction.events`). `budget.register`
197
+ * attaches it and `update` stops draining by itself; unregistering gives the draining back to `update` and `eventBudgetMs`. Asking for it changes nothing.
198
+ */
199
+ deferredQueue?(): DeferredQueue;
178
200
  /** Emit particles directly (muzzle flashes, extinguisher foam, tool sparks). */
179
201
  emit(kind: DebrisKind | 'fire' | 'flash' | 'foam', point: Vec3, normal: Vec3, count: number, speed?: number): void;
180
202
  /**
@@ -228,9 +250,14 @@ interface SpecFromObjectResult {
228
250
 
229
251
  /** Sound key for an impact on a material. */
230
252
  declare function impactKey(def: MaterialDef | undefined): string;
253
+ /** Sound key (`shot_*`) of a round striking this material: sharper than a blow (a tink on steel, a crack on glass); a host without it plays the `impact_*`. */
254
+ declare const shotKey: (def: MaterialDef | undefined) => string;
231
255
  /** Sound key (`drag_*`) of a base of this material dragged on a floor. */
232
256
  declare const dragSoundKey: (def: MaterialDef | undefined) => string;
233
- declare function createDestructionRenderer(THREE: ThreeNamespace, options: RendererOptions): DestructionRenderer;
257
+ /** The renderer this package makes always has its `deferredQueue`; a third-party `DestructionRenderer` may leave it out. */
258
+ declare function createDestructionRenderer(THREE: ThreeNamespace, options: RendererOptions): DestructionRenderer & {
259
+ deferredQueue(): DeferredQueue;
260
+ };
234
261
 
235
262
  /** What `userData.destructionPart` says: a string (the part id) or an object `{ id, material?, parent?, kind? }`. */
236
263
  interface PartHint {
@@ -268,8 +295,66 @@ declare const EXPLOSION_BOOM: SoundRecipe;
268
295
  declare const BURN_LOOP: LoopRecipe;
269
296
  /** Something heavy dragged on a floor, by what its base is made of. */
270
297
  declare const DRAG_SOUNDS: readonly SoundRecipe[];
298
+ /**
299
+ * A blow on a material, by what it is made of and what met it (`impactSoundKeys`): a bullet, a blunt blow (a punch, a kick, a fall), a blade. Each
300
+ * is drawn from how the real thing sounds (checklist 5.3): a bullet rings steel and goes "tchak" on concrete with pebbles after it, a fist on steel is a
301
+ * low "bong", a knife scrapes it; the ground is a dull "tuf", ceramic a dry "tic", plastic a hollow "toc", a sofa a "fump". Levels are matched so a
302
+ * bullet on concrete is as loud as on wood.
303
+ */
304
+ declare const IMPACT_SOUNDS: readonly SoundRecipe[];
271
305
  declare const DESTRUCTION_SOUND_BANK: SoundBank;
272
306
 
307
+ /**
308
+ * The sound family of a material, from its tags: what it sounds like when something meets it. Finer than the five keys of `impactKey` (which stay the
309
+ * last fallback): ceramic is not glass, the ground is not concrete, plastic and wax are not wood, leaves are not a sofa.
310
+ */
311
+ type ImpactFamily = 'metal' | 'glass' | 'ceramic' | 'soil' | 'masonry' | 'wood' | 'plant' | 'plastic' | 'rubber' | 'wax' | 'soft';
312
+ /** Tag → family, in the order they are tried (`green_wood` is `plant` and `wood`: wood wins; `gravel` is `soil` and `masonry`: soil wins). */
313
+ declare const IMPACT_FAMILY_TAGS: readonly (readonly [tag: string, family: ImpactFamily])[];
314
+ /** The family of a material, or `undefined` when none of its tags names one (the five legacy keys of `impactKey` then answer). */
315
+ declare function impactFamily(def: MaterialDef | undefined): ImpactFamily | undefined;
316
+ /** What an impact or a break sends with its key: the keys to try before it, most specific first (material and blow, material, family and blow, family). */
317
+ type ImpactKeys = SoundDetail & {
318
+ readonly keys: readonly string[];
319
+ };
320
+ /**
321
+ * The impact keys for a blow on a material, most specific first: `impact_<material>_<blow>`, `impact_<material>`, `impact_<family>_<blow>`,
322
+ * `impact_<family>` (blow is the `DamageType`: `ballistic`, `blunt`, `slash`, `explosive`...). The legacy `impactKey` is the last fallback the caller sends as the key.
323
+ */
324
+ declare function impactSoundKeys(material: string, def: MaterialDef | undefined, blow?: DamageType): ImpactKeys;
325
+ /** The keys for a part of this material breaking, most specific first: `break_<material>_<blow>`, `break_<material>`, `break_<family>_<blow>`, `break_<family>`. */
326
+ declare function breakSoundKeys(material: string, def: MaterialDef | undefined, blow?: DamageType): ImpactKeys;
327
+ /** The keys for a round glancing off a part of this material: `ricochet_<material>`, `ricochet_<family>` (then the plain `ricochet`). */
328
+ declare function ricochetSoundKeys(material: string, def: MaterialDef | undefined): ImpactKeys;
329
+
330
+ /** A blast near the listener: loud for tens of metres; lowpass cutoffs fall with distance. */
331
+ declare const BLAST_FALLOFF: Falloff;
332
+ /** A blast far away (past `BLAST_FAR_DISTANCE`): heard for hundreds of metres. */
333
+ declare const BLAST_FAR_FALLOFF: Falloff;
334
+ /** Fire, ignition and crackles: a room-size sound. */
335
+ declare const FIRE_FALLOFF: Falloff;
336
+ /** Metres from the listener past which a blast plays its far version. */
337
+ declare const BLAST_FAR_DISTANCE = 60;
338
+ /** The explosion families, each with its own sound. */
339
+ declare const BLAST_KINDS: readonly ["grenade", "big", "fuel", "car", "pipe_bomb", "rocket", "flashbang", "molotov"];
340
+ type BlastKind = (typeof BLAST_KINDS)[number];
341
+ /**
342
+ * Which family each named explosion of `@adatechnology/game-vfx` (`explosions.json`, the `effect` of an explosive part) belongs to. Data: a game adds
343
+ * or changes entries through `DestructionSoundOptions.blastKinds`. An explosion without a known effect is a grenade below `BIG_BLAST_RADIUS` and a big
344
+ * charge above it.
345
+ */
346
+ declare const BLAST_KIND_OF: Readonly<Record<string, BlastKind>>;
347
+ /** Blast radius (metres) from which an explosion with no known effect is a big charge. */
348
+ declare const BIG_BLAST_RADIUS = 8;
349
+ /** One sound per explosion family, the far and the indoor versions. */
350
+ declare const BLAST_SOUNDS: readonly SoundRecipe[];
351
+ /** Fire: ignition, gas leaking from a vessel about to burst, a fire put out, and the crackles that ride on the fire loop. */
352
+ declare const FIRE_SOUNDS: readonly SoundRecipe[];
353
+ /** The roar of the fires: low, soft noise; `createDestructionSound` raises it with every part that burns and sprinkles crackles on it. */
354
+ declare const FIRE_LOOP: LoopRecipe;
355
+ /** The sound of a blast family. */
356
+ declare const BLAST_SOUND_OF: Readonly<Record<BlastKind, string>>;
357
+
273
358
  /**
274
359
  * What a sound key plays: the recipe, its params, a scale on the volume the renderer gives, and (optionally) a louder recipe above a volume
275
360
  * (a big blast gets the long boom).
@@ -282,8 +367,15 @@ type SoundKeyRule = {
282
367
  readonly volume: number;
283
368
  readonly sound: string;
284
369
  };
370
+ /** A sound of this bank played when the engine does not know `sound` (a vehicle door without the vehicle bank registered shuts like metal). */
371
+ readonly fallback?: string;
285
372
  };
286
373
  type DestructionSoundStyle = 'art_scene' | 'game';
374
+ /**
375
+ * The keys of a blow on a material (`impactSoundKeys`, `breakSoundKeys`): `impact_<family>_<blow>`, `impact_<material>[_<blow>]`, `break_<...>`. The renderer
376
+ * sends them before its legacy family key, so the first one with a rule plays; a game replaces any of them in `keys`.
377
+ */
378
+ declare const IMPACT_SOUND_KEYS: Readonly<Record<string, SoundKeyRule>>;
287
379
  /**
288
380
  * The keys `createDestructionRenderer` sends to `onSound`, mapped to the sounds the teams delivered. The Cena de Arte has a sound for every material
289
381
  * (clang, wood, glass, crumble, thud...); the game has four classes (impact, glass, wood, explosion) and its own explosion thump. Which team sound answers
@@ -293,7 +385,7 @@ declare const DESTRUCTION_SOUND_KEYS: Readonly<Record<DestructionSoundStyle, Rea
293
385
  type DestructionSoundOptions = {
294
386
  /** Which team answers the keys (default `art_scene`, the kit mix). `game` falls back to the art scene for the keys the game has no sound for. */
295
387
  readonly style?: DestructionSoundStyle;
296
- /** Replaces or adds a key's rule; `null` silences it. */
388
+ /** Replaces or adds a key's rule; `null` silences it. A key without a rule that is the id of a registered sound (the foley bank's) plays that sound. */
297
389
  readonly keys?: Readonly<Record<string, SoundKeyRule | null>>;
298
390
  /** The fire loop: the volume each burning part adds and the most it reaches (the page never raises it, so these are the kit's). */
299
391
  readonly fire?: {
@@ -302,27 +394,82 @@ type DestructionSoundOptions = {
302
394
  };
303
395
  /** Burn loop on or off (default on). */
304
396
  readonly burnLoop?: boolean;
397
+ /** The fire loop played while something burns (default `fire_loop`; `burn_loop` is the art scene's bright one). */
398
+ readonly fireLoop?: string;
399
+ /** Crackles per second over the fire loop: `base + perSqrtFire × √(parts burning)`, at most `max` (default 2, 3, 14). `false` turns them off. */
400
+ readonly crackles?: false | {
401
+ readonly base?: number;
402
+ readonly perSqrtFire?: number;
403
+ readonly max?: number;
404
+ readonly volume?: number;
405
+ };
406
+ /** Adds to or changes `BLAST_KIND_OF`: which family a named explosion (`effect`) sounds like. */
407
+ readonly blastKinds?: Readonly<Record<string, BlastKind>>;
408
+ /** Metres past which a blast plays `explosion_far` instead of its own sound (default `BLAST_FAR_DISTANCE`). */
409
+ readonly blastFarDistance?: number;
410
+ /** `confinement` of a blast (from its report) from which the indoor tail plays too (default 1.3). */
411
+ readonly indoorConfinement?: number;
412
+ /** Seed of the crackle timing (default 1). */
413
+ readonly seed?: number;
305
414
  };
306
415
  type DestructionSound = {
307
416
  /** Pass to `createDestructionRenderer({ onSound })`. */
308
- readonly onSound: (key: string, point: Vec3$1, volume: number) => void;
309
- /** The rule a key resolves to, if any. */
310
- rule(key: string): SoundKeyRule | undefined;
417
+ readonly onSound: (key: string, point: Vec3$1, volume: number, detail?: SoundDetail) => void;
418
+ /** Moves the fire crackles on; call once per frame with the frame time in seconds. */
419
+ update(dt: number): void;
420
+ /** The sound an explosion plays (`explode` with its detail), for a listener at the engine's listener position. */
421
+ blastSound(point: Vec3$1, volume: number, detail?: SoundDetail): string;
422
+ /** The rule a key resolves to, if any; with `detail`, the rule `onSound` would play (the first of `detail.keys` that has one, else `key`'s). */
423
+ rule(key: string, detail?: SoundDetail): SoundKeyRule | undefined;
311
424
  /** Parts burning now. */
312
425
  readonly burning: () => number;
313
426
  dispose(): void;
314
427
  };
315
428
  declare const DEFAULT_FIRE: {
316
- readonly perFire: 0.05;
317
- readonly max: 0.2;
429
+ readonly perFire: 0.08;
430
+ readonly max: 0.45;
431
+ };
432
+ declare const DEFAULT_CRACKLES: {
433
+ readonly base: 2;
434
+ readonly perSqrtFire: 3;
435
+ readonly max: 14;
436
+ readonly volume: 0.5;
318
437
  };
438
+ declare const DEFAULT_INDOOR_CONFINEMENT = 1.3;
319
439
  /**
320
440
  * Plays the renderer's sound keys on a game-audio engine: the bank is registered in the engine, a key becomes a positional sound at the point, and
321
- * `burn_loop` / `burn_end` (a part starts to burn, a part burns out or is put out) drive one fire loop whose volume follows how many parts burn.
441
+ * `burn_loop` / `burn_end` (a part starts to burn, a part burns out or is put out) drive one fire loop whose volume follows how many parts burn, with
442
+ * crackles at the burning points (`update`). An explosion plays the sound of its family (`BLAST_KIND_OF` by its effect), the far rumble past
443
+ * `blastFarDistance` and the indoor tail when the blast was confined.
322
444
  */
323
445
  declare function createDestructionSound(engine: AudioEngine, options?: DestructionSoundOptions): DestructionSound;
324
446
 
447
+ /** The numbers of one recorded sound, plus whether it loops (the fire's roar). */
448
+ type BlastFireSampleDefaults = SampleDefaults & {
449
+ readonly loop?: boolean | {
450
+ readonly crossfade?: number;
451
+ };
452
+ };
453
+ /**
454
+ * The sounds of the blasts and the fire that have recorded takes, with their bus, level and distance curve (the recipes' own curves, so a file and its
455
+ * fallback fade alike with distance). Data: a game passes its own table to `blastFireSampleBank`.
456
+ */
457
+ declare const BLAST_FIRE_SAMPLES: Readonly<Record<string, BlastFireSampleDefaults>>;
458
+ type BlastFireSampleBankOptions = {
459
+ /** Bank id (default 'destruction-samples'). */
460
+ readonly id?: string;
461
+ /** Where the manifest's folder is served (`/sfx/`); default ''. */
462
+ readonly baseUrl?: string;
463
+ /** Numbers per id (default `BLAST_FIRE_SAMPLES`). */
464
+ readonly sounds?: Readonly<Record<string, BlastFireSampleDefaults>>;
465
+ };
466
+ /**
467
+ * A bank of the recorded blasts and fire from the kit's sound manifest. Register it with the engine next to `DESTRUCTION_SOUND_BANK`: each id plays
468
+ * its files once loaded and its synthesized recipe until then.
469
+ */
470
+ declare function blastFireSampleBank(manifest: SfxManifest, options?: BlastFireSampleBankOptions): SoundBank;
471
+
325
472
  /** Every particle kind the renderer can emit (the game's sprite kinds, see `@adatechnology/game-vfx`). */
326
473
  type ParticleKind = SpriteKind;
327
474
 
328
- export { BURN_LOOP, type BindOptions, DEFAULT_FIRE, DESTRUCTION_SOUND_BANK, DESTRUCTION_SOUND_KEYS, DRAG_SOUNDS, type DestructionRenderer, type DestructionSound, type DestructionSoundOptions, type DestructionSoundStyle, EXPLOSION_BOOM, type ExplodeOptions, type FragmentLifeRule, type LooseBody, type Mesh, type MeshRange, type Object3D, type PartHint, type ParticleKind, type Piece, type Quality, type RendererOptions, SCENE_SOUNDS, type SolidBox, type SoundKeyRule, type SpecFromObjectOptions, type SpecFromObjectResult, type ThreeNamespace, createDestructionRenderer, createDestructionSound, dragSoundKey, heatColor, impactKey, specFromObject3D };
475
+ export { BIG_BLAST_RADIUS, BLAST_FALLOFF, BLAST_FAR_DISTANCE, BLAST_FAR_FALLOFF, BLAST_FIRE_SAMPLES, BLAST_KINDS, BLAST_KIND_OF, BLAST_SOUNDS, BLAST_SOUND_OF, BURN_LOOP, type BindOptions, type BlastFireSampleBankOptions, type BlastFireSampleDefaults, type BlastKind, DEFAULT_CRACKLES, DEFAULT_FIRE, DEFAULT_INDOOR_CONFINEMENT, DESTRUCTION_SOUND_BANK, DESTRUCTION_SOUND_KEYS, DRAG_SOUNDS, type DestructionRenderer, type DestructionSound, type DestructionSoundOptions, type DestructionSoundStyle, EXPLOSION_BOOM, type ExplodeOptions, FIRE_FALLOFF, FIRE_LOOP, FIRE_SOUNDS, type FragmentLifeRule, IMPACT_FAMILY_TAGS, IMPACT_SOUNDS, IMPACT_SOUND_KEYS, type ImpactFamily, type ImpactKeys, type LooseBody, type Mesh, type MeshRange, type Object3D, type PartHint, type ParticleKind, type Piece, type Quality, type RendererOptions, SCENE_SOUNDS, type SolidBox, type SoundDetail, type SoundKeyRule, type SpecFromObjectOptions, type SpecFromObjectResult, type ThreeNamespace, blastFireSampleBank, breakSoundKeys, createDestructionRenderer, createDestructionSound, dragSoundKey, heatColor, impactFamily, impactKey, impactSoundKeys, ricochetSoundKeys, shotKey, specFromObject3D };