@adatechnology/game-destruction-three 0.14.0 → 0.15.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/dist/index.d.ts +21 -1
- package/dist/index.global.js +106 -33
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +106 -33
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3187,7 +3187,7 @@ var Renderer = class {
|
|
|
3187
3187
|
this.startFire(ev);
|
|
3188
3188
|
this.activate(ev.object, ev.part);
|
|
3189
3189
|
this.sound("ignite", ev.point, 0.7);
|
|
3190
|
-
this.sound("burn_loop", ev.point, 0.5);
|
|
3190
|
+
this.sound("burn_loop", ev.point, 0.5, this.burning(ev.object, ev.part));
|
|
3191
3191
|
break;
|
|
3192
3192
|
case "primed":
|
|
3193
3193
|
if (ev.trigger !== "explosion") {
|
|
@@ -3195,7 +3195,7 @@ var Renderer = class {
|
|
|
3195
3195
|
this.primedBurning.add(`${ev.object}|${ev.part}`);
|
|
3196
3196
|
this.activate(ev.object, ev.part);
|
|
3197
3197
|
this.sound("primed", ev.point, 0.7, { seconds: ev.delay });
|
|
3198
|
-
this.sound("burn_loop", ev.point, 0.5);
|
|
3198
|
+
this.sound("burn_loop", ev.point, 0.5, { fuel: "gas" });
|
|
3199
3199
|
}
|
|
3200
3200
|
break;
|
|
3201
3201
|
case "spall":
|
|
@@ -3221,15 +3221,15 @@ var Renderer = class {
|
|
|
3221
3221
|
this.stopFire(`${ev.object}|${ev.part}`);
|
|
3222
3222
|
this.vfx.burst("steam", f.point, [0, 1, 0], 14, 1.2);
|
|
3223
3223
|
}
|
|
3224
|
-
if (f || primed) this.sound("burn_end", f?.point ?? this.rootPoint(ev.object), 0.5);
|
|
3224
|
+
if (f || primed) this.sound("burn_end", f?.point ?? this.rootPoint(ev.object), 0.5, primed ? { fuel: "gas" } : this.burning(ev.object, ev.part));
|
|
3225
3225
|
this.activate(ev.object, ev.part);
|
|
3226
3226
|
this.sound("extinguish", f?.point ?? this.rootPoint(ev.object), 0.6);
|
|
3227
3227
|
break;
|
|
3228
3228
|
}
|
|
3229
3229
|
case "burnout": {
|
|
3230
3230
|
this.stopFire(`${ev.object}|${ev.part}`);
|
|
3231
|
-
this.primedBurning.delete(`${ev.object}|${ev.part}`);
|
|
3232
|
-
this.sound("burn_end", ev.point, 0.5);
|
|
3231
|
+
const primed = this.primedBurning.delete(`${ev.object}|${ev.part}`);
|
|
3232
|
+
this.sound("burn_end", ev.point, 0.5, primed ? { fuel: "gas" } : this.burning(ev.object, ev.part));
|
|
3233
3233
|
this.vfx.burst("ash", ev.point, [0, 1, 0], 16, 1);
|
|
3234
3234
|
this.vfx.burst("smoke", ev.point, [0, 1, 0], 6, 0.8, { color: SMOKE_TONES.grey });
|
|
3235
3235
|
const pv = this.partVis(ev.object, ev.part);
|
|
@@ -3297,7 +3297,7 @@ var Renderer = class {
|
|
|
3297
3297
|
break;
|
|
3298
3298
|
}
|
|
3299
3299
|
case "explode":
|
|
3300
|
-
this.primedBurning.delete(`${ev.object}|${ev.part}`);
|
|
3300
|
+
if (this.primedBurning.delete(`${ev.object}|${ev.part}`)) this.sound("burn_end", ev.point, 0.5, { fuel: "gas" });
|
|
3301
3301
|
this.onExplode(ev);
|
|
3302
3302
|
break;
|
|
3303
3303
|
case "destroyed":
|
|
@@ -3312,6 +3312,11 @@ var Renderer = class {
|
|
|
3312
3312
|
if (detail) this.o.onSound?.(key, point, volume, detail);
|
|
3313
3313
|
else this.o.onSound?.(key, point, volume);
|
|
3314
3314
|
}
|
|
3315
|
+
/** What a burning part is made of, for the fire's sound. */
|
|
3316
|
+
burning(objectId, partId) {
|
|
3317
|
+
const material = this.partVis(objectId, partId)?.material || this.world.part(objectId, partId)?.material;
|
|
3318
|
+
return material ? { material } : void 0;
|
|
3319
|
+
}
|
|
3315
3320
|
partVis(objectId, partId) {
|
|
3316
3321
|
return this.bounds.get(objectId)?.parts.get(partId) ?? null;
|
|
3317
3322
|
}
|
|
@@ -4187,6 +4192,47 @@ var FIRE_LOOP = {
|
|
|
4187
4192
|
volume: 0,
|
|
4188
4193
|
sources: ["sdk: the roar under the crackles of a fire"]
|
|
4189
4194
|
};
|
|
4195
|
+
var FIRE_FUEL_OF = {
|
|
4196
|
+
wood_soft: "wood",
|
|
4197
|
+
wood_hard: "wood",
|
|
4198
|
+
plywood: "wood",
|
|
4199
|
+
mdf: "wood",
|
|
4200
|
+
green_wood: "wood",
|
|
4201
|
+
foliage: "wood",
|
|
4202
|
+
cactus: "wood",
|
|
4203
|
+
paper: "fabric",
|
|
4204
|
+
fabric: "fabric",
|
|
4205
|
+
leather: "fabric",
|
|
4206
|
+
foam: "plastic",
|
|
4207
|
+
plastic: "plastic",
|
|
4208
|
+
rubber: "plastic",
|
|
4209
|
+
wax: "plastic"
|
|
4210
|
+
};
|
|
4211
|
+
var FIRE_LOOP_OF = {
|
|
4212
|
+
wood: "fire_loop",
|
|
4213
|
+
fabric: "fire_loop",
|
|
4214
|
+
plastic: "fire_loop_plastic",
|
|
4215
|
+
gas: "fire_loop_gas",
|
|
4216
|
+
liquid: "fire_loop_liquid"
|
|
4217
|
+
};
|
|
4218
|
+
var FIRE_CRACKLE_OF = { wood: 1, fabric: 0.5, plastic: 0.35, gas: 0, liquid: 0 };
|
|
4219
|
+
var roar = (id, bank, source) => ({
|
|
4220
|
+
id,
|
|
4221
|
+
bus: "world",
|
|
4222
|
+
order: "chain_loop",
|
|
4223
|
+
source: { kind: "noise", loop: true },
|
|
4224
|
+
filters: { mode: "serial", bank },
|
|
4225
|
+
volume: 0,
|
|
4226
|
+
sources: [source]
|
|
4227
|
+
});
|
|
4228
|
+
var FIRE_FUEL_LOOPS = [
|
|
4229
|
+
// a gas jet: a steady, broad roar, brighter than wood and without crackles
|
|
4230
|
+
roar("fire_loop_gas", [{ type: "highpass", frequency: { set: 220 }, q: 0.7 }, { type: "lowpass", frequency: { set: 2400 }, q: 0.6 }], "sdk: a gas jet burning (a vessel venting into the flame)"),
|
|
4231
|
+
// a fuel pool: a soft, low, rolling roar
|
|
4232
|
+
roar("fire_loop_liquid", [{ type: "highpass", frequency: { set: 90 }, q: 0.7 }, { type: "lowpass", frequency: { set: 650 }, q: 0.9 }], "sdk: a pool of fuel burning"),
|
|
4233
|
+
// plastic and rubber: a thin sizzle over a low burn
|
|
4234
|
+
roar("fire_loop_plastic", [{ type: "bandpass", frequency: { set: 1800 }, q: 0.6 }], "sdk: plastic and rubber melting in the fire")
|
|
4235
|
+
];
|
|
4190
4236
|
var BLAST_SOUND_OF = {
|
|
4191
4237
|
grenade: "explosion_grenade",
|
|
4192
4238
|
big: "explosion_big",
|
|
@@ -4333,7 +4379,7 @@ var IMPACT_SOUNDS = [
|
|
|
4333
4379
|
impact("plastic_crack", [noise2({ type: "bandpass", f: 1800, q: 2, d: 0.06, vol: 0.75 }), tone2({ f: [450, 550], f2: 250, d: 0.05, vol: 0.22 })]),
|
|
4334
4380
|
impact("mdf_crumble", [noise2({ type: "lowpass", f: 700, d: 0.25, vol: 0.75, wet: 0.2 }), ...many2(4, () => noise2({ type: "lowpass", f: 500, d: 0.05, vol: 0.32, delay: [0.05, 0.3] }))])
|
|
4335
4381
|
];
|
|
4336
|
-
var DESTRUCTION_SOUND_BANK = { id: "destruction_three", sounds: [...SCENE_SOUNDS, ...DRAG_SOUNDS, ...LEAF_SOUNDS, ...IMPACT_SOUNDS, EXPLOSION_BOOM, ...BLAST_SOUNDS, ...FIRE_SOUNDS], loops: [BURN_LOOP, FIRE_LOOP] };
|
|
4382
|
+
var DESTRUCTION_SOUND_BANK = { id: "destruction_three", sounds: [...SCENE_SOUNDS, ...DRAG_SOUNDS, ...LEAF_SOUNDS, ...IMPACT_SOUNDS, EXPLOSION_BOOM, ...BLAST_SOUNDS, ...FIRE_SOUNDS], loops: [BURN_LOOP, FIRE_LOOP, ...FIRE_FUEL_LOOPS] };
|
|
4337
4383
|
|
|
4338
4384
|
// src/audio/sound.ts
|
|
4339
4385
|
import { FOLEY_BANK, GAME_SFX_BANK } from "@adatechnology/game-audio";
|
|
@@ -4485,31 +4531,52 @@ function createDestructionSound(engine, options = {}) {
|
|
|
4485
4531
|
const rules = { ...DESTRUCTION_SOUND_KEYS.art_scene, ...options.style === "game" ? DESTRUCTION_SOUND_KEYS.game : {}, ...options.keys };
|
|
4486
4532
|
const blastByKind = options.style !== "game" && !(options.keys && "explode" in options.keys);
|
|
4487
4533
|
const channel = engine.channel({ seed: options.seed ?? 1 });
|
|
4488
|
-
|
|
4489
|
-
|
|
4534
|
+
const fuelOf = { ...FIRE_FUEL_OF, ...options.fuels };
|
|
4535
|
+
const loopOf = { ...FIRE_LOOP_OF, ...options.fireLoop ? { wood: fireLoop, fabric: fireLoop } : {}, ...options.fireLoops };
|
|
4536
|
+
const defaultFuel = options.defaultFuel ?? "wood";
|
|
4537
|
+
const loops = /* @__PURE__ */ new Map();
|
|
4490
4538
|
const fires = [];
|
|
4491
4539
|
let nextCrackle = 0;
|
|
4540
|
+
const fuelFor = (detail) => detail?.fuel ?? (detail?.material !== void 0 ? fuelOf[detail.material] : void 0) ?? defaultFuel;
|
|
4492
4541
|
const setFire = () => {
|
|
4493
4542
|
if (options.burnLoop === false) return;
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4543
|
+
const count = /* @__PURE__ */ new Map();
|
|
4544
|
+
for (const f of fires) {
|
|
4545
|
+
const id = loopOf[f.fuel];
|
|
4546
|
+
count.set(id, (count.get(id) ?? 0) + 1);
|
|
4547
|
+
}
|
|
4548
|
+
for (const [id, handle] of loops) {
|
|
4549
|
+
if (count.has(id)) continue;
|
|
4550
|
+
handle.stop();
|
|
4551
|
+
loops.delete(id);
|
|
4552
|
+
}
|
|
4553
|
+
for (const [id, n] of count) {
|
|
4554
|
+
let handle = loops.get(id);
|
|
4555
|
+
if (!handle) {
|
|
4556
|
+
handle = engine.loop(id);
|
|
4557
|
+
loops.set(id, handle);
|
|
4558
|
+
}
|
|
4559
|
+
handle.set({ volume: Math.min(fire2.max, fire2.perFire * n) });
|
|
4498
4560
|
}
|
|
4499
|
-
loop ?? (loop = engine.loop(fireLoop));
|
|
4500
|
-
loop.set({ volume: Math.min(fire2.max, fire2.perFire * burning) });
|
|
4501
4561
|
};
|
|
4502
|
-
const forget = (point) => {
|
|
4562
|
+
const forget = (point, fuel) => {
|
|
4503
4563
|
let best = -1;
|
|
4504
4564
|
let bestD = Infinity;
|
|
4505
4565
|
fires.forEach((f, i) => {
|
|
4506
|
-
|
|
4566
|
+
if (fuel !== void 0 && f.fuel !== fuel) return;
|
|
4567
|
+
const d = (f.point[0] - point[0]) ** 2 + (f.point[1] - point[1]) ** 2 + (f.point[2] - point[2]) ** 2;
|
|
4507
4568
|
if (d < bestD) {
|
|
4508
4569
|
bestD = d;
|
|
4509
4570
|
best = i;
|
|
4510
4571
|
}
|
|
4511
4572
|
});
|
|
4512
|
-
if (best
|
|
4573
|
+
if (best < 0 && fuel !== void 0) forget(point, void 0);
|
|
4574
|
+
else if (best >= 0) fires.splice(best, 1);
|
|
4575
|
+
};
|
|
4576
|
+
const reset = () => {
|
|
4577
|
+
for (const handle of loops.values()) handle.stop();
|
|
4578
|
+
loops.clear();
|
|
4579
|
+
fires.length = 0;
|
|
4513
4580
|
};
|
|
4514
4581
|
const blastSound = (point, volume, detail) => {
|
|
4515
4582
|
const named = detail?.effect === void 0 ? void 0 : kinds[detail.effect];
|
|
@@ -4521,14 +4588,12 @@ function createDestructionSound(engine, options = {}) {
|
|
|
4521
4588
|
return {
|
|
4522
4589
|
onSound(key, point, volume, detail) {
|
|
4523
4590
|
if (key === "burn_loop") {
|
|
4524
|
-
|
|
4525
|
-
fires.push(point);
|
|
4591
|
+
fires.push({ point, fuel: fuelFor(detail) });
|
|
4526
4592
|
setFire();
|
|
4527
4593
|
return;
|
|
4528
4594
|
}
|
|
4529
4595
|
if (key === "burn_end") {
|
|
4530
|
-
|
|
4531
|
-
forget(point);
|
|
4596
|
+
forget(point, detail?.fuel ?? (detail?.material !== void 0 ? fuelOf[detail.material] : void 0));
|
|
4532
4597
|
setFire();
|
|
4533
4598
|
return;
|
|
4534
4599
|
}
|
|
@@ -4558,12 +4623,15 @@ function createDestructionSound(engine, options = {}) {
|
|
|
4558
4623
|
},
|
|
4559
4624
|
update(dt) {
|
|
4560
4625
|
if (!crackles || fires.length === 0 || options.burnLoop === false) return;
|
|
4626
|
+
const weight = fires.reduce((sum, f) => sum + FIRE_CRACKLE_OF[f.fuel], 0);
|
|
4627
|
+
if (weight <= 0) return;
|
|
4561
4628
|
nextCrackle -= dt;
|
|
4562
4629
|
for (let i = 0; i < 4 && nextCrackle <= 0; i++) {
|
|
4563
|
-
const rate = Math.min(crackles.max, crackles.base + crackles.perSqrtFire * Math.sqrt(
|
|
4630
|
+
const rate = Math.min(crackles.max, crackles.base * Math.min(1, weight) + crackles.perSqrtFire * Math.sqrt(weight));
|
|
4564
4631
|
nextCrackle += (0.4 + channel.random() * 1.2) / rate;
|
|
4565
|
-
|
|
4566
|
-
|
|
4632
|
+
let pick = channel.random() * weight;
|
|
4633
|
+
const at = fires.find((f) => (pick -= FIRE_CRACKLE_OF[f.fuel]) <= 0) ?? fires[fires.length - 1];
|
|
4634
|
+
engine.play(channel.random() < 0.18 ? "fire_pop" : "fire_crackle", { position: at.point, volume: crackles.volume });
|
|
4567
4635
|
}
|
|
4568
4636
|
if (nextCrackle < 0) nextCrackle = 0;
|
|
4569
4637
|
},
|
|
@@ -4572,13 +4640,14 @@ function createDestructionSound(engine, options = {}) {
|
|
|
4572
4640
|
for (const k of detail?.keys ?? []) if (k in rules) return rules[k] ?? void 0;
|
|
4573
4641
|
return rules[key] ?? void 0;
|
|
4574
4642
|
},
|
|
4575
|
-
burning: () =>
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4643
|
+
burning: () => fires.length,
|
|
4644
|
+
burningBy: () => {
|
|
4645
|
+
const by = {};
|
|
4646
|
+
for (const f of fires) by[f.fuel] = (by[f.fuel] ?? 0) + 1;
|
|
4647
|
+
return by;
|
|
4648
|
+
},
|
|
4649
|
+
reset,
|
|
4650
|
+
dispose: reset
|
|
4582
4651
|
};
|
|
4583
4652
|
}
|
|
4584
4653
|
|
|
@@ -4600,7 +4669,11 @@ var BLAST_FIRE_SAMPLES = {
|
|
|
4600
4669
|
fire_extinguish: fire(0.7),
|
|
4601
4670
|
gas_leak: fire(0.5),
|
|
4602
4671
|
// the roar: recorded fires chained with a crossfade (the level comes from `createDestructionSound`, by how much burns)
|
|
4603
|
-
fire_loop: { bus: "world", gain: 4, loop: { crossfade: 1 } }
|
|
4672
|
+
fire_loop: { bus: "world", gain: 4, loop: { crossfade: 1 } },
|
|
4673
|
+
// each fuel has its own roar: a gas jet (burners and torches), a pool of fuel, plastic and rubber sizzling under the flames
|
|
4674
|
+
fire_loop_gas: { bus: "world", gain: 6, loop: { crossfade: 1 } },
|
|
4675
|
+
fire_loop_liquid: { bus: "world", gain: 3.5, loop: { crossfade: 1 } },
|
|
4676
|
+
fire_loop_plastic: { bus: "world", gain: 1, loop: { crossfade: 0.6 } }
|
|
4604
4677
|
};
|
|
4605
4678
|
function blastFireSampleBank(manifest, options = {}) {
|
|
4606
4679
|
const sounds = options.sounds ?? BLAST_FIRE_SAMPLES;
|