@adatechnology/game-destruction-three 0.13.0 → 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
3
  import { DeferredQueue, WarmUpProbes, BlastShoveOptions, Vec3 as Vec3$1 } from '@adatechnology/game-contracts';
4
4
  import * as THREE_NS from 'three';
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`.
@@ -233,6 +250,8 @@ interface SpecFromObjectResult {
233
250
 
234
251
  /** Sound key for an impact on a material. */
235
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;
236
255
  /** Sound key (`drag_*`) of a base of this material dragged on a floor. */
237
256
  declare const dragSoundKey: (def: MaterialDef | undefined) => string;
238
257
  /** The renderer this package makes always has its `deferredQueue`; a third-party `DestructionRenderer` may leave it out. */
@@ -276,8 +295,66 @@ declare const EXPLOSION_BOOM: SoundRecipe;
276
295
  declare const BURN_LOOP: LoopRecipe;
277
296
  /** Something heavy dragged on a floor, by what its base is made of. */
278
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[];
279
305
  declare const DESTRUCTION_SOUND_BANK: SoundBank;
280
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
+
281
358
  /**
282
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
283
360
  * (a big blast gets the long boom).
@@ -290,8 +367,15 @@ type SoundKeyRule = {
290
367
  readonly volume: number;
291
368
  readonly sound: string;
292
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;
293
372
  };
294
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>>;
295
379
  /**
296
380
  * The keys `createDestructionRenderer` sends to `onSound`, mapped to the sounds the teams delivered. The Cena de Arte has a sound for every material
297
381
  * (clang, wood, glass, crumble, thud...); the game has four classes (impact, glass, wood, explosion) and its own explosion thump. Which team sound answers
@@ -301,7 +385,7 @@ declare const DESTRUCTION_SOUND_KEYS: Readonly<Record<DestructionSoundStyle, Rea
301
385
  type DestructionSoundOptions = {
302
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. */
303
387
  readonly style?: DestructionSoundStyle;
304
- /** 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. */
305
389
  readonly keys?: Readonly<Record<string, SoundKeyRule | null>>;
306
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). */
307
391
  readonly fire?: {
@@ -310,27 +394,82 @@ type DestructionSoundOptions = {
310
394
  };
311
395
  /** Burn loop on or off (default on). */
312
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;
313
414
  };
314
415
  type DestructionSound = {
315
416
  /** Pass to `createDestructionRenderer({ onSound })`. */
316
- readonly onSound: (key: string, point: Vec3$1, volume: number) => void;
317
- /** The rule a key resolves to, if any. */
318
- 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;
319
424
  /** Parts burning now. */
320
425
  readonly burning: () => number;
321
426
  dispose(): void;
322
427
  };
323
428
  declare const DEFAULT_FIRE: {
324
- readonly perFire: 0.05;
325
- 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;
326
437
  };
438
+ declare const DEFAULT_INDOOR_CONFINEMENT = 1.3;
327
439
  /**
328
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
329
- * `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.
330
444
  */
331
445
  declare function createDestructionSound(engine: AudioEngine, options?: DestructionSoundOptions): DestructionSound;
332
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
+
333
472
  /** Every particle kind the renderer can emit (the game's sprite kinds, see `@adatechnology/game-vfx`). */
334
473
  type ParticleKind = SpriteKind;
335
474
 
336
- 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 };