@adatechnology/game-destruction-three 0.14.0 → 0.15.1
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 +1 -1
- package/dist/index.d.ts +21 -1
- package/dist/index.global.js +199 -103
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +111 -42
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2480,7 +2480,6 @@ var Visuals = class {
|
|
|
2480
2480
|
var rendererCount = 0;
|
|
2481
2481
|
var COLLAPSE_CLOUD_KG = 1e3;
|
|
2482
2482
|
var CHIP_DECAL_EDGE = 0.05;
|
|
2483
|
-
var OPAQUE_CRACK = 0.1;
|
|
2484
2483
|
var WOOD_RIM = 1.5;
|
|
2485
2484
|
var IMPACT_TAGS = ["metal", "glass", "wood"];
|
|
2486
2485
|
var GLOW_FROM_C = 430;
|
|
@@ -3187,7 +3186,7 @@ var Renderer = class {
|
|
|
3187
3186
|
this.startFire(ev);
|
|
3188
3187
|
this.activate(ev.object, ev.part);
|
|
3189
3188
|
this.sound("ignite", ev.point, 0.7);
|
|
3190
|
-
this.sound("burn_loop", ev.point, 0.5);
|
|
3189
|
+
this.sound("burn_loop", ev.point, 0.5, this.burning(ev.object, ev.part));
|
|
3191
3190
|
break;
|
|
3192
3191
|
case "primed":
|
|
3193
3192
|
if (ev.trigger !== "explosion") {
|
|
@@ -3195,7 +3194,7 @@ var Renderer = class {
|
|
|
3195
3194
|
this.primedBurning.add(`${ev.object}|${ev.part}`);
|
|
3196
3195
|
this.activate(ev.object, ev.part);
|
|
3197
3196
|
this.sound("primed", ev.point, 0.7, { seconds: ev.delay });
|
|
3198
|
-
this.sound("burn_loop", ev.point, 0.5);
|
|
3197
|
+
this.sound("burn_loop", ev.point, 0.5, { fuel: "gas" });
|
|
3199
3198
|
}
|
|
3200
3199
|
break;
|
|
3201
3200
|
case "spall":
|
|
@@ -3221,15 +3220,15 @@ var Renderer = class {
|
|
|
3221
3220
|
this.stopFire(`${ev.object}|${ev.part}`);
|
|
3222
3221
|
this.vfx.burst("steam", f.point, [0, 1, 0], 14, 1.2);
|
|
3223
3222
|
}
|
|
3224
|
-
if (f || primed) this.sound("burn_end", f?.point ?? this.rootPoint(ev.object), 0.5);
|
|
3223
|
+
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
3224
|
this.activate(ev.object, ev.part);
|
|
3226
3225
|
this.sound("extinguish", f?.point ?? this.rootPoint(ev.object), 0.6);
|
|
3227
3226
|
break;
|
|
3228
3227
|
}
|
|
3229
3228
|
case "burnout": {
|
|
3230
3229
|
this.stopFire(`${ev.object}|${ev.part}`);
|
|
3231
|
-
this.primedBurning.delete(`${ev.object}|${ev.part}`);
|
|
3232
|
-
this.sound("burn_end", ev.point, 0.5);
|
|
3230
|
+
const primed = this.primedBurning.delete(`${ev.object}|${ev.part}`);
|
|
3231
|
+
this.sound("burn_end", ev.point, 0.5, primed ? { fuel: "gas" } : this.burning(ev.object, ev.part));
|
|
3233
3232
|
this.vfx.burst("ash", ev.point, [0, 1, 0], 16, 1);
|
|
3234
3233
|
this.vfx.burst("smoke", ev.point, [0, 1, 0], 6, 0.8, { color: SMOKE_TONES.grey });
|
|
3235
3234
|
const pv = this.partVis(ev.object, ev.part);
|
|
@@ -3297,7 +3296,7 @@ var Renderer = class {
|
|
|
3297
3296
|
break;
|
|
3298
3297
|
}
|
|
3299
3298
|
case "explode":
|
|
3300
|
-
this.primedBurning.delete(`${ev.object}|${ev.part}`);
|
|
3299
|
+
if (this.primedBurning.delete(`${ev.object}|${ev.part}`)) this.sound("burn_end", ev.point, 0.5, { fuel: "gas" });
|
|
3301
3300
|
this.onExplode(ev);
|
|
3302
3301
|
break;
|
|
3303
3302
|
case "destroyed":
|
|
@@ -3312,6 +3311,11 @@ var Renderer = class {
|
|
|
3312
3311
|
if (detail) this.o.onSound?.(key, point, volume, detail);
|
|
3313
3312
|
else this.o.onSound?.(key, point, volume);
|
|
3314
3313
|
}
|
|
3314
|
+
/** What a burning part is made of, for the fire's sound. */
|
|
3315
|
+
burning(objectId, partId) {
|
|
3316
|
+
const material = this.partVis(objectId, partId)?.material || this.world.part(objectId, partId)?.material;
|
|
3317
|
+
return material ? { material } : void 0;
|
|
3318
|
+
}
|
|
3315
3319
|
partVis(objectId, partId) {
|
|
3316
3320
|
return this.bounds.get(objectId)?.parts.get(partId) ?? null;
|
|
3317
3321
|
}
|
|
@@ -3411,14 +3415,11 @@ var Renderer = class {
|
|
|
3411
3415
|
const pv = b?.parts.get(ev.part);
|
|
3412
3416
|
if (!b || !pv) return;
|
|
3413
3417
|
const def = this.world.materials.get(pv.material);
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
const
|
|
3418
|
-
|
|
3419
|
-
const color = glass2 ? 15267071 : new this.THREE.Color(own).multiplyScalar(0.45).getHex();
|
|
3420
|
-
const length = glass2 ? ev.crack.length : Math.min(ev.crack.length, OPAQUE_CRACK);
|
|
3421
|
-
this.visuals.addCrack(mv, wp, this.worldDir(b, ev.crack.normal), ev.crack.rays, length, color, hashNumbers(ev.time, ev.worldPoint[0], ev.worldPoint[1], ev.worldPoint[2]));
|
|
3418
|
+
if (def?.visual?.transparent) {
|
|
3419
|
+
const wp = new this.THREE.Vector3(ev.worldPoint[0], ev.worldPoint[1], ev.worldPoint[2]);
|
|
3420
|
+
const mv = this.nearestMesh(pv, wp);
|
|
3421
|
+
const seed = hashNumbers(ev.time, ev.worldPoint[0], ev.worldPoint[1], ev.worldPoint[2]);
|
|
3422
|
+
if (mv) this.visuals.addCrack(mv, wp, this.worldDir(b, ev.crack.normal), ev.crack.rays, ev.crack.length, 15267071, seed);
|
|
3422
3423
|
}
|
|
3423
3424
|
this.impactSound(pv.material, ev.worldPoint, 0.5, ev.blow);
|
|
3424
3425
|
}
|
|
@@ -4187,6 +4188,47 @@ var FIRE_LOOP = {
|
|
|
4187
4188
|
volume: 0,
|
|
4188
4189
|
sources: ["sdk: the roar under the crackles of a fire"]
|
|
4189
4190
|
};
|
|
4191
|
+
var FIRE_FUEL_OF = {
|
|
4192
|
+
wood_soft: "wood",
|
|
4193
|
+
wood_hard: "wood",
|
|
4194
|
+
plywood: "wood",
|
|
4195
|
+
mdf: "wood",
|
|
4196
|
+
green_wood: "wood",
|
|
4197
|
+
foliage: "wood",
|
|
4198
|
+
cactus: "wood",
|
|
4199
|
+
paper: "fabric",
|
|
4200
|
+
fabric: "fabric",
|
|
4201
|
+
leather: "fabric",
|
|
4202
|
+
foam: "plastic",
|
|
4203
|
+
plastic: "plastic",
|
|
4204
|
+
rubber: "plastic",
|
|
4205
|
+
wax: "plastic"
|
|
4206
|
+
};
|
|
4207
|
+
var FIRE_LOOP_OF = {
|
|
4208
|
+
wood: "fire_loop",
|
|
4209
|
+
fabric: "fire_loop",
|
|
4210
|
+
plastic: "fire_loop_plastic",
|
|
4211
|
+
gas: "fire_loop_gas",
|
|
4212
|
+
liquid: "fire_loop_liquid"
|
|
4213
|
+
};
|
|
4214
|
+
var FIRE_CRACKLE_OF = { wood: 1, fabric: 0.5, plastic: 0.35, gas: 0, liquid: 0 };
|
|
4215
|
+
var roar = (id, bank, source) => ({
|
|
4216
|
+
id,
|
|
4217
|
+
bus: "world",
|
|
4218
|
+
order: "chain_loop",
|
|
4219
|
+
source: { kind: "noise", loop: true },
|
|
4220
|
+
filters: { mode: "serial", bank },
|
|
4221
|
+
volume: 0,
|
|
4222
|
+
sources: [source]
|
|
4223
|
+
});
|
|
4224
|
+
var FIRE_FUEL_LOOPS = [
|
|
4225
|
+
// a gas jet: a steady, broad roar, brighter than wood and without crackles
|
|
4226
|
+
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)"),
|
|
4227
|
+
// a fuel pool: a soft, low, rolling roar
|
|
4228
|
+
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"),
|
|
4229
|
+
// plastic and rubber: a thin sizzle over a low burn
|
|
4230
|
+
roar("fire_loop_plastic", [{ type: "bandpass", frequency: { set: 1800 }, q: 0.6 }], "sdk: plastic and rubber melting in the fire")
|
|
4231
|
+
];
|
|
4190
4232
|
var BLAST_SOUND_OF = {
|
|
4191
4233
|
grenade: "explosion_grenade",
|
|
4192
4234
|
big: "explosion_big",
|
|
@@ -4333,7 +4375,7 @@ var IMPACT_SOUNDS = [
|
|
|
4333
4375
|
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
4376
|
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
4377
|
];
|
|
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] };
|
|
4378
|
+
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
4379
|
|
|
4338
4380
|
// src/audio/sound.ts
|
|
4339
4381
|
import { FOLEY_BANK, GAME_SFX_BANK } from "@adatechnology/game-audio";
|
|
@@ -4485,31 +4527,52 @@ function createDestructionSound(engine, options = {}) {
|
|
|
4485
4527
|
const rules = { ...DESTRUCTION_SOUND_KEYS.art_scene, ...options.style === "game" ? DESTRUCTION_SOUND_KEYS.game : {}, ...options.keys };
|
|
4486
4528
|
const blastByKind = options.style !== "game" && !(options.keys && "explode" in options.keys);
|
|
4487
4529
|
const channel = engine.channel({ seed: options.seed ?? 1 });
|
|
4488
|
-
|
|
4489
|
-
|
|
4530
|
+
const fuelOf = { ...FIRE_FUEL_OF, ...options.fuels };
|
|
4531
|
+
const loopOf = { ...FIRE_LOOP_OF, ...options.fireLoop ? { wood: fireLoop, fabric: fireLoop } : {}, ...options.fireLoops };
|
|
4532
|
+
const defaultFuel = options.defaultFuel ?? "wood";
|
|
4533
|
+
const loops = /* @__PURE__ */ new Map();
|
|
4490
4534
|
const fires = [];
|
|
4491
4535
|
let nextCrackle = 0;
|
|
4536
|
+
const fuelFor = (detail) => detail?.fuel ?? (detail?.material !== void 0 ? fuelOf[detail.material] : void 0) ?? defaultFuel;
|
|
4492
4537
|
const setFire = () => {
|
|
4493
4538
|
if (options.burnLoop === false) return;
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4539
|
+
const count = /* @__PURE__ */ new Map();
|
|
4540
|
+
for (const f of fires) {
|
|
4541
|
+
const id = loopOf[f.fuel];
|
|
4542
|
+
count.set(id, (count.get(id) ?? 0) + 1);
|
|
4543
|
+
}
|
|
4544
|
+
for (const [id, handle] of loops) {
|
|
4545
|
+
if (count.has(id)) continue;
|
|
4546
|
+
handle.stop();
|
|
4547
|
+
loops.delete(id);
|
|
4548
|
+
}
|
|
4549
|
+
for (const [id, n] of count) {
|
|
4550
|
+
let handle = loops.get(id);
|
|
4551
|
+
if (!handle) {
|
|
4552
|
+
handle = engine.loop(id);
|
|
4553
|
+
loops.set(id, handle);
|
|
4554
|
+
}
|
|
4555
|
+
handle.set({ volume: Math.min(fire2.max, fire2.perFire * n) });
|
|
4498
4556
|
}
|
|
4499
|
-
loop ?? (loop = engine.loop(fireLoop));
|
|
4500
|
-
loop.set({ volume: Math.min(fire2.max, fire2.perFire * burning) });
|
|
4501
4557
|
};
|
|
4502
|
-
const forget = (point) => {
|
|
4558
|
+
const forget = (point, fuel) => {
|
|
4503
4559
|
let best = -1;
|
|
4504
4560
|
let bestD = Infinity;
|
|
4505
4561
|
fires.forEach((f, i) => {
|
|
4506
|
-
|
|
4562
|
+
if (fuel !== void 0 && f.fuel !== fuel) return;
|
|
4563
|
+
const d = (f.point[0] - point[0]) ** 2 + (f.point[1] - point[1]) ** 2 + (f.point[2] - point[2]) ** 2;
|
|
4507
4564
|
if (d < bestD) {
|
|
4508
4565
|
bestD = d;
|
|
4509
4566
|
best = i;
|
|
4510
4567
|
}
|
|
4511
4568
|
});
|
|
4512
|
-
if (best
|
|
4569
|
+
if (best < 0 && fuel !== void 0) forget(point, void 0);
|
|
4570
|
+
else if (best >= 0) fires.splice(best, 1);
|
|
4571
|
+
};
|
|
4572
|
+
const reset = () => {
|
|
4573
|
+
for (const handle of loops.values()) handle.stop();
|
|
4574
|
+
loops.clear();
|
|
4575
|
+
fires.length = 0;
|
|
4513
4576
|
};
|
|
4514
4577
|
const blastSound = (point, volume, detail) => {
|
|
4515
4578
|
const named = detail?.effect === void 0 ? void 0 : kinds[detail.effect];
|
|
@@ -4521,14 +4584,12 @@ function createDestructionSound(engine, options = {}) {
|
|
|
4521
4584
|
return {
|
|
4522
4585
|
onSound(key, point, volume, detail) {
|
|
4523
4586
|
if (key === "burn_loop") {
|
|
4524
|
-
|
|
4525
|
-
fires.push(point);
|
|
4587
|
+
fires.push({ point, fuel: fuelFor(detail) });
|
|
4526
4588
|
setFire();
|
|
4527
4589
|
return;
|
|
4528
4590
|
}
|
|
4529
4591
|
if (key === "burn_end") {
|
|
4530
|
-
|
|
4531
|
-
forget(point);
|
|
4592
|
+
forget(point, detail?.fuel ?? (detail?.material !== void 0 ? fuelOf[detail.material] : void 0));
|
|
4532
4593
|
setFire();
|
|
4533
4594
|
return;
|
|
4534
4595
|
}
|
|
@@ -4558,12 +4619,15 @@ function createDestructionSound(engine, options = {}) {
|
|
|
4558
4619
|
},
|
|
4559
4620
|
update(dt) {
|
|
4560
4621
|
if (!crackles || fires.length === 0 || options.burnLoop === false) return;
|
|
4622
|
+
const weight = fires.reduce((sum, f) => sum + FIRE_CRACKLE_OF[f.fuel], 0);
|
|
4623
|
+
if (weight <= 0) return;
|
|
4561
4624
|
nextCrackle -= dt;
|
|
4562
4625
|
for (let i = 0; i < 4 && nextCrackle <= 0; i++) {
|
|
4563
|
-
const rate = Math.min(crackles.max, crackles.base + crackles.perSqrtFire * Math.sqrt(
|
|
4626
|
+
const rate = Math.min(crackles.max, crackles.base * Math.min(1, weight) + crackles.perSqrtFire * Math.sqrt(weight));
|
|
4564
4627
|
nextCrackle += (0.4 + channel.random() * 1.2) / rate;
|
|
4565
|
-
|
|
4566
|
-
|
|
4628
|
+
let pick = channel.random() * weight;
|
|
4629
|
+
const at = fires.find((f) => (pick -= FIRE_CRACKLE_OF[f.fuel]) <= 0) ?? fires[fires.length - 1];
|
|
4630
|
+
engine.play(channel.random() < 0.18 ? "fire_pop" : "fire_crackle", { position: at.point, volume: crackles.volume });
|
|
4567
4631
|
}
|
|
4568
4632
|
if (nextCrackle < 0) nextCrackle = 0;
|
|
4569
4633
|
},
|
|
@@ -4572,13 +4636,14 @@ function createDestructionSound(engine, options = {}) {
|
|
|
4572
4636
|
for (const k of detail?.keys ?? []) if (k in rules) return rules[k] ?? void 0;
|
|
4573
4637
|
return rules[key] ?? void 0;
|
|
4574
4638
|
},
|
|
4575
|
-
burning: () =>
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4639
|
+
burning: () => fires.length,
|
|
4640
|
+
burningBy: () => {
|
|
4641
|
+
const by = {};
|
|
4642
|
+
for (const f of fires) by[f.fuel] = (by[f.fuel] ?? 0) + 1;
|
|
4643
|
+
return by;
|
|
4644
|
+
},
|
|
4645
|
+
reset,
|
|
4646
|
+
dispose: reset
|
|
4582
4647
|
};
|
|
4583
4648
|
}
|
|
4584
4649
|
|
|
@@ -4600,7 +4665,11 @@ var BLAST_FIRE_SAMPLES = {
|
|
|
4600
4665
|
fire_extinguish: fire(0.7),
|
|
4601
4666
|
gas_leak: fire(0.5),
|
|
4602
4667
|
// 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 } }
|
|
4668
|
+
fire_loop: { bus: "world", gain: 4, loop: { crossfade: 1 } },
|
|
4669
|
+
// each fuel has its own roar: a gas jet (burners and torches), a pool of fuel, plastic and rubber sizzling under the flames
|
|
4670
|
+
fire_loop_gas: { bus: "world", gain: 6, loop: { crossfade: 1 } },
|
|
4671
|
+
fire_loop_liquid: { bus: "world", gain: 3.5, loop: { crossfade: 1 } },
|
|
4672
|
+
fire_loop_plastic: { bus: "world", gain: 1, loop: { crossfade: 0.6 } }
|
|
4604
4673
|
};
|
|
4605
4674
|
function blastFireSampleBank(manifest, options = {}) {
|
|
4606
4675
|
const sounds = options.sounds ?? BLAST_FIRE_SAMPLES;
|