@adatechnology/game-destruction-three 0.4.0 → 0.6.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 +5 -5
- package/dist/index.d.ts +94 -8
- package/dist/index.global.js +8363 -548
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +326 -461
- package/dist/index.js.map +1 -1
- package/package.json +8 -4
package/dist/index.js
CHANGED
|
@@ -2,6 +2,10 @@ var __defProp = Object.defineProperty;
|
|
|
2
2
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
3
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
4
|
|
|
5
|
+
// src/renderer.ts
|
|
6
|
+
import { createVfx, effectForEvent, EVENT_EFFECTS_MAP } from "@adatechnology/game-vfx";
|
|
7
|
+
import { createVfxRenderer } from "@adatechnology/game-vfx-three";
|
|
8
|
+
|
|
5
9
|
// src/math.ts
|
|
6
10
|
function createRng(seed) {
|
|
7
11
|
let a = seed >>> 0;
|
|
@@ -29,9 +33,6 @@ function hashString(s) {
|
|
|
29
33
|
}
|
|
30
34
|
var clamp = (v, lo, hi) => v < lo ? lo : v > hi ? hi : v;
|
|
31
35
|
var lerp = (a, b, t) => a + (b - a) * t;
|
|
32
|
-
function hexToRgb(hex) {
|
|
33
|
-
return [(hex >> 16 & 255) / 255, (hex >> 8 & 255) / 255, (hex & 255) / 255];
|
|
34
|
-
}
|
|
35
36
|
function mixHex(a, b, t) {
|
|
36
37
|
const ar = a >> 16 & 255;
|
|
37
38
|
const ag = a >> 8 & 255;
|
|
@@ -294,9 +295,9 @@ var FragmentSystem = class {
|
|
|
294
295
|
// src/ground.ts
|
|
295
296
|
var MAX_RADIUS = 1.2;
|
|
296
297
|
var GroundSpots = class {
|
|
297
|
-
constructor(THREE,
|
|
298
|
+
constructor(THREE, scene2, groundAt, cap) {
|
|
298
299
|
__publicField(this, "THREE", THREE);
|
|
299
|
-
__publicField(this, "scene",
|
|
300
|
+
__publicField(this, "scene", scene2);
|
|
300
301
|
__publicField(this, "groundAt", groundAt);
|
|
301
302
|
__publicField(this, "cap", cap);
|
|
302
303
|
__publicField(this, "spots", []);
|
|
@@ -372,345 +373,6 @@ var GroundSpots = class {
|
|
|
372
373
|
}
|
|
373
374
|
};
|
|
374
375
|
|
|
375
|
-
// src/particles.ts
|
|
376
|
-
var KINDS = {
|
|
377
|
-
fire: { additive: true, life: [0.45, 0.9], size: [0.1, 0.2], grow: 0.3, gravity: 0, rise: 2.4, drag: 1.2, c0: 16760896, c1: 12591104, alpha: 0.9, fadeIn: 0.12, cone: 0.7, ground: "none", flicker: 0.35 },
|
|
378
|
-
smoke: { additive: false, life: [1.8, 3.2], size: [0.14, 0.24], grow: 3.2, gravity: 0, rise: 0.7, drag: 0.8, c0: 6710886, c1: 10132122, alpha: 0.35, fadeIn: 0.15, cone: 0.6, ground: "none" },
|
|
379
|
-
sparks: { additive: true, life: [0.25, 0.6], size: [0.018, 0.03], grow: 0.5, gravity: 1, rise: 0, drag: 0.3, c0: 16773792, c1: 16738832, alpha: 1, fadeIn: 0.02, cone: 0.55, ground: "bounce" },
|
|
380
|
-
dust: { additive: false, life: [1, 2], size: [0.08, 0.14], grow: 2.5, gravity: 0.05, rise: 0, drag: 2.5, c0: 9274745, c1: 9274745, alpha: 0.4, fadeIn: 0.1, cone: 0.4, ground: "none" },
|
|
381
|
-
shards: { additive: true, life: [0.8, 1.4], size: [0.02, 0.035], grow: 0.8, gravity: 1, rise: 0, drag: 0.2, c0: 14677247, c1: 10473727, alpha: 0.9, fadeIn: 0.03, cone: 0.5, ground: "bounce", flicker: 0.5 },
|
|
382
|
-
splinters: { additive: false, life: [0.8, 1.5], size: [0.02, 0.035], grow: 1, gravity: 1, rise: 0, drag: 0.3, c0: 10516042, c1: 8017200, alpha: 1, fadeIn: 0.02, cone: 0.5, ground: "bounce" },
|
|
383
|
-
chips: { additive: false, life: [0.8, 1.5], size: [0.025, 0.04], grow: 1, gravity: 1, rise: 0, drag: 0.3, c0: 9077624, c1: 7235679, alpha: 1, fadeIn: 0.02, cone: 0.5, ground: "bounce" },
|
|
384
|
-
fibers: { additive: false, life: [1.5, 2.5], size: [0.02, 0.03], grow: 1, gravity: 0.15, rise: 0, drag: 2, c0: 12101768, c1: 12101768, alpha: 0.9, fadeIn: 0.05, cone: 0.4, ground: "stop", flutter: 2.5 },
|
|
385
|
-
leaves: { additive: false, life: [1.5, 2.8], size: [0.03, 0.05], grow: 1, gravity: 0.15, rise: 0, drag: 2, c0: 6003260, c1: 4882224, alpha: 0.95, fadeIn: 0.05, cone: 0.4, ground: "stop", flutter: 2.8 },
|
|
386
|
-
drips: { additive: false, life: [2, 3], size: [0.025, 0.035], grow: 1, gravity: 1, rise: 0, drag: 0, c0: 6974058, c1: 6974058, alpha: 1, fadeIn: 0.01, cone: 0.2, ground: "land" },
|
|
387
|
-
ash: { additive: false, life: [2, 4], size: [0.02, 0.04], grow: 1.2, gravity: 0.08, rise: 0, drag: 1.5, c0: 2762275, c1: 1710102, alpha: 0.8, fadeIn: 0.1, cone: 0.5, ground: "stop", flutter: 0.6 },
|
|
388
|
-
steam: { additive: false, life: [1, 2], size: [0.12, 0.2], grow: 3, gravity: 0, rise: 0.9, drag: 1, c0: 16777215, c1: 14739694, alpha: 0.35, fadeIn: 0.15, cone: 0.6, ground: "none" },
|
|
389
|
-
flash: { additive: true, life: [0.12, 0.2], size: [1.2, 1.8], grow: 1.6, gravity: 0, rise: 0, drag: 0, c0: 16774080, c1: 16748592, alpha: 1, fadeIn: 0.1, cone: 0, ground: "none" },
|
|
390
|
-
foam: { additive: false, life: [1, 1.8], size: [0.06, 0.1], grow: 1.5, gravity: 0.6, rise: 0, drag: 1.5, c0: 16054527, c1: 14542576, alpha: 0.8, fadeIn: 0.05, cone: 0.5, ground: "stop" }
|
|
391
|
-
};
|
|
392
|
-
var KIND_IDS = Object.keys(KINDS);
|
|
393
|
-
var KIND_INDEX = {};
|
|
394
|
-
KIND_IDS.forEach((k, i) => KIND_INDEX[k] = i);
|
|
395
|
-
var KIND_LIST = KIND_IDS.map((k) => KINDS[k]);
|
|
396
|
-
var VERT = `
|
|
397
|
-
uniform float uScale;
|
|
398
|
-
attribute float size;
|
|
399
|
-
attribute float alpha;
|
|
400
|
-
attribute vec3 pcolor;
|
|
401
|
-
varying float vAlpha;
|
|
402
|
-
varying vec3 vColor;
|
|
403
|
-
void main() {
|
|
404
|
-
vAlpha = alpha;
|
|
405
|
-
vColor = pcolor;
|
|
406
|
-
vec4 mv = modelViewMatrix * vec4(position, 1.0);
|
|
407
|
-
gl_PointSize = min(size * uScale / max(0.1, -mv.z), 256.0);
|
|
408
|
-
gl_Position = projectionMatrix * mv;
|
|
409
|
-
}`;
|
|
410
|
-
var FRAG = `
|
|
411
|
-
varying float vAlpha;
|
|
412
|
-
varying vec3 vColor;
|
|
413
|
-
void main() {
|
|
414
|
-
float d = length(gl_PointCoord - vec2(0.5)) * 2.0;
|
|
415
|
-
float a = (1.0 - smoothstep(0.35, 1.0, d)) * vAlpha;
|
|
416
|
-
if (a < 0.004) discard;
|
|
417
|
-
gl_FragColor = vec4(vColor, a);
|
|
418
|
-
}`;
|
|
419
|
-
var Pool = class {
|
|
420
|
-
constructor(THREE, capacity, additive, pointScale) {
|
|
421
|
-
__publicField(this, "capacity", capacity);
|
|
422
|
-
__publicField(this, "points");
|
|
423
|
-
__publicField(this, "geometry");
|
|
424
|
-
__publicField(this, "material");
|
|
425
|
-
__publicField(this, "live", 0);
|
|
426
|
-
__publicField(this, "next", 0);
|
|
427
|
-
__publicField(this, "high", 0);
|
|
428
|
-
__publicField(this, "pos");
|
|
429
|
-
__publicField(this, "col");
|
|
430
|
-
__publicField(this, "alp");
|
|
431
|
-
__publicField(this, "siz");
|
|
432
|
-
__publicField(this, "vel");
|
|
433
|
-
__publicField(this, "age");
|
|
434
|
-
__publicField(this, "life");
|
|
435
|
-
__publicField(this, "size0");
|
|
436
|
-
__publicField(this, "alive");
|
|
437
|
-
__publicField(this, "kind");
|
|
438
|
-
__publicField(this, "seed");
|
|
439
|
-
__publicField(this, "alphaMul");
|
|
440
|
-
__publicField(this, "tag");
|
|
441
|
-
__publicField(this, "colorHex");
|
|
442
|
-
__publicField(this, "rgbStart");
|
|
443
|
-
__publicField(this, "rgbEnd");
|
|
444
|
-
__publicField(this, "bounced");
|
|
445
|
-
const n = Math.max(1, capacity);
|
|
446
|
-
this.pos = new Float32Array(n * 3);
|
|
447
|
-
this.col = new Float32Array(n * 3);
|
|
448
|
-
this.alp = new Float32Array(n);
|
|
449
|
-
this.siz = new Float32Array(n);
|
|
450
|
-
this.vel = new Float32Array(n * 3);
|
|
451
|
-
this.age = new Float32Array(n);
|
|
452
|
-
this.life = new Float32Array(n);
|
|
453
|
-
this.size0 = new Float32Array(n);
|
|
454
|
-
this.alive = new Uint8Array(n);
|
|
455
|
-
this.kind = new Uint8Array(n);
|
|
456
|
-
this.seed = new Float32Array(n);
|
|
457
|
-
this.alphaMul = new Float32Array(n);
|
|
458
|
-
this.tag = new Int32Array(n);
|
|
459
|
-
this.colorHex = new Int32Array(n);
|
|
460
|
-
this.rgbStart = new Float32Array(n * 3);
|
|
461
|
-
this.rgbEnd = new Float32Array(n * 3);
|
|
462
|
-
this.bounced = new Uint8Array(n);
|
|
463
|
-
this.geometry = new THREE.BufferGeometry();
|
|
464
|
-
const attr = (a, size) => {
|
|
465
|
-
const at = new THREE.BufferAttribute(a, size);
|
|
466
|
-
at.setUsage(THREE.DynamicDrawUsage);
|
|
467
|
-
return at;
|
|
468
|
-
};
|
|
469
|
-
this.geometry.setAttribute("position", attr(this.pos, 3));
|
|
470
|
-
this.geometry.setAttribute("pcolor", attr(this.col, 3));
|
|
471
|
-
this.geometry.setAttribute("alpha", attr(this.alp, 1));
|
|
472
|
-
this.geometry.setAttribute("size", attr(this.siz, 1));
|
|
473
|
-
this.geometry.setDrawRange(0, 0);
|
|
474
|
-
this.material = new THREE.ShaderMaterial({
|
|
475
|
-
uniforms: { uScale: { value: pointScale } },
|
|
476
|
-
vertexShader: VERT,
|
|
477
|
-
fragmentShader: FRAG,
|
|
478
|
-
transparent: true,
|
|
479
|
-
depthWrite: false,
|
|
480
|
-
blending: additive ? THREE.AdditiveBlending : THREE.NormalBlending
|
|
481
|
-
});
|
|
482
|
-
this.points = new THREE.Points(this.geometry, this.material);
|
|
483
|
-
this.points.frustumCulled = false;
|
|
484
|
-
this.points.name = additive ? "destruction-particles-additive" : "destruction-particles";
|
|
485
|
-
this.points.renderOrder = additive ? 11 : 10;
|
|
486
|
-
}
|
|
487
|
-
spawn(kind, x, y, z, vx, vy, vz, def, rnd, o) {
|
|
488
|
-
if (this.capacity <= 0) return;
|
|
489
|
-
const i = this.next;
|
|
490
|
-
this.next = (this.next + 1) % this.capacity;
|
|
491
|
-
if (this.alive[i]) this.live--;
|
|
492
|
-
this.alive[i] = 1;
|
|
493
|
-
this.live++;
|
|
494
|
-
if (i + 1 > this.high) this.high = i + 1;
|
|
495
|
-
this.kind[i] = kind;
|
|
496
|
-
this.pos[i * 3] = x;
|
|
497
|
-
this.pos[i * 3 + 1] = y;
|
|
498
|
-
this.pos[i * 3 + 2] = z;
|
|
499
|
-
this.vel[i * 3] = vx;
|
|
500
|
-
this.vel[i * 3 + 1] = vy;
|
|
501
|
-
this.vel[i * 3 + 2] = vz;
|
|
502
|
-
this.age[i] = 0;
|
|
503
|
-
this.life[i] = lerp(def.life[0], def.life[1], rnd()) * (o?.life ?? 1);
|
|
504
|
-
this.size0[i] = lerp(def.size[0], def.size[1], rnd()) * (o?.size ?? 1);
|
|
505
|
-
this.seed[i] = rnd();
|
|
506
|
-
this.alphaMul[i] = o?.alpha ?? 1;
|
|
507
|
-
this.tag[i] = o?.tag ?? 0;
|
|
508
|
-
this.colorHex[i] = o?.color ?? -1;
|
|
509
|
-
this.rgbStart.set(hexToRgb(o?.color ?? def.c0), i * 3);
|
|
510
|
-
this.rgbEnd.set(hexToRgb(o?.color ?? def.c1), i * 3);
|
|
511
|
-
this.bounced[i] = 0;
|
|
512
|
-
this.siz[i] = this.size0[i];
|
|
513
|
-
this.alp[i] = 0;
|
|
514
|
-
}
|
|
515
|
-
kill(i) {
|
|
516
|
-
if (!this.alive[i]) return;
|
|
517
|
-
this.alive[i] = 0;
|
|
518
|
-
this.live--;
|
|
519
|
-
this.alp[i] = 0;
|
|
520
|
-
this.siz[i] = 0;
|
|
521
|
-
}
|
|
522
|
-
clear() {
|
|
523
|
-
this.alive.fill(0);
|
|
524
|
-
this.alp.fill(0);
|
|
525
|
-
this.siz.fill(0);
|
|
526
|
-
this.live = 0;
|
|
527
|
-
this.high = 0;
|
|
528
|
-
this.next = 0;
|
|
529
|
-
this.geometry.setDrawRange(0, 0);
|
|
530
|
-
}
|
|
531
|
-
/** Move a live particle (used by external pushes such as blasts). */
|
|
532
|
-
push(cx, cy, cz, radius, speed) {
|
|
533
|
-
for (let i = 0; i < this.high; i++) {
|
|
534
|
-
if (!this.alive[i]) continue;
|
|
535
|
-
const dx = this.pos[i * 3] - cx;
|
|
536
|
-
const dy = this.pos[i * 3 + 1] - cy;
|
|
537
|
-
const dz = this.pos[i * 3 + 2] - cz;
|
|
538
|
-
const d = Math.hypot(dx, dy, dz);
|
|
539
|
-
if (d > radius || d < 1e-4) continue;
|
|
540
|
-
const f = (1 - d / radius) * speed;
|
|
541
|
-
this.vel[i * 3] += dx / d * f;
|
|
542
|
-
this.vel[i * 3 + 1] += dy / d * f;
|
|
543
|
-
this.vel[i * 3 + 2] += dz / d * f;
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
update(dt, host) {
|
|
547
|
-
let top = 0;
|
|
548
|
-
for (let i = 0; i < this.high; i++) {
|
|
549
|
-
if (!this.alive[i]) continue;
|
|
550
|
-
const def = KIND_LIST[this.kind[i]];
|
|
551
|
-
const age = this.age[i] += dt;
|
|
552
|
-
const life = this.life[i];
|
|
553
|
-
if (age >= life) {
|
|
554
|
-
this.kill(i);
|
|
555
|
-
continue;
|
|
556
|
-
}
|
|
557
|
-
const t = age / life;
|
|
558
|
-
const i3 = i * 3;
|
|
559
|
-
let vx = this.vel[i3];
|
|
560
|
-
let vy = this.vel[i3 + 1];
|
|
561
|
-
let vz = this.vel[i3 + 2];
|
|
562
|
-
vy += (def.rise - def.gravity * host.gravity) * dt;
|
|
563
|
-
if (def.flutter) {
|
|
564
|
-
const ph = age * 7 + this.seed[i] * 40;
|
|
565
|
-
vx += Math.sin(ph) * def.flutter * dt;
|
|
566
|
-
vz += Math.cos(ph * 1.3) * def.flutter * dt;
|
|
567
|
-
}
|
|
568
|
-
if (def.drag > 0) {
|
|
569
|
-
const k = Math.exp(-def.drag * dt);
|
|
570
|
-
vx *= k;
|
|
571
|
-
vy *= k;
|
|
572
|
-
vz *= k;
|
|
573
|
-
}
|
|
574
|
-
const x = this.pos[i3] + vx * dt;
|
|
575
|
-
let y = this.pos[i3 + 1] + vy * dt;
|
|
576
|
-
const z = this.pos[i3 + 2] + vz * dt;
|
|
577
|
-
if (def.ground !== "none") {
|
|
578
|
-
const gy = host.groundAt(x, z) + 5e-3;
|
|
579
|
-
if (y < gy) {
|
|
580
|
-
if (def.ground === "land") {
|
|
581
|
-
const tag = this.tag[i];
|
|
582
|
-
if (tag !== 0) host.onLand(x, gy, z, this.colorHex[i] >= 0 ? this.colorHex[i] : def.c0, tag);
|
|
583
|
-
this.kill(i);
|
|
584
|
-
continue;
|
|
585
|
-
}
|
|
586
|
-
y = gy;
|
|
587
|
-
if (def.ground === "bounce" && !this.bounced[i]) {
|
|
588
|
-
this.bounced[i] = 1;
|
|
589
|
-
vy = Math.abs(vy) * 0.35;
|
|
590
|
-
vx *= 0.6;
|
|
591
|
-
vz *= 0.6;
|
|
592
|
-
} else {
|
|
593
|
-
vx = vy = vz = 0;
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
this.vel[i3] = vx;
|
|
598
|
-
this.vel[i3 + 1] = vy;
|
|
599
|
-
this.vel[i3 + 2] = vz;
|
|
600
|
-
this.pos[i3] = x;
|
|
601
|
-
this.pos[i3 + 1] = y;
|
|
602
|
-
this.pos[i3 + 2] = z;
|
|
603
|
-
this.siz[i] = this.size0[i] * lerp(1, def.grow, t);
|
|
604
|
-
this.col[i3] = lerp(this.rgbStart[i3], this.rgbEnd[i3], t);
|
|
605
|
-
this.col[i3 + 1] = lerp(this.rgbStart[i3 + 1], this.rgbEnd[i3 + 1], t);
|
|
606
|
-
this.col[i3 + 2] = lerp(this.rgbStart[i3 + 2], this.rgbEnd[i3 + 2], t);
|
|
607
|
-
let env = t < def.fadeIn ? t / def.fadeIn : 1 - (t - def.fadeIn) / (1 - def.fadeIn);
|
|
608
|
-
if (def.flicker) env *= 1 - def.flicker * (0.5 + 0.5 * Math.sin(age * 38 + this.seed[i] * 50));
|
|
609
|
-
this.alp[i] = clamp(env, 0, 1) * def.alpha * this.alphaMul[i];
|
|
610
|
-
top = i + 1;
|
|
611
|
-
}
|
|
612
|
-
this.high = top;
|
|
613
|
-
this.geometry.setDrawRange(0, top);
|
|
614
|
-
for (const name of ["position", "pcolor", "alpha", "size"]) {
|
|
615
|
-
;
|
|
616
|
-
this.geometry.getAttribute(name).needsUpdate = true;
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
dispose() {
|
|
620
|
-
this.geometry.dispose();
|
|
621
|
-
this.material.dispose();
|
|
622
|
-
}
|
|
623
|
-
};
|
|
624
|
-
var ParticleSystem = class {
|
|
625
|
-
constructor(THREE, o) {
|
|
626
|
-
__publicField(this, "o", o);
|
|
627
|
-
__publicField(this, "add");
|
|
628
|
-
__publicField(this, "normal");
|
|
629
|
-
__publicField(this, "rng", createRng(2654435761));
|
|
630
|
-
__publicField(this, "attached", false);
|
|
631
|
-
__publicField(this, "host");
|
|
632
|
-
const addCap = Math.floor(o.capacity * 0.4);
|
|
633
|
-
this.add = new Pool(THREE, addCap, true, o.pointScale);
|
|
634
|
-
this.normal = new Pool(THREE, o.capacity - addCap, false, o.pointScale);
|
|
635
|
-
this.host = { groundAt: o.groundAt, gravity: o.gravity, onLand: o.onLand };
|
|
636
|
-
}
|
|
637
|
-
get capacity() {
|
|
638
|
-
return this.add.capacity + this.normal.capacity;
|
|
639
|
-
}
|
|
640
|
-
get live() {
|
|
641
|
-
return this.add.live + this.normal.live;
|
|
642
|
-
}
|
|
643
|
-
ensureAttached() {
|
|
644
|
-
if (this.attached) return;
|
|
645
|
-
this.attached = true;
|
|
646
|
-
this.o.scene.add(this.add.points, this.normal.points);
|
|
647
|
-
}
|
|
648
|
-
/** Remove the Points from the scene (they come back on the next emit). */
|
|
649
|
-
detach() {
|
|
650
|
-
if (!this.attached) return;
|
|
651
|
-
this.attached = false;
|
|
652
|
-
this.o.scene.remove(this.add.points, this.normal.points);
|
|
653
|
-
}
|
|
654
|
-
/** Spawn one particle with an explicit velocity. */
|
|
655
|
-
spawn(kind, p, v, opts) {
|
|
656
|
-
const def = KINDS[kind];
|
|
657
|
-
this.ensureAttached();
|
|
658
|
-
(def.additive ? this.add : this.normal).spawn(KIND_INDEX[kind], p[0], p[1], p[2], v[0], v[1], v[2], def, this.rng, opts);
|
|
659
|
-
}
|
|
660
|
-
/** Spawn `count` particles leaving `point` around `normal` at about `speed` m/s. */
|
|
661
|
-
burst(kind, point, normal, count, speed = 1, opts) {
|
|
662
|
-
const def = KINDS[kind];
|
|
663
|
-
const n = Math.max(0, Math.round(count * this.o.density));
|
|
664
|
-
const rnd = this.rng;
|
|
665
|
-
const nl = Math.hypot(normal[0], normal[1], normal[2]) || 1;
|
|
666
|
-
const nx = normal[0] / nl;
|
|
667
|
-
const ny = normal[1] / nl;
|
|
668
|
-
const nz = normal[2] / nl;
|
|
669
|
-
for (let k = 0; k < n; k++) {
|
|
670
|
-
const u = rnd() * 2 - 1;
|
|
671
|
-
const ph = rnd() * Math.PI * 2;
|
|
672
|
-
const s = Math.sqrt(1 - u * u);
|
|
673
|
-
const rx = s * Math.cos(ph);
|
|
674
|
-
const ry = u;
|
|
675
|
-
const rz = s * Math.sin(ph);
|
|
676
|
-
let dx = nx * def.cone + rx * (1 - def.cone);
|
|
677
|
-
let dy = ny * def.cone + ry * (1 - def.cone);
|
|
678
|
-
let dz = nz * def.cone + rz * (1 - def.cone);
|
|
679
|
-
dx += rx * 0.35;
|
|
680
|
-
dy += ry * 0.35;
|
|
681
|
-
dz += rz * 0.35;
|
|
682
|
-
const l = Math.hypot(dx, dy, dz) || 1;
|
|
683
|
-
const sp = speed * (0.35 + 0.65 * rnd());
|
|
684
|
-
const jitter = 0.01 + (def.size[1] > 0.5 ? 0.15 : 0.02);
|
|
685
|
-
this.spawn(
|
|
686
|
-
kind,
|
|
687
|
-
[point[0] + (rnd() - 0.5) * jitter, point[1] + (rnd() - 0.5) * jitter, point[2] + (rnd() - 0.5) * jitter],
|
|
688
|
-
[dx / l * sp, dy / l * sp, dz / l * sp],
|
|
689
|
-
opts
|
|
690
|
-
);
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
|
-
/** Blast wave: shove live particles away from a point. */
|
|
694
|
-
push(point, radius, speed) {
|
|
695
|
-
this.add.push(point[0], point[1], point[2], radius, speed);
|
|
696
|
-
this.normal.push(point[0], point[1], point[2], radius, speed);
|
|
697
|
-
}
|
|
698
|
-
update(dt) {
|
|
699
|
-
if (this.add.live > 0 || this.add.points.geometry.drawRange.count > 0) this.add.update(dt, this.host);
|
|
700
|
-
if (this.normal.live > 0 || this.normal.points.geometry.drawRange.count > 0) this.normal.update(dt, this.host);
|
|
701
|
-
}
|
|
702
|
-
clear() {
|
|
703
|
-
this.add.clear();
|
|
704
|
-
this.normal.clear();
|
|
705
|
-
this.detach();
|
|
706
|
-
}
|
|
707
|
-
dispose() {
|
|
708
|
-
this.detach();
|
|
709
|
-
this.add.dispose();
|
|
710
|
-
this.normal.dispose();
|
|
711
|
-
}
|
|
712
|
-
};
|
|
713
|
-
|
|
714
376
|
// src/pieces.ts
|
|
715
377
|
var SUB_DT = 1 / 120;
|
|
716
378
|
var MU = 0.7;
|
|
@@ -1037,9 +699,9 @@ var PieceSystem = class {
|
|
|
1037
699
|
|
|
1038
700
|
// src/rebar.ts
|
|
1039
701
|
var RebarCages = class {
|
|
1040
|
-
constructor(THREE,
|
|
702
|
+
constructor(THREE, scene2, radius = 4e-3) {
|
|
1041
703
|
__publicField(this, "THREE", THREE);
|
|
1042
|
-
__publicField(this, "scene",
|
|
704
|
+
__publicField(this, "scene", scene2);
|
|
1043
705
|
__publicField(this, "cages", /* @__PURE__ */ new Map());
|
|
1044
706
|
__publicField(this, "geometry");
|
|
1045
707
|
__publicField(this, "material");
|
|
@@ -1855,10 +1517,10 @@ var Visuals = class {
|
|
|
1855
1517
|
const sy = lerp(1, 0.25, melt);
|
|
1856
1518
|
const sxz = 1 + 0.18 * melt;
|
|
1857
1519
|
for (let i = range[0] * 3, v = 0; i < range[1] * 3; i += 3, v++) {
|
|
1858
|
-
const
|
|
1520
|
+
const noise2 = (Math.imul(v + 1, 2654435761) >>> 0) % 1e3 / 1e3;
|
|
1859
1521
|
arr[i] = cx + (work[i] - cx) * sxz;
|
|
1860
1522
|
arr[i + 2] = cz + (work[i + 2] - cz) * sxz;
|
|
1861
|
-
arr[i + 1] = Math.max(minY, minY + (work[i + 1] - minY) * sy -
|
|
1523
|
+
arr[i + 1] = Math.max(minY, minY + (work[i + 1] - minY) * sy - noise2 * melt * h * 0.04);
|
|
1862
1524
|
}
|
|
1863
1525
|
}
|
|
1864
1526
|
/** The colour attribute of the host's current geometry (created white when there is none) with vertex colours switched on in its material. */
|
|
@@ -2091,8 +1753,13 @@ var Visuals = class {
|
|
|
2091
1753
|
};
|
|
2092
1754
|
|
|
2093
1755
|
// src/renderer.ts
|
|
1756
|
+
var rendererCount = 0;
|
|
1757
|
+
var IMPACT_TAGS = ["metal", "glass", "wood"];
|
|
2094
1758
|
var GLOW_FROM_C = 430;
|
|
2095
1759
|
var QUEUE_CAP = 4e3;
|
|
1760
|
+
var FIRE_CEILING_SECONDS = 3600;
|
|
1761
|
+
var GENERIC_REFERENCE_RADIUS = 15;
|
|
1762
|
+
var PRIMED_FIRE_RADIUS = 0.2;
|
|
2096
1763
|
var FLUID_COLORS = {
|
|
2097
1764
|
water: 9226495,
|
|
2098
1765
|
fuel: 14262322,
|
|
@@ -2172,21 +1839,24 @@ var Renderer = class {
|
|
|
2172
1839
|
__publicField(this, "queue", []);
|
|
2173
1840
|
__publicField(this, "unsubscribe");
|
|
2174
1841
|
__publicField(this, "visuals");
|
|
2175
|
-
__publicField(this, "particles");
|
|
2176
1842
|
__publicField(this, "fragments");
|
|
2177
1843
|
__publicField(this, "ground");
|
|
2178
1844
|
__publicField(this, "pieceSys");
|
|
2179
1845
|
__publicField(this, "cages");
|
|
2180
1846
|
__publicField(this, "active", /* @__PURE__ */ new Map());
|
|
2181
1847
|
__publicField(this, "fires", /* @__PURE__ */ new Map());
|
|
1848
|
+
/** Primed parts burning in the effects world until they go off: their burn loop ends when they are put out. */
|
|
1849
|
+
__publicField(this, "primedBurning", /* @__PURE__ */ new Set());
|
|
2182
1850
|
__publicField(this, "leaks", []);
|
|
2183
1851
|
__publicField(this, "homeTags", []);
|
|
2184
1852
|
__publicField(this, "throttles", /* @__PURE__ */ new Map());
|
|
2185
1853
|
__publicField(this, "rng", createRng(12648430));
|
|
2186
|
-
__publicField(this, "
|
|
2187
|
-
|
|
2188
|
-
__publicField(this, "
|
|
2189
|
-
__publicField(this, "
|
|
1854
|
+
__publicField(this, "vfx");
|
|
1855
|
+
/** Names this renderer to the world's drips, so a shared world tells each renderer where its own landed. */
|
|
1856
|
+
__publicField(this, "owner", `destruction-${++rendererCount}`);
|
|
1857
|
+
__publicField(this, "stopLanding");
|
|
1858
|
+
/** The effects renderer made here when the host gave no shared world; it is stepped in `update`. */
|
|
1859
|
+
__publicField(this, "ownVfx");
|
|
2190
1860
|
__publicField(this, "time", 0);
|
|
2191
1861
|
__publicField(this, "scanTimer", 0);
|
|
2192
1862
|
__publicField(this, "disposed", false);
|
|
@@ -2209,17 +1879,11 @@ var Renderer = class {
|
|
|
2209
1879
|
const maxParticles = Math.max(8, Math.floor((o.maxParticles ?? 2500) * (this.low ? 0.5 : 1)));
|
|
2210
1880
|
this.visuals = new Visuals(THREE, this.low, 600);
|
|
2211
1881
|
this.ground = new GroundSpots(THREE, o.scene, this.groundAt, 48);
|
|
2212
|
-
this.
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
groundAt: this.groundAt,
|
|
2218
|
-
gravity: this.gravity,
|
|
2219
|
-
onLand: (x, _y, z, color, tag) => {
|
|
2220
|
-
const home = this.homeTags[tag - 1] ?? "";
|
|
2221
|
-
this.ground.add("puddle", x, z, 0.05 + this.rng() * 0.04, color, home);
|
|
2222
|
-
}
|
|
1882
|
+
this.vfx = o.vfx ?? createVfx({ profile: this.quality, gravity: this.gravity, groundAt: this.groundAt, sprites: { capacity: maxParticles, density: this.low ? 0.6 : 1 } });
|
|
1883
|
+
this.ownVfx = o.vfx ? void 0 : createVfxRenderer(THREE, { scene: o.scene, camera: o.camera ?? new THREE.PerspectiveCamera(), vfx: this.vfx, pointScale: o.pointScale ?? 700 });
|
|
1884
|
+
this.stopLanding = this.vfx.onLand(this.owner, (x, _y, z, color, tag) => {
|
|
1885
|
+
const home = this.homeTags[tag - 1] ?? "";
|
|
1886
|
+
this.ground.add("puddle", x, z, 0.05 + this.rng() * 0.04, color, home);
|
|
2223
1887
|
});
|
|
2224
1888
|
this.fragments = new FragmentSystem(THREE, {
|
|
2225
1889
|
scene: o.scene,
|
|
@@ -2436,8 +2100,7 @@ var Renderer = class {
|
|
|
2436
2100
|
this.pieceSys.update(dt);
|
|
2437
2101
|
this.fragments.update(dt);
|
|
2438
2102
|
this.ground.update(dt);
|
|
2439
|
-
this.
|
|
2440
|
-
this.particles.update(dt);
|
|
2103
|
+
this.ownVfx?.update(dt);
|
|
2441
2104
|
this.syncTransforms();
|
|
2442
2105
|
}
|
|
2443
2106
|
drainQueue() {
|
|
@@ -2491,12 +2154,12 @@ var Renderer = class {
|
|
|
2491
2154
|
break;
|
|
2492
2155
|
case "ricochet": {
|
|
2493
2156
|
const d = ev.direction;
|
|
2494
|
-
this.
|
|
2157
|
+
this.vfx.burst("sparks", ev.point, d, 4 + Math.min(10, ev.energy / 150), 4);
|
|
2495
2158
|
this.sound("ricochet", ev.point, 0.7);
|
|
2496
2159
|
break;
|
|
2497
2160
|
}
|
|
2498
2161
|
case "debris":
|
|
2499
|
-
this.
|
|
2162
|
+
this.vfx.burst(ev.kind, ev.point, ev.normal, ev.count, ev.speed > 0 ? ev.speed : DEFAULT_SPEED[ev.kind] ?? 1, this.debrisOptions(ev.kind, ev.object, ev.part));
|
|
2500
2163
|
break;
|
|
2501
2164
|
case "fracture":
|
|
2502
2165
|
this.onFracture(ev);
|
|
@@ -2506,6 +2169,7 @@ var Renderer = class {
|
|
|
2506
2169
|
break;
|
|
2507
2170
|
case "collapse":
|
|
2508
2171
|
this.splitLoose(ev.object);
|
|
2172
|
+
this.playEvent({ kind: "collapse" }, this.rootPoint(ev.object));
|
|
2509
2173
|
break;
|
|
2510
2174
|
case "disassemble_progress": {
|
|
2511
2175
|
const k = `unscrew:${ev.joint}`;
|
|
@@ -2516,18 +2180,18 @@ var Renderer = class {
|
|
|
2516
2180
|
break;
|
|
2517
2181
|
}
|
|
2518
2182
|
case "ignite":
|
|
2519
|
-
this.
|
|
2183
|
+
this.startFire(ev);
|
|
2520
2184
|
this.activate(ev.object, ev.part);
|
|
2521
|
-
this.particles.burst("fire", ev.point, [0, 1, 0], 6, 1);
|
|
2522
2185
|
this.sound("ignite", ev.point, 0.7);
|
|
2523
2186
|
this.sound("burn_loop", ev.point, 0.5);
|
|
2524
2187
|
break;
|
|
2525
2188
|
case "primed":
|
|
2526
2189
|
if (ev.trigger !== "explosion") {
|
|
2527
|
-
this.
|
|
2190
|
+
this.vfx.fire({ point: ev.point, radius: PRIMED_FIRE_RADIUS, life: ev.delay });
|
|
2191
|
+
this.primedBurning.add(`${ev.object}|${ev.part}`);
|
|
2528
2192
|
this.activate(ev.object, ev.part);
|
|
2529
|
-
this.particles.burst("fire", ev.point, [0, 1, 0], 6, 1);
|
|
2530
2193
|
this.sound("ignite", ev.point, 0.7);
|
|
2194
|
+
this.sound("burn_loop", ev.point, 0.5);
|
|
2531
2195
|
}
|
|
2532
2196
|
break;
|
|
2533
2197
|
case "spall":
|
|
@@ -2538,21 +2202,29 @@ var Renderer = class {
|
|
|
2538
2202
|
break;
|
|
2539
2203
|
case "expose":
|
|
2540
2204
|
this.cages.set(`${ev.object}|${ev.part}`, ev.bars);
|
|
2205
|
+
if (ev.bars.length > 0) {
|
|
2206
|
+
const [a, b] = ev.bars[0];
|
|
2207
|
+
this.playEvent({ kind: "expose" }, [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2, (a[2] + b[2]) / 2]);
|
|
2208
|
+
}
|
|
2541
2209
|
break;
|
|
2542
2210
|
case "extinguish": {
|
|
2543
2211
|
const f = this.fires.get(`${ev.object}|${ev.part}`);
|
|
2212
|
+
const primed = this.primedBurning.delete(`${ev.object}|${ev.part}`);
|
|
2544
2213
|
if (f) {
|
|
2545
|
-
this.
|
|
2546
|
-
this.
|
|
2214
|
+
this.stopFire(`${ev.object}|${ev.part}`);
|
|
2215
|
+
this.vfx.burst("steam", f.point, [0, 1, 0], 14, 1.2);
|
|
2547
2216
|
}
|
|
2217
|
+
if (f || primed) this.sound("burn_end", f?.point ?? this.rootPoint(ev.object), 0.5);
|
|
2548
2218
|
this.activate(ev.object, ev.part);
|
|
2549
2219
|
this.sound("extinguish", f?.point ?? this.rootPoint(ev.object), 0.6);
|
|
2550
2220
|
break;
|
|
2551
2221
|
}
|
|
2552
2222
|
case "burnout": {
|
|
2553
|
-
this.
|
|
2554
|
-
this.
|
|
2555
|
-
this.
|
|
2223
|
+
this.stopFire(`${ev.object}|${ev.part}`);
|
|
2224
|
+
this.primedBurning.delete(`${ev.object}|${ev.part}`);
|
|
2225
|
+
this.sound("burn_end", ev.point, 0.5);
|
|
2226
|
+
this.vfx.burst("ash", ev.point, [0, 1, 0], 16, 1);
|
|
2227
|
+
this.vfx.burst("smoke", ev.point, [0, 1, 0], 6, 0.8, { color: SMOKE_TONES.grey });
|
|
2556
2228
|
const pv = this.partVis(ev.object, ev.part);
|
|
2557
2229
|
if (pv) {
|
|
2558
2230
|
const def = this.world.materials.get(pv.material);
|
|
@@ -2575,7 +2247,7 @@ var Renderer = class {
|
|
|
2575
2247
|
const color = (pv && this.partColor(pv, def)) ?? fallbackColor(ev.material, def);
|
|
2576
2248
|
let tag = this.homeTags.indexOf(ev.object) + 1;
|
|
2577
2249
|
if (tag === 0) tag = this.homeTags.push(ev.object);
|
|
2578
|
-
this.
|
|
2250
|
+
this.vfx.spawn("drips", ev.point, [(this.rng() - 0.5) * 0.1, -0.1, (this.rng() - 0.5) * 0.1], { color, tag, owner: this.owner });
|
|
2579
2251
|
break;
|
|
2580
2252
|
}
|
|
2581
2253
|
case "leak": {
|
|
@@ -2592,6 +2264,7 @@ var Renderer = class {
|
|
|
2592
2264
|
break;
|
|
2593
2265
|
}
|
|
2594
2266
|
case "explode":
|
|
2267
|
+
this.primedBurning.delete(`${ev.object}|${ev.part}`);
|
|
2595
2268
|
this.onExplode(ev);
|
|
2596
2269
|
break;
|
|
2597
2270
|
case "destroyed":
|
|
@@ -2666,7 +2339,7 @@ var Renderer = class {
|
|
|
2666
2339
|
if (!this.visuals.shown(mv)) continue;
|
|
2667
2340
|
this.visuals.applyDent(mv, wp, wn, ev.dent);
|
|
2668
2341
|
}
|
|
2669
|
-
if (def?.tags?.includes("metal")) this.
|
|
2342
|
+
if (def?.tags?.includes("metal")) this.vfx.burst("sparks", ev.worldPoint, [wn.x, wn.y, wn.z], 4 + Math.min(8, ev.dent.depth * 150), 3.2);
|
|
2670
2343
|
this.sound(impactKey(def), ev.worldPoint, clamp(0.3 + ev.dent.depth * 12, 0.3, 1));
|
|
2671
2344
|
}
|
|
2672
2345
|
onHole(ev) {
|
|
@@ -2688,7 +2361,7 @@ var Renderer = class {
|
|
|
2688
2361
|
};
|
|
2689
2362
|
draw(ev.worldPoint, n);
|
|
2690
2363
|
if (ev.hole.through && ev.exitPoint) draw(ev.exitPoint, n.clone().negate());
|
|
2691
|
-
if (def?.tags?.includes("metal")) this.
|
|
2364
|
+
if (def?.tags?.includes("metal")) this.vfx.burst("sparks", ev.worldPoint, [n.x, n.y, n.z], 6, 3.5);
|
|
2692
2365
|
this.sound(impactKey(def), ev.worldPoint, 0.6);
|
|
2693
2366
|
}
|
|
2694
2367
|
onCrack(ev) {
|
|
@@ -2712,6 +2385,20 @@ var Renderer = class {
|
|
|
2712
2385
|
const side = point[axis] >= (box.min[axis] + box.max[axis]) / 2 ? 1 : -1;
|
|
2713
2386
|
return new this.THREE.Vector3(axis === "x" ? side : 0, axis === "y" ? side : 0, axis === "z" ? side : 0);
|
|
2714
2387
|
}
|
|
2388
|
+
/** The material an impact effect is chosen by: the tags of the material (`metal`, `glass`, `wood`), else stone and the like are `concrete`. */
|
|
2389
|
+
impactMaterial(def) {
|
|
2390
|
+
for (const tag of IMPACT_TAGS) if (def?.tags?.includes(tag)) return tag;
|
|
2391
|
+
return "concrete";
|
|
2392
|
+
}
|
|
2393
|
+
/**
|
|
2394
|
+
* Plays the effect the event map names for a destruction event (`data/event-effects.json` of `game-vfx`, or `RendererOptions.eventEffects`) at a point.
|
|
2395
|
+
* Answers whether one played, so the caller can leave out the burst it replaces.
|
|
2396
|
+
*/
|
|
2397
|
+
playEvent(event, point) {
|
|
2398
|
+
const id = effectForEvent(event, this.o.eventEffects ?? EVENT_EFFECTS_MAP);
|
|
2399
|
+
if (!id) return false;
|
|
2400
|
+
return this.vfx.play(id, point, { seed: hashNumbers(point[0], point[1], point[2], this.time) }) !== null;
|
|
2401
|
+
}
|
|
2715
2402
|
/** A coat came off: a patch in the colour of the core under it, below the bullet holes and cracks. */
|
|
2716
2403
|
onSpall(ev) {
|
|
2717
2404
|
const pv = this.partVis(ev.object, ev.part);
|
|
@@ -2720,6 +2407,7 @@ var Renderer = class {
|
|
|
2720
2407
|
const mv = this.nearestMesh(pv, wp);
|
|
2721
2408
|
if (!mv) return;
|
|
2722
2409
|
const def = this.world.materials.get(pv.material);
|
|
2410
|
+
this.playEvent({ kind: "spall", material: this.impactMaterial(def) }, ev.point);
|
|
2723
2411
|
this.visuals.addSpall(mv, wp, this.faceNormal(mv, wp), Math.sqrt(ev.area / Math.PI), fallbackColor(pv.material, def));
|
|
2724
2412
|
}
|
|
2725
2413
|
/**
|
|
@@ -2727,6 +2415,7 @@ var Renderer = class {
|
|
|
2727
2415
|
* (cutting the wall's faces) is not drawn yet: it needs the per-vertex binding of the house renderer.
|
|
2728
2416
|
*/
|
|
2729
2417
|
onStage(ev) {
|
|
2418
|
+
const played = this.playEvent({ kind: "stage", stage: ev.stage }, ev.opening?.center ?? this.rootPoint(ev.object));
|
|
2730
2419
|
if (!ev.fragments || !ev.opening) return;
|
|
2731
2420
|
const pv = this.partVis(ev.object, ev.part);
|
|
2732
2421
|
const def = pv ? this.world.materials.get(pv.material) : void 0;
|
|
@@ -2738,7 +2427,7 @@ var Renderer = class {
|
|
|
2738
2427
|
const fdef = this.world.materials.get(fr.material);
|
|
2739
2428
|
this.fragments.spawn(fr, this.fragments.materialFor(fr.material, fdef === def || !fdef ? color : fallbackColor(fr.material, fdef), false), ev.object, maxAlive);
|
|
2740
2429
|
}
|
|
2741
|
-
this.
|
|
2430
|
+
if (!played) this.vfx.burst("dust", ev.opening.center, [0, 1, 0], 14, 1.2, { color });
|
|
2742
2431
|
this.sound(impactKey(def), ev.opening.center, 0.8);
|
|
2743
2432
|
}
|
|
2744
2433
|
onFracture(ev) {
|
|
@@ -2768,24 +2457,24 @@ var Renderer = class {
|
|
|
2768
2457
|
const sizeK = clamp(ev.fragments.length / 10, 0.5, 2);
|
|
2769
2458
|
switch (ev.style) {
|
|
2770
2459
|
case "shatter":
|
|
2771
|
-
this.
|
|
2460
|
+
this.vfx.burst("shards", ev.point, up, 18 * sizeK, 3);
|
|
2772
2461
|
break;
|
|
2773
2462
|
case "crumble":
|
|
2774
|
-
this.
|
|
2775
|
-
this.
|
|
2463
|
+
this.vfx.burst("dust", ev.point, up, 16 * sizeK, 1.2, { color: pv ? this.partColor(pv, def) ?? void 0 : void 0 });
|
|
2464
|
+
this.vfx.burst("chips", ev.point, up, 6, 3);
|
|
2776
2465
|
break;
|
|
2777
2466
|
case "splinter":
|
|
2778
|
-
this.
|
|
2779
|
-
this.
|
|
2467
|
+
this.vfx.burst("splinters", ev.point, up, 14 * sizeK, 3);
|
|
2468
|
+
this.vfx.burst("dust", ev.point, up, 5, 0.8, { color: 11901556 });
|
|
2780
2469
|
break;
|
|
2781
2470
|
case "tear":
|
|
2782
|
-
this.
|
|
2471
|
+
this.vfx.burst("fibers", ev.point, up, 12 * sizeK, 1.6, { color });
|
|
2783
2472
|
break;
|
|
2784
2473
|
case "snap":
|
|
2785
|
-
this.
|
|
2474
|
+
this.vfx.burst("chips", ev.point, up, 8 * sizeK, 3);
|
|
2786
2475
|
break;
|
|
2787
2476
|
default:
|
|
2788
|
-
this.
|
|
2477
|
+
this.vfx.burst("sparks", ev.point, up, 10, 4);
|
|
2789
2478
|
}
|
|
2790
2479
|
this.sound(fractureKey(def, ev.style), ev.point, clamp(0.4 + ev.fragments.length * 0.03, 0.4, 1));
|
|
2791
2480
|
}
|
|
@@ -2864,27 +2553,10 @@ var Renderer = class {
|
|
|
2864
2553
|
nb.piece = piece;
|
|
2865
2554
|
this.o.onPiece?.(piece);
|
|
2866
2555
|
}
|
|
2867
|
-
onExplode(ev) {
|
|
2556
|
+
onExplode(ev, options = {}) {
|
|
2868
2557
|
const p = ev.point;
|
|
2869
|
-
|
|
2870
|
-
this.
|
|
2871
|
-
this.particles.burst("fire", p, [0, 1, 0], 34 * k, 4 * k, { size: 1 + k * 0.5 });
|
|
2872
|
-
this.particles.burst("smoke", p, [0, 1, 0], 24 * k, 2.2, { color: SMOKE_TONES.black, size: 1 + k * 0.6 });
|
|
2873
|
-
this.particles.burst("dust", p, [0, 1, 0], 22 * k, 3.5 * k);
|
|
2874
|
-
this.particles.burst("sparks", p, [0, 1, 0], 16 * k, 8);
|
|
2875
|
-
this.particles.push(p, ev.radius, 4);
|
|
2876
|
-
if (this.o.explosionLight !== false) {
|
|
2877
|
-
if (!this.light) {
|
|
2878
|
-
this.light = new this.THREE.PointLight(16754784, 0, 10, 2);
|
|
2879
|
-
this.light.name = "destruction-explosion-light";
|
|
2880
|
-
}
|
|
2881
|
-
if (!this.light.parent) this.scene.add(this.light);
|
|
2882
|
-
this.light.position.set(p[0], p[1] + 0.5, p[2]);
|
|
2883
|
-
this.light.distance = Math.max(6, ev.radius * 3);
|
|
2884
|
-
this.lightPeak = clamp(Math.sqrt(Math.max(1, ev.energy)) * 0.6, 10, 400);
|
|
2885
|
-
this.lightDur = 0.35;
|
|
2886
|
-
this.lightT = this.lightDur;
|
|
2887
|
-
}
|
|
2558
|
+
if (options.visual !== false) this.playExplosion(ev, options);
|
|
2559
|
+
this.vfx.push(p, ev.radius, 4);
|
|
2888
2560
|
const c = this.v1;
|
|
2889
2561
|
for (const pc of this.pieceSys.list) {
|
|
2890
2562
|
this.pieceSys.centerOf(pc, c);
|
|
@@ -2903,11 +2575,23 @@ var Renderer = class {
|
|
|
2903
2575
|
this.fragments.blast(p, ev.radius, Math.min(20, Math.sqrt(ev.energy) * 0.08));
|
|
2904
2576
|
this.sound("explode", p, clamp(0.5 + ev.radius / 10, 0.5, 1));
|
|
2905
2577
|
}
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
const
|
|
2910
|
-
this.
|
|
2578
|
+
playExplosion(ev, options) {
|
|
2579
|
+
const named = options.effect ?? ev.effect;
|
|
2580
|
+
const direction = options.direction;
|
|
2581
|
+
const scale = clamp(ev.radius / GENERIC_REFERENCE_RADIUS, 0.2, 3);
|
|
2582
|
+
if (named !== void 0 && this.vfx.explosion(named, ev.point, direction ? { direction } : {}) !== null) return;
|
|
2583
|
+
this.vfx.explosion("generic", ev.point, { scale, ...direction ? { direction } : {} });
|
|
2584
|
+
}
|
|
2585
|
+
startFire(ev) {
|
|
2586
|
+
const key = `${ev.object}|${ev.part}`;
|
|
2587
|
+
this.stopFire(key);
|
|
2588
|
+
this.fires.set(key, { object: ev.object, part: ev.part, point: ev.point, follow: { x: ev.point[0], y: ev.point[1], z: ev.point[2] } });
|
|
2589
|
+
}
|
|
2590
|
+
stopFire(key) {
|
|
2591
|
+
const fire = this.fires.get(key);
|
|
2592
|
+
if (!fire) return;
|
|
2593
|
+
if (fire.handle !== void 0) this.vfx.stop(fire.handle);
|
|
2594
|
+
this.fires.delete(key);
|
|
2911
2595
|
}
|
|
2912
2596
|
// ----------------------------------------------------------------------------------------------------------------------------------
|
|
2913
2597
|
// Fire, heat, char, melt, leaks
|
|
@@ -2945,8 +2629,8 @@ var Renderer = class {
|
|
|
2945
2629
|
}
|
|
2946
2630
|
const fire = this.fires.get(key);
|
|
2947
2631
|
if (fire) {
|
|
2948
|
-
if (ps.burning && ps.status !== "broken" && ps.status !== "ashes" && ps.status !== "melted") this.emitFire(fire, pv, def
|
|
2949
|
-
else this.
|
|
2632
|
+
if (ps.burning && ps.status !== "broken" && ps.status !== "ashes" && ps.status !== "melted") this.emitFire(fire, pv, def);
|
|
2633
|
+
else this.stopFire(key);
|
|
2950
2634
|
}
|
|
2951
2635
|
const hot = def?.visual?.glows === true && ps.temperature > GLOW_FROM_C;
|
|
2952
2636
|
if (!ps.burning && !hot && Math.abs(ps.char - pv.charApplied) < 0.01 && Math.abs(ps.melt - pv.meltApplied) < 0.01) {
|
|
@@ -2967,43 +2651,25 @@ var Renderer = class {
|
|
|
2967
2651
|
this.ground.add("puddle", c.x, c.z, clamp(footprint * 0.55, 0.05, 1), this.partColor(pv, def) ?? fallbackColor(pv.material, def), objectId);
|
|
2968
2652
|
} else {
|
|
2969
2653
|
this.ground.add("ash", c.x, c.z, clamp(footprint * 0.35, 0.04, 0.5), 1907224, objectId);
|
|
2970
|
-
this.
|
|
2654
|
+
this.vfx.burst("ash", [c.x, bb.max.y, c.z], [0, 1, 0], 10, 0.8);
|
|
2971
2655
|
}
|
|
2972
2656
|
}
|
|
2973
|
-
emitFire(f, pv, def
|
|
2657
|
+
emitFire(f, pv, def) {
|
|
2974
2658
|
const bb = this.box;
|
|
2975
2659
|
bb.makeEmpty();
|
|
2976
2660
|
const tmp = new this.THREE.Box3();
|
|
2977
2661
|
for (const mv of pv.meshes) if (this.visuals.shown(mv)) bb.union(this.visuals.worldBox(mv, tmp));
|
|
2978
|
-
let cx = f.point[0];
|
|
2979
|
-
let cy = f.point[1];
|
|
2980
|
-
let cz = f.point[2];
|
|
2981
|
-
let sx = 0.2;
|
|
2982
|
-
let sy = 0.2;
|
|
2983
|
-
let sz = 0.2;
|
|
2984
2662
|
if (!bb.isEmpty()) {
|
|
2985
2663
|
const c = bb.getCenter(this.v1);
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
cz = c.z;
|
|
2990
|
-
sx = s.x;
|
|
2991
|
-
sy = s.y;
|
|
2992
|
-
sz = s.z;
|
|
2993
|
-
}
|
|
2994
|
-
const vol = clamp(Math.cbrt(Math.max(sx * sy * sz, 1e-5)), 0.05, 1.5);
|
|
2995
|
-
f.accFire += (18 + 70 * vol) * dt;
|
|
2996
|
-
f.accSmoke += (6 + 24 * vol) * dt;
|
|
2997
|
-
const tone = SMOKE_TONES[def?.thermal.smoke ?? "grey"];
|
|
2998
|
-
const rnd = this.rng;
|
|
2999
|
-
while (f.accFire >= 1) {
|
|
3000
|
-
f.accFire -= 1;
|
|
3001
|
-
this.particles.spawn("fire", [cx + (rnd() - 0.5) * sx, cy + sy * (0.3 + 0.7 * rnd()), cz + (rnd() - 0.5) * sz], [(rnd() - 0.5) * 0.4, 0.5 + rnd() * 0.9, (rnd() - 0.5) * 0.4], { size: 0.6 + vol * 0.8 });
|
|
3002
|
-
}
|
|
3003
|
-
while (f.accSmoke >= 1) {
|
|
3004
|
-
f.accSmoke -= 1;
|
|
3005
|
-
this.particles.spawn("smoke", [cx + (rnd() - 0.5) * sx, cy + sy * (0.8 + 0.4 * rnd()), cz + (rnd() - 0.5) * sz], [(rnd() - 0.5) * 0.3 + 0.1, 0.4 + rnd() * 0.5, (rnd() - 0.5) * 0.3], { color: tone, size: 0.6 + vol });
|
|
2664
|
+
f.follow.x = c.x;
|
|
2665
|
+
f.follow.y = bb.min.y;
|
|
2666
|
+
f.follow.z = c.z;
|
|
3006
2667
|
}
|
|
2668
|
+
if (f.handle !== void 0) return;
|
|
2669
|
+
const size = bb.isEmpty() ? this.v2.set(0.2, 0.2, 0.2) : bb.getSize(this.v2);
|
|
2670
|
+
const radius = clamp(Math.max(size.x, size.z) * 0.5, 0.12, 1.2);
|
|
2671
|
+
const smokeColor = SMOKE_TONES[def?.thermal.smoke ?? "grey"];
|
|
2672
|
+
f.handle = this.vfx.fire({ point: [f.follow.x, f.follow.y, f.follow.z], radius, life: FIRE_CEILING_SECONDS, follow: f.follow, ...smokeColor !== void 0 ? { smokeColor } : {} });
|
|
3007
2673
|
}
|
|
3008
2674
|
updateLeaks(dt) {
|
|
3009
2675
|
for (let i = this.leaks.length - 1; i >= 0; i--) {
|
|
@@ -3021,8 +2687,8 @@ var Renderer = class {
|
|
|
3021
2687
|
while (l.acc >= 1) {
|
|
3022
2688
|
l.acc -= 1;
|
|
3023
2689
|
const vel = [d[0] / dl * sp + (this.rng() - 0.5) * 0.25, d[1] / dl * sp + (this.rng() - 0.5) * 0.25, d[2] / dl * sp + (this.rng() - 0.5) * 0.25];
|
|
3024
|
-
if (l.fluid === "gas") this.
|
|
3025
|
-
else this.
|
|
2690
|
+
if (l.fluid === "gas") this.vfx.spawn("steam", l.point, vel, { color, size: 0.5, alpha: 0.5 });
|
|
2691
|
+
else this.vfx.spawn("drips", l.point, vel, { color, tag: this.rng() < 0.03 ? this.tagOf(l.object) : 0, owner: this.owner });
|
|
3026
2692
|
}
|
|
3027
2693
|
}
|
|
3028
2694
|
}
|
|
@@ -3035,10 +2701,10 @@ var Renderer = class {
|
|
|
3035
2701
|
// Emit, clear, restore, stats, dispose
|
|
3036
2702
|
// ----------------------------------------------------------------------------------------------------------------------------------
|
|
3037
2703
|
emit(kind, point, normal, count, speed) {
|
|
3038
|
-
this.
|
|
2704
|
+
this.vfx.burst(kind, point, normal, count, speed ?? DEFAULT_SPEED[kind] ?? 1, kind === "smoke" ? { color: SMOKE_TONES.grey } : void 0);
|
|
3039
2705
|
}
|
|
3040
|
-
explode(point, energy, radius) {
|
|
3041
|
-
this.onExplode({ type: "explode", object: "", part: "", time: this.time, point, energy, radius });
|
|
2706
|
+
explode(point, energy, radius, options) {
|
|
2707
|
+
this.onExplode({ type: "explode", object: "", part: "", time: this.time, point, energy, radius }, options);
|
|
3042
2708
|
}
|
|
3043
2709
|
restore(objectId) {
|
|
3044
2710
|
const homes = /* @__PURE__ */ new Set([objectId]);
|
|
@@ -3046,7 +2712,7 @@ var Renderer = class {
|
|
|
3046
2712
|
if (self?.isSplit) for (const pv of self.parts.values()) homes.add(pv.home);
|
|
3047
2713
|
for (const home of homes) this.restoreHome(home);
|
|
3048
2714
|
for (const [key, a] of [...this.active]) if (homes.has(a.object)) this.active.delete(key);
|
|
3049
|
-
for (const [key, f] of [...this.fires]) if (homes.has(f.object)) this.
|
|
2715
|
+
for (const [key, f] of [...this.fires]) if (homes.has(f.object)) this.stopFire(key);
|
|
3050
2716
|
for (let i = this.leaks.length - 1; i >= 0; i--) if (homes.has(this.leaks[i].object)) this.leaks.splice(i, 1);
|
|
3051
2717
|
for (const home of homes) {
|
|
3052
2718
|
this.fragments.removeHome(home);
|
|
@@ -3075,7 +2741,7 @@ var Renderer = class {
|
|
|
3075
2741
|
clear() {
|
|
3076
2742
|
this.queue.length = 0;
|
|
3077
2743
|
this.active.clear();
|
|
3078
|
-
this.fires.
|
|
2744
|
+
for (const key of [...this.fires.keys()]) this.stopFire(key);
|
|
3079
2745
|
this.leaks.length = 0;
|
|
3080
2746
|
for (const id of [...this.bounds.keys()]) if (this.bounds.has(id)) this.restore(id);
|
|
3081
2747
|
for (const b of [...this.bounds.values()]) {
|
|
@@ -3089,15 +2755,17 @@ var Renderer = class {
|
|
|
3089
2755
|
this.fragments.clear();
|
|
3090
2756
|
this.ground.clear();
|
|
3091
2757
|
this.cages.clear();
|
|
3092
|
-
this.
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
this.lightT = 0;
|
|
3096
|
-
this.scene.remove(this.light);
|
|
3097
|
-
}
|
|
2758
|
+
this.primedBurning.clear();
|
|
2759
|
+
this.vfx.sprites().clear();
|
|
2760
|
+
this.ownVfx?.world.clear();
|
|
3098
2761
|
}
|
|
3099
2762
|
stats() {
|
|
3100
|
-
return { fragments: this.fragments.count, particles: this.
|
|
2763
|
+
return { fragments: this.fragments.count, particles: this.vfx.sprites().live + this.ownedVfxParticles(), pieces: this.pieceSys.list.length, decals: this.visuals.decalCount + this.ground.count };
|
|
2764
|
+
}
|
|
2765
|
+
ownedVfxParticles() {
|
|
2766
|
+
if (!this.ownVfx) return 0;
|
|
2767
|
+
const { puffs, sparks, glows, chunks } = this.vfx.stats();
|
|
2768
|
+
return puffs + sparks + glows + chunks;
|
|
3101
2769
|
}
|
|
3102
2770
|
rebarCount() {
|
|
3103
2771
|
return this.cages.count;
|
|
@@ -3110,15 +2778,212 @@ var Renderer = class {
|
|
|
3110
2778
|
this.fragments.dispose();
|
|
3111
2779
|
this.ground.dispose();
|
|
3112
2780
|
this.cages.dispose();
|
|
3113
|
-
this.
|
|
2781
|
+
this.stopLanding();
|
|
3114
2782
|
this.visuals.dispose();
|
|
3115
|
-
this.
|
|
3116
|
-
this.light = null;
|
|
2783
|
+
this.ownVfx?.dispose();
|
|
3117
2784
|
this.disposed = true;
|
|
3118
2785
|
}
|
|
3119
2786
|
};
|
|
2787
|
+
|
|
2788
|
+
// src/audio/recipes.ts
|
|
2789
|
+
import { DISTANCE_CURVES } from "@adatechnology/game-audio";
|
|
2790
|
+
var ATTACK = 2e-3;
|
|
2791
|
+
var lengthOf = (d, attack) => {
|
|
2792
|
+
if (typeof d === "number") return attack + d;
|
|
2793
|
+
if (Array.isArray(d)) return [attack + d[0], attack + d[1]];
|
|
2794
|
+
return { ...d, add: (d.add ?? 0) + attack };
|
|
2795
|
+
};
|
|
2796
|
+
var envelope = (attack) => [
|
|
2797
|
+
[0, 1e-4, "set", "fixed"],
|
|
2798
|
+
[attack, 1, "exp"],
|
|
2799
|
+
["1d", 1e-4, "exp", "fixed"]
|
|
2800
|
+
];
|
|
2801
|
+
function noise(o) {
|
|
2802
|
+
const a = o.a ?? ATTACK;
|
|
2803
|
+
const over = typeof o.d === "number" ? o.d : 0;
|
|
2804
|
+
return {
|
|
2805
|
+
filters: {
|
|
2806
|
+
mode: "serial",
|
|
2807
|
+
bank: [{ type: o.type ?? "lowpass", frequency: o.f2 === void 0 ? { set: o.f } : { set: o.f, to: o.f2, curve: "exp", over }, q: o.q ?? 0.7 }]
|
|
2808
|
+
},
|
|
2809
|
+
duration: lengthOf(o.d, a),
|
|
2810
|
+
gain: o.vol,
|
|
2811
|
+
...o.delay === void 0 ? {} : { at: o.delay },
|
|
2812
|
+
...o.wet === void 0 ? {} : { wet: o.wet },
|
|
2813
|
+
source: { kind: "noise", offset: "random", offsetScale: 1.5, loop: true },
|
|
2814
|
+
envelope: envelope(a)
|
|
2815
|
+
};
|
|
2816
|
+
}
|
|
2817
|
+
function tone(o) {
|
|
2818
|
+
return {
|
|
2819
|
+
source: { kind: "osc", wave: o.wave ?? "sine", frequency: { from: o.f, to: o.f2 ?? "from", curve: "exp", over: "duration" } },
|
|
2820
|
+
duration: lengthOf(o.d, ATTACK),
|
|
2821
|
+
gain: o.vol,
|
|
2822
|
+
...o.delay === void 0 ? {} : { at: o.delay },
|
|
2823
|
+
...o.wet === void 0 ? {} : { wet: o.wet },
|
|
2824
|
+
envelope: envelope(ATTACK)
|
|
2825
|
+
};
|
|
2826
|
+
}
|
|
2827
|
+
var many = (count, make) => Array.from({ length: count }, make);
|
|
2828
|
+
var scene = (id, layers, line, vars) => ({
|
|
2829
|
+
id,
|
|
2830
|
+
bus: "world",
|
|
2831
|
+
pan: true,
|
|
2832
|
+
layers,
|
|
2833
|
+
falloff: DISTANCE_CURVES.art_scene,
|
|
2834
|
+
...vars ? { vars } : {},
|
|
2835
|
+
sources: [`cena:${line}`]
|
|
2836
|
+
});
|
|
2837
|
+
var boomLayers = (big) => [
|
|
2838
|
+
noise({ f: 1200, f2: 120, d: 1.8, vol: 2, wet: 1.2 }),
|
|
2839
|
+
tone({ f: 65, f2: 24, d: 1, vol: 1.8 }),
|
|
2840
|
+
noise({ type: "highpass", f: 1500, d: 0.2, vol: 0.8 }),
|
|
2841
|
+
...big ? [noise({ f: 500, f2: 90, a: 0.2, d: 2.4, vol: 1.2, wet: 1, delay: 0.15 })] : [],
|
|
2842
|
+
...many(8, () => tone({ f: [2600, 5600], d: 0.05, vol: 0.08, delay: [0.4, 1.6] }))
|
|
2843
|
+
];
|
|
2844
|
+
var SCENE_SOUNDS = [
|
|
2845
|
+
scene(
|
|
2846
|
+
"glass_shatter",
|
|
2847
|
+
[noise({ type: "highpass", f: 2800, d: 0.45, vol: 0.9, wet: 0.5 }), ...many(12, () => tone({ f: [3e3, 7500], d: [0.06, 0.14], vol: 0.18, delay: [0, 0.55] }))],
|
|
2848
|
+
"L1359 glass"
|
|
2849
|
+
),
|
|
2850
|
+
scene(
|
|
2851
|
+
"wood_hit",
|
|
2852
|
+
[noise({ type: "bandpass", f: 850, q: 1, d: 0.25, vol: 0.9, wet: 0.3 }), tone({ f: 210, f2: 90, d: 0.1, vol: 0.6 }), noise({ type: "highpass", f: 2e3, d: 0.04, vol: 0.5 })],
|
|
2853
|
+
"L1360 wood"
|
|
2854
|
+
),
|
|
2855
|
+
scene("thunk", [noise({ type: "bandpass", f: 700, q: 2, d: 0.07, vol: 0.4 })], "L1361 thunk"),
|
|
2856
|
+
scene("ping", [tone({ f: [2600, 3500], f2: 1400, d: 0.14, vol: 0.18, wet: 0.3 })], "L1362 ping"),
|
|
2857
|
+
scene("clang", [tone({ f: [520, 720], f2: 480, d: 0.5, vol: 0.35, wet: 0.4 }), tone({ f: 1370, f2: 1300, d: 0.3, vol: 0.15 }), noise({ type: "highpass", f: 2e3, d: 0.05, vol: 0.5 })], "L1363 clang"),
|
|
2858
|
+
scene("chip", [noise({ type: "bandpass", f: 1600, q: 1.2, d: 0.06, vol: 0.5 })], "L1364 chip"),
|
|
2859
|
+
scene(
|
|
2860
|
+
"crumble",
|
|
2861
|
+
[noise({ f: 900, f2: 200, d: 0.6, vol: 0.9, wet: 0.3 }), ...many(5, () => noise({ type: "bandpass", f: [500, 1400], q: 2, d: 0.05, vol: 0.4, delay: [0.1, 0.5] }))],
|
|
2862
|
+
"L1365 crumble"
|
|
2863
|
+
),
|
|
2864
|
+
// `thud(x, z, v)`: `v` is the `scale` param (default 1)
|
|
2865
|
+
scene("thud", [tone({ f: 75, f2: 38, d: 0.22, vol: { param: "scale", scale: 0.6 } }), noise({ f: 420, d: 0.16, vol: { param: "scale", scale: 0.45 } })], "L1358 thud", { scale: 1 }),
|
|
2866
|
+
// `hiss(x, z, d)`: `seconds` is the param `d` (default 1.2)
|
|
2867
|
+
scene("hiss", [noise({ type: "highpass", f: 3500, a: 0.05, d: { param: "seconds" }, vol: 0.35 })], "L1366 hiss", { seconds: 1.2 }),
|
|
2868
|
+
scene("boom", boomLayers(false), "L1367 boom"),
|
|
2869
|
+
scene("boom_big", boomLayers(true), "L1367 boom (big)"),
|
|
2870
|
+
scene("burst", [noise({ f: 700, f2: 120, d: 0.7, vol: 1.4, wet: 0.5 }), noise({ type: "highpass", f: 3e3, a: 0.05, d: 1.2, vol: 0.4, delay: 0.1 })], "L1357 burst"),
|
|
2871
|
+
scene("crack", [noise({ type: "highpass", f: 1800, d: 0.05, vol: 0.8 }), tone({ f: 420, f2: 180, d: 0.06, vol: 0.4 })], "L1354 crack")
|
|
2872
|
+
];
|
|
2873
|
+
var EXPLOSION_BOOM = {
|
|
2874
|
+
id: "explosion_boom",
|
|
2875
|
+
bus: "world",
|
|
2876
|
+
order: "boom",
|
|
2877
|
+
duration: 1.6,
|
|
2878
|
+
tail: 0.1,
|
|
2879
|
+
layers: [
|
|
2880
|
+
{
|
|
2881
|
+
source: { kind: "osc", wave: "sine", frequency: { from: 70, to: 28, curve: "exp", over: 1.2 } },
|
|
2882
|
+
envelope: [
|
|
2883
|
+
[0, 1, "set"],
|
|
2884
|
+
["1d", 1e-3, "exp", "fixed"]
|
|
2885
|
+
]
|
|
2886
|
+
}
|
|
2887
|
+
],
|
|
2888
|
+
falloff: DISTANCE_CURVES.game_boom,
|
|
2889
|
+
sources: ["jogo:L68278-68285 boom"]
|
|
2890
|
+
};
|
|
2891
|
+
var BURN_LOOP = {
|
|
2892
|
+
id: "burn_loop",
|
|
2893
|
+
bus: "world",
|
|
2894
|
+
order: "chain_loop",
|
|
2895
|
+
source: { kind: "noise", loop: true },
|
|
2896
|
+
filters: { mode: "serial", bank: [{ type: "bandpass", frequency: { set: 1800 }, q: 0.6 }] },
|
|
2897
|
+
volume: 0,
|
|
2898
|
+
sources: ["cena:L1316 fire loop (silent in the page: its gain is never raised)", "sdk: the level while burning"]
|
|
2899
|
+
};
|
|
2900
|
+
var DESTRUCTION_SOUND_BANK = { id: "destruction_three", sounds: [...SCENE_SOUNDS, EXPLOSION_BOOM], loops: [BURN_LOOP] };
|
|
2901
|
+
|
|
2902
|
+
// src/audio/sound.ts
|
|
2903
|
+
import { GAME_SFX_BANK } from "@adatechnology/game-audio";
|
|
2904
|
+
var DESTRUCTION_SOUND_KEYS = {
|
|
2905
|
+
art_scene: {
|
|
2906
|
+
impact_metal: { sound: "clang" },
|
|
2907
|
+
impact_wood: { sound: "wood_hit" },
|
|
2908
|
+
impact_glass: { sound: "ping" },
|
|
2909
|
+
impact_masonry: { sound: "chip" },
|
|
2910
|
+
impact_soft: { sound: "thud" },
|
|
2911
|
+
shatter_glass: { sound: "glass_shatter" },
|
|
2912
|
+
crumble: { sound: "crumble" },
|
|
2913
|
+
splinter: { sound: "crack" },
|
|
2914
|
+
ricochet: { sound: "ping" },
|
|
2915
|
+
unscrew: { sound: "chip" },
|
|
2916
|
+
detach: { sound: "thunk" },
|
|
2917
|
+
ignite: { sound: "hiss", params: { seconds: 0.5 } },
|
|
2918
|
+
extinguish: { sound: "hiss" },
|
|
2919
|
+
melt_sizzle: { sound: "hiss", params: { seconds: 0.6 } },
|
|
2920
|
+
explode: { sound: "boom", above: { volume: 0.9, sound: "boom_big" } }
|
|
2921
|
+
},
|
|
2922
|
+
game: {
|
|
2923
|
+
impact_metal: { sound: "sfx_impact" },
|
|
2924
|
+
impact_wood: { sound: "sfx_wood" },
|
|
2925
|
+
impact_glass: { sound: "sfx_glass" },
|
|
2926
|
+
impact_masonry: { sound: "sfx_impact" },
|
|
2927
|
+
impact_soft: { sound: "sfx_flesh" },
|
|
2928
|
+
shatter_glass: { sound: "sfx_glass" },
|
|
2929
|
+
crumble: { sound: "sfx_impact" },
|
|
2930
|
+
splinter: { sound: "sfx_wood" },
|
|
2931
|
+
explode: { sound: "explosion_boom" }
|
|
2932
|
+
}
|
|
2933
|
+
};
|
|
2934
|
+
var DEFAULT_FIRE = { perFire: 0.05, max: 0.2 };
|
|
2935
|
+
function createDestructionSound(engine, options = {}) {
|
|
2936
|
+
engine.register(DESTRUCTION_SOUND_BANK);
|
|
2937
|
+
engine.register(GAME_SFX_BANK);
|
|
2938
|
+
const fire = { ...DEFAULT_FIRE, ...options.fire };
|
|
2939
|
+
const rules = { ...DESTRUCTION_SOUND_KEYS.art_scene, ...options.style === "game" ? DESTRUCTION_SOUND_KEYS.game : {}, ...options.keys };
|
|
2940
|
+
let loop;
|
|
2941
|
+
let burning = 0;
|
|
2942
|
+
const setFire = () => {
|
|
2943
|
+
if (options.burnLoop === false) return;
|
|
2944
|
+
if (burning <= 0) {
|
|
2945
|
+
loop?.stop();
|
|
2946
|
+
loop = void 0;
|
|
2947
|
+
return;
|
|
2948
|
+
}
|
|
2949
|
+
loop ?? (loop = engine.loop("burn_loop"));
|
|
2950
|
+
loop.set({ volume: Math.min(fire.max, fire.perFire * burning) });
|
|
2951
|
+
};
|
|
2952
|
+
return {
|
|
2953
|
+
onSound(key, point, volume) {
|
|
2954
|
+
if (key === "burn_loop") {
|
|
2955
|
+
burning += 1;
|
|
2956
|
+
setFire();
|
|
2957
|
+
return;
|
|
2958
|
+
}
|
|
2959
|
+
if (key === "burn_end") {
|
|
2960
|
+
if (burning > 0) burning -= 1;
|
|
2961
|
+
setFire();
|
|
2962
|
+
return;
|
|
2963
|
+
}
|
|
2964
|
+
const rule = rules[key];
|
|
2965
|
+
if (!rule) return;
|
|
2966
|
+
const sound = rule.above && volume >= rule.above.volume ? rule.above.sound : rule.sound;
|
|
2967
|
+
engine.play(sound, { position: point, volume: volume * (rule.volumeScale ?? 1), ...rule.params ? { params: rule.params } : {} });
|
|
2968
|
+
},
|
|
2969
|
+
rule: (key) => rules[key] ?? void 0,
|
|
2970
|
+
burning: () => burning,
|
|
2971
|
+
dispose() {
|
|
2972
|
+
loop?.stop();
|
|
2973
|
+
loop = void 0;
|
|
2974
|
+
burning = 0;
|
|
2975
|
+
}
|
|
2976
|
+
};
|
|
2977
|
+
}
|
|
3120
2978
|
export {
|
|
2979
|
+
BURN_LOOP,
|
|
2980
|
+
DEFAULT_FIRE,
|
|
2981
|
+
DESTRUCTION_SOUND_BANK,
|
|
2982
|
+
DESTRUCTION_SOUND_KEYS,
|
|
2983
|
+
EXPLOSION_BOOM,
|
|
2984
|
+
SCENE_SOUNDS,
|
|
3121
2985
|
createDestructionRenderer,
|
|
2986
|
+
createDestructionSound,
|
|
3122
2987
|
heatColor,
|
|
3123
2988
|
specFromObject3D
|
|
3124
2989
|
};
|