@adatechnology/game-destruction-three 0.4.0 → 0.5.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 +3 -3
- package/dist/index.d.ts +28 -7
- package/dist/index.global.js +8021 -542
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +112 -455
- package/dist/index.js.map +1 -1
- package/package.json +5 -3
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;
|
|
@@ -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;
|
|
@@ -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,7 +1839,6 @@ 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");
|
|
@@ -2183,10 +1849,12 @@ var Renderer = class {
|
|
|
2183
1849
|
__publicField(this, "homeTags", []);
|
|
2184
1850
|
__publicField(this, "throttles", /* @__PURE__ */ new Map());
|
|
2185
1851
|
__publicField(this, "rng", createRng(12648430));
|
|
2186
|
-
__publicField(this, "
|
|
2187
|
-
|
|
2188
|
-
__publicField(this, "
|
|
2189
|
-
__publicField(this, "
|
|
1852
|
+
__publicField(this, "vfx");
|
|
1853
|
+
/** Names this renderer to the world's drips, so a shared world tells each renderer where its own landed. */
|
|
1854
|
+
__publicField(this, "owner", `destruction-${++rendererCount}`);
|
|
1855
|
+
__publicField(this, "stopLanding");
|
|
1856
|
+
/** The effects renderer made here when the host gave no shared world; it is stepped in `update`. */
|
|
1857
|
+
__publicField(this, "ownVfx");
|
|
2190
1858
|
__publicField(this, "time", 0);
|
|
2191
1859
|
__publicField(this, "scanTimer", 0);
|
|
2192
1860
|
__publicField(this, "disposed", false);
|
|
@@ -2209,17 +1877,11 @@ var Renderer = class {
|
|
|
2209
1877
|
const maxParticles = Math.max(8, Math.floor((o.maxParticles ?? 2500) * (this.low ? 0.5 : 1)));
|
|
2210
1878
|
this.visuals = new Visuals(THREE, this.low, 600);
|
|
2211
1879
|
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
|
-
}
|
|
1880
|
+
this.vfx = o.vfx ?? createVfx({ profile: this.quality, gravity: this.gravity, groundAt: this.groundAt, sprites: { capacity: maxParticles, density: this.low ? 0.6 : 1 } });
|
|
1881
|
+
this.ownVfx = o.vfx ? void 0 : createVfxRenderer(THREE, { scene: o.scene, camera: o.camera ?? new THREE.PerspectiveCamera(), vfx: this.vfx, pointScale: o.pointScale ?? 700 });
|
|
1882
|
+
this.stopLanding = this.vfx.onLand(this.owner, (x, _y, z, color, tag) => {
|
|
1883
|
+
const home = this.homeTags[tag - 1] ?? "";
|
|
1884
|
+
this.ground.add("puddle", x, z, 0.05 + this.rng() * 0.04, color, home);
|
|
2223
1885
|
});
|
|
2224
1886
|
this.fragments = new FragmentSystem(THREE, {
|
|
2225
1887
|
scene: o.scene,
|
|
@@ -2436,8 +2098,7 @@ var Renderer = class {
|
|
|
2436
2098
|
this.pieceSys.update(dt);
|
|
2437
2099
|
this.fragments.update(dt);
|
|
2438
2100
|
this.ground.update(dt);
|
|
2439
|
-
this.
|
|
2440
|
-
this.particles.update(dt);
|
|
2101
|
+
this.ownVfx?.update(dt);
|
|
2441
2102
|
this.syncTransforms();
|
|
2442
2103
|
}
|
|
2443
2104
|
drainQueue() {
|
|
@@ -2491,12 +2152,12 @@ var Renderer = class {
|
|
|
2491
2152
|
break;
|
|
2492
2153
|
case "ricochet": {
|
|
2493
2154
|
const d = ev.direction;
|
|
2494
|
-
this.
|
|
2155
|
+
this.vfx.burst("sparks", ev.point, d, 4 + Math.min(10, ev.energy / 150), 4);
|
|
2495
2156
|
this.sound("ricochet", ev.point, 0.7);
|
|
2496
2157
|
break;
|
|
2497
2158
|
}
|
|
2498
2159
|
case "debris":
|
|
2499
|
-
this.
|
|
2160
|
+
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
2161
|
break;
|
|
2501
2162
|
case "fracture":
|
|
2502
2163
|
this.onFracture(ev);
|
|
@@ -2506,6 +2167,7 @@ var Renderer = class {
|
|
|
2506
2167
|
break;
|
|
2507
2168
|
case "collapse":
|
|
2508
2169
|
this.splitLoose(ev.object);
|
|
2170
|
+
this.playEvent({ kind: "collapse" }, this.rootPoint(ev.object));
|
|
2509
2171
|
break;
|
|
2510
2172
|
case "disassemble_progress": {
|
|
2511
2173
|
const k = `unscrew:${ev.joint}`;
|
|
@@ -2516,17 +2178,15 @@ var Renderer = class {
|
|
|
2516
2178
|
break;
|
|
2517
2179
|
}
|
|
2518
2180
|
case "ignite":
|
|
2519
|
-
this.
|
|
2181
|
+
this.startFire(ev);
|
|
2520
2182
|
this.activate(ev.object, ev.part);
|
|
2521
|
-
this.particles.burst("fire", ev.point, [0, 1, 0], 6, 1);
|
|
2522
2183
|
this.sound("ignite", ev.point, 0.7);
|
|
2523
2184
|
this.sound("burn_loop", ev.point, 0.5);
|
|
2524
2185
|
break;
|
|
2525
2186
|
case "primed":
|
|
2526
2187
|
if (ev.trigger !== "explosion") {
|
|
2527
|
-
this.
|
|
2188
|
+
this.vfx.fire({ point: ev.point, radius: PRIMED_FIRE_RADIUS, life: ev.delay });
|
|
2528
2189
|
this.activate(ev.object, ev.part);
|
|
2529
|
-
this.particles.burst("fire", ev.point, [0, 1, 0], 6, 1);
|
|
2530
2190
|
this.sound("ignite", ev.point, 0.7);
|
|
2531
2191
|
}
|
|
2532
2192
|
break;
|
|
@@ -2538,21 +2198,25 @@ var Renderer = class {
|
|
|
2538
2198
|
break;
|
|
2539
2199
|
case "expose":
|
|
2540
2200
|
this.cages.set(`${ev.object}|${ev.part}`, ev.bars);
|
|
2201
|
+
if (ev.bars.length > 0) {
|
|
2202
|
+
const [a, b] = ev.bars[0];
|
|
2203
|
+
this.playEvent({ kind: "expose" }, [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2, (a[2] + b[2]) / 2]);
|
|
2204
|
+
}
|
|
2541
2205
|
break;
|
|
2542
2206
|
case "extinguish": {
|
|
2543
2207
|
const f = this.fires.get(`${ev.object}|${ev.part}`);
|
|
2544
2208
|
if (f) {
|
|
2545
|
-
this.
|
|
2546
|
-
this.
|
|
2209
|
+
this.stopFire(`${ev.object}|${ev.part}`);
|
|
2210
|
+
this.vfx.burst("steam", f.point, [0, 1, 0], 14, 1.2);
|
|
2547
2211
|
}
|
|
2548
2212
|
this.activate(ev.object, ev.part);
|
|
2549
2213
|
this.sound("extinguish", f?.point ?? this.rootPoint(ev.object), 0.6);
|
|
2550
2214
|
break;
|
|
2551
2215
|
}
|
|
2552
2216
|
case "burnout": {
|
|
2553
|
-
this.
|
|
2554
|
-
this.
|
|
2555
|
-
this.
|
|
2217
|
+
this.stopFire(`${ev.object}|${ev.part}`);
|
|
2218
|
+
this.vfx.burst("ash", ev.point, [0, 1, 0], 16, 1);
|
|
2219
|
+
this.vfx.burst("smoke", ev.point, [0, 1, 0], 6, 0.8, { color: SMOKE_TONES.grey });
|
|
2556
2220
|
const pv = this.partVis(ev.object, ev.part);
|
|
2557
2221
|
if (pv) {
|
|
2558
2222
|
const def = this.world.materials.get(pv.material);
|
|
@@ -2575,7 +2239,7 @@ var Renderer = class {
|
|
|
2575
2239
|
const color = (pv && this.partColor(pv, def)) ?? fallbackColor(ev.material, def);
|
|
2576
2240
|
let tag = this.homeTags.indexOf(ev.object) + 1;
|
|
2577
2241
|
if (tag === 0) tag = this.homeTags.push(ev.object);
|
|
2578
|
-
this.
|
|
2242
|
+
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
2243
|
break;
|
|
2580
2244
|
}
|
|
2581
2245
|
case "leak": {
|
|
@@ -2666,7 +2330,7 @@ var Renderer = class {
|
|
|
2666
2330
|
if (!this.visuals.shown(mv)) continue;
|
|
2667
2331
|
this.visuals.applyDent(mv, wp, wn, ev.dent);
|
|
2668
2332
|
}
|
|
2669
|
-
if (def?.tags?.includes("metal")) this.
|
|
2333
|
+
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
2334
|
this.sound(impactKey(def), ev.worldPoint, clamp(0.3 + ev.dent.depth * 12, 0.3, 1));
|
|
2671
2335
|
}
|
|
2672
2336
|
onHole(ev) {
|
|
@@ -2688,7 +2352,7 @@ var Renderer = class {
|
|
|
2688
2352
|
};
|
|
2689
2353
|
draw(ev.worldPoint, n);
|
|
2690
2354
|
if (ev.hole.through && ev.exitPoint) draw(ev.exitPoint, n.clone().negate());
|
|
2691
|
-
if (def?.tags?.includes("metal")) this.
|
|
2355
|
+
if (def?.tags?.includes("metal")) this.vfx.burst("sparks", ev.worldPoint, [n.x, n.y, n.z], 6, 3.5);
|
|
2692
2356
|
this.sound(impactKey(def), ev.worldPoint, 0.6);
|
|
2693
2357
|
}
|
|
2694
2358
|
onCrack(ev) {
|
|
@@ -2712,6 +2376,20 @@ var Renderer = class {
|
|
|
2712
2376
|
const side = point[axis] >= (box.min[axis] + box.max[axis]) / 2 ? 1 : -1;
|
|
2713
2377
|
return new this.THREE.Vector3(axis === "x" ? side : 0, axis === "y" ? side : 0, axis === "z" ? side : 0);
|
|
2714
2378
|
}
|
|
2379
|
+
/** The material an impact effect is chosen by: the tags of the material (`metal`, `glass`, `wood`), else stone and the like are `concrete`. */
|
|
2380
|
+
impactMaterial(def) {
|
|
2381
|
+
for (const tag of IMPACT_TAGS) if (def?.tags?.includes(tag)) return tag;
|
|
2382
|
+
return "concrete";
|
|
2383
|
+
}
|
|
2384
|
+
/**
|
|
2385
|
+
* Plays the effect the event map names for a destruction event (`data/event-effects.json` of `game-vfx`, or `RendererOptions.eventEffects`) at a point.
|
|
2386
|
+
* Answers whether one played, so the caller can leave out the burst it replaces.
|
|
2387
|
+
*/
|
|
2388
|
+
playEvent(event, point) {
|
|
2389
|
+
const id = effectForEvent(event, this.o.eventEffects ?? EVENT_EFFECTS_MAP);
|
|
2390
|
+
if (!id) return false;
|
|
2391
|
+
return this.vfx.play(id, point, { seed: hashNumbers(point[0], point[1], point[2], this.time) }) !== null;
|
|
2392
|
+
}
|
|
2715
2393
|
/** A coat came off: a patch in the colour of the core under it, below the bullet holes and cracks. */
|
|
2716
2394
|
onSpall(ev) {
|
|
2717
2395
|
const pv = this.partVis(ev.object, ev.part);
|
|
@@ -2720,6 +2398,7 @@ var Renderer = class {
|
|
|
2720
2398
|
const mv = this.nearestMesh(pv, wp);
|
|
2721
2399
|
if (!mv) return;
|
|
2722
2400
|
const def = this.world.materials.get(pv.material);
|
|
2401
|
+
this.playEvent({ kind: "spall", material: this.impactMaterial(def) }, ev.point);
|
|
2723
2402
|
this.visuals.addSpall(mv, wp, this.faceNormal(mv, wp), Math.sqrt(ev.area / Math.PI), fallbackColor(pv.material, def));
|
|
2724
2403
|
}
|
|
2725
2404
|
/**
|
|
@@ -2727,6 +2406,7 @@ var Renderer = class {
|
|
|
2727
2406
|
* (cutting the wall's faces) is not drawn yet: it needs the per-vertex binding of the house renderer.
|
|
2728
2407
|
*/
|
|
2729
2408
|
onStage(ev) {
|
|
2409
|
+
const played = this.playEvent({ kind: "stage", stage: ev.stage }, ev.opening?.center ?? this.rootPoint(ev.object));
|
|
2730
2410
|
if (!ev.fragments || !ev.opening) return;
|
|
2731
2411
|
const pv = this.partVis(ev.object, ev.part);
|
|
2732
2412
|
const def = pv ? this.world.materials.get(pv.material) : void 0;
|
|
@@ -2738,7 +2418,7 @@ var Renderer = class {
|
|
|
2738
2418
|
const fdef = this.world.materials.get(fr.material);
|
|
2739
2419
|
this.fragments.spawn(fr, this.fragments.materialFor(fr.material, fdef === def || !fdef ? color : fallbackColor(fr.material, fdef), false), ev.object, maxAlive);
|
|
2740
2420
|
}
|
|
2741
|
-
this.
|
|
2421
|
+
if (!played) this.vfx.burst("dust", ev.opening.center, [0, 1, 0], 14, 1.2, { color });
|
|
2742
2422
|
this.sound(impactKey(def), ev.opening.center, 0.8);
|
|
2743
2423
|
}
|
|
2744
2424
|
onFracture(ev) {
|
|
@@ -2768,24 +2448,24 @@ var Renderer = class {
|
|
|
2768
2448
|
const sizeK = clamp(ev.fragments.length / 10, 0.5, 2);
|
|
2769
2449
|
switch (ev.style) {
|
|
2770
2450
|
case "shatter":
|
|
2771
|
-
this.
|
|
2451
|
+
this.vfx.burst("shards", ev.point, up, 18 * sizeK, 3);
|
|
2772
2452
|
break;
|
|
2773
2453
|
case "crumble":
|
|
2774
|
-
this.
|
|
2775
|
-
this.
|
|
2454
|
+
this.vfx.burst("dust", ev.point, up, 16 * sizeK, 1.2, { color: pv ? this.partColor(pv, def) ?? void 0 : void 0 });
|
|
2455
|
+
this.vfx.burst("chips", ev.point, up, 6, 3);
|
|
2776
2456
|
break;
|
|
2777
2457
|
case "splinter":
|
|
2778
|
-
this.
|
|
2779
|
-
this.
|
|
2458
|
+
this.vfx.burst("splinters", ev.point, up, 14 * sizeK, 3);
|
|
2459
|
+
this.vfx.burst("dust", ev.point, up, 5, 0.8, { color: 11901556 });
|
|
2780
2460
|
break;
|
|
2781
2461
|
case "tear":
|
|
2782
|
-
this.
|
|
2462
|
+
this.vfx.burst("fibers", ev.point, up, 12 * sizeK, 1.6, { color });
|
|
2783
2463
|
break;
|
|
2784
2464
|
case "snap":
|
|
2785
|
-
this.
|
|
2465
|
+
this.vfx.burst("chips", ev.point, up, 8 * sizeK, 3);
|
|
2786
2466
|
break;
|
|
2787
2467
|
default:
|
|
2788
|
-
this.
|
|
2468
|
+
this.vfx.burst("sparks", ev.point, up, 10, 4);
|
|
2789
2469
|
}
|
|
2790
2470
|
this.sound(fractureKey(def, ev.style), ev.point, clamp(0.4 + ev.fragments.length * 0.03, 0.4, 1));
|
|
2791
2471
|
}
|
|
@@ -2864,27 +2544,10 @@ var Renderer = class {
|
|
|
2864
2544
|
nb.piece = piece;
|
|
2865
2545
|
this.o.onPiece?.(piece);
|
|
2866
2546
|
}
|
|
2867
|
-
onExplode(ev) {
|
|
2547
|
+
onExplode(ev, options = {}) {
|
|
2868
2548
|
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
|
-
}
|
|
2549
|
+
if (options.visual !== false) this.playExplosion(ev, options);
|
|
2550
|
+
this.vfx.push(p, ev.radius, 4);
|
|
2888
2551
|
const c = this.v1;
|
|
2889
2552
|
for (const pc of this.pieceSys.list) {
|
|
2890
2553
|
this.pieceSys.centerOf(pc, c);
|
|
@@ -2903,11 +2566,23 @@ var Renderer = class {
|
|
|
2903
2566
|
this.fragments.blast(p, ev.radius, Math.min(20, Math.sqrt(ev.energy) * 0.08));
|
|
2904
2567
|
this.sound("explode", p, clamp(0.5 + ev.radius / 10, 0.5, 1));
|
|
2905
2568
|
}
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
const
|
|
2910
|
-
this.
|
|
2569
|
+
playExplosion(ev, options) {
|
|
2570
|
+
const named = options.effect ?? ev.effect;
|
|
2571
|
+
const direction = options.direction;
|
|
2572
|
+
const scale = clamp(ev.radius / GENERIC_REFERENCE_RADIUS, 0.2, 3);
|
|
2573
|
+
if (named !== void 0 && this.vfx.explosion(named, ev.point, direction ? { direction } : {}) !== null) return;
|
|
2574
|
+
this.vfx.explosion("generic", ev.point, { scale, ...direction ? { direction } : {} });
|
|
2575
|
+
}
|
|
2576
|
+
startFire(ev) {
|
|
2577
|
+
const key = `${ev.object}|${ev.part}`;
|
|
2578
|
+
this.stopFire(key);
|
|
2579
|
+
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] } });
|
|
2580
|
+
}
|
|
2581
|
+
stopFire(key) {
|
|
2582
|
+
const fire = this.fires.get(key);
|
|
2583
|
+
if (!fire) return;
|
|
2584
|
+
if (fire.handle !== void 0) this.vfx.stop(fire.handle);
|
|
2585
|
+
this.fires.delete(key);
|
|
2911
2586
|
}
|
|
2912
2587
|
// ----------------------------------------------------------------------------------------------------------------------------------
|
|
2913
2588
|
// Fire, heat, char, melt, leaks
|
|
@@ -2945,8 +2620,8 @@ var Renderer = class {
|
|
|
2945
2620
|
}
|
|
2946
2621
|
const fire = this.fires.get(key);
|
|
2947
2622
|
if (fire) {
|
|
2948
|
-
if (ps.burning && ps.status !== "broken" && ps.status !== "ashes" && ps.status !== "melted") this.emitFire(fire, pv, def
|
|
2949
|
-
else this.
|
|
2623
|
+
if (ps.burning && ps.status !== "broken" && ps.status !== "ashes" && ps.status !== "melted") this.emitFire(fire, pv, def);
|
|
2624
|
+
else this.stopFire(key);
|
|
2950
2625
|
}
|
|
2951
2626
|
const hot = def?.visual?.glows === true && ps.temperature > GLOW_FROM_C;
|
|
2952
2627
|
if (!ps.burning && !hot && Math.abs(ps.char - pv.charApplied) < 0.01 && Math.abs(ps.melt - pv.meltApplied) < 0.01) {
|
|
@@ -2967,43 +2642,25 @@ var Renderer = class {
|
|
|
2967
2642
|
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
2643
|
} else {
|
|
2969
2644
|
this.ground.add("ash", c.x, c.z, clamp(footprint * 0.35, 0.04, 0.5), 1907224, objectId);
|
|
2970
|
-
this.
|
|
2645
|
+
this.vfx.burst("ash", [c.x, bb.max.y, c.z], [0, 1, 0], 10, 0.8);
|
|
2971
2646
|
}
|
|
2972
2647
|
}
|
|
2973
|
-
emitFire(f, pv, def
|
|
2648
|
+
emitFire(f, pv, def) {
|
|
2974
2649
|
const bb = this.box;
|
|
2975
2650
|
bb.makeEmpty();
|
|
2976
2651
|
const tmp = new this.THREE.Box3();
|
|
2977
2652
|
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
2653
|
if (!bb.isEmpty()) {
|
|
2985
2654
|
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 });
|
|
2655
|
+
f.follow.x = c.x;
|
|
2656
|
+
f.follow.y = bb.min.y;
|
|
2657
|
+
f.follow.z = c.z;
|
|
3006
2658
|
}
|
|
2659
|
+
if (f.handle !== void 0) return;
|
|
2660
|
+
const size = bb.isEmpty() ? this.v2.set(0.2, 0.2, 0.2) : bb.getSize(this.v2);
|
|
2661
|
+
const radius = clamp(Math.max(size.x, size.z) * 0.5, 0.12, 1.2);
|
|
2662
|
+
const smokeColor = SMOKE_TONES[def?.thermal.smoke ?? "grey"];
|
|
2663
|
+
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
2664
|
}
|
|
3008
2665
|
updateLeaks(dt) {
|
|
3009
2666
|
for (let i = this.leaks.length - 1; i >= 0; i--) {
|
|
@@ -3021,8 +2678,8 @@ var Renderer = class {
|
|
|
3021
2678
|
while (l.acc >= 1) {
|
|
3022
2679
|
l.acc -= 1;
|
|
3023
2680
|
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.
|
|
2681
|
+
if (l.fluid === "gas") this.vfx.spawn("steam", l.point, vel, { color, size: 0.5, alpha: 0.5 });
|
|
2682
|
+
else this.vfx.spawn("drips", l.point, vel, { color, tag: this.rng() < 0.03 ? this.tagOf(l.object) : 0, owner: this.owner });
|
|
3026
2683
|
}
|
|
3027
2684
|
}
|
|
3028
2685
|
}
|
|
@@ -3035,10 +2692,10 @@ var Renderer = class {
|
|
|
3035
2692
|
// Emit, clear, restore, stats, dispose
|
|
3036
2693
|
// ----------------------------------------------------------------------------------------------------------------------------------
|
|
3037
2694
|
emit(kind, point, normal, count, speed) {
|
|
3038
|
-
this.
|
|
2695
|
+
this.vfx.burst(kind, point, normal, count, speed ?? DEFAULT_SPEED[kind] ?? 1, kind === "smoke" ? { color: SMOKE_TONES.grey } : void 0);
|
|
3039
2696
|
}
|
|
3040
|
-
explode(point, energy, radius) {
|
|
3041
|
-
this.onExplode({ type: "explode", object: "", part: "", time: this.time, point, energy, radius });
|
|
2697
|
+
explode(point, energy, radius, options) {
|
|
2698
|
+
this.onExplode({ type: "explode", object: "", part: "", time: this.time, point, energy, radius }, options);
|
|
3042
2699
|
}
|
|
3043
2700
|
restore(objectId) {
|
|
3044
2701
|
const homes = /* @__PURE__ */ new Set([objectId]);
|
|
@@ -3046,7 +2703,7 @@ var Renderer = class {
|
|
|
3046
2703
|
if (self?.isSplit) for (const pv of self.parts.values()) homes.add(pv.home);
|
|
3047
2704
|
for (const home of homes) this.restoreHome(home);
|
|
3048
2705
|
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.
|
|
2706
|
+
for (const [key, f] of [...this.fires]) if (homes.has(f.object)) this.stopFire(key);
|
|
3050
2707
|
for (let i = this.leaks.length - 1; i >= 0; i--) if (homes.has(this.leaks[i].object)) this.leaks.splice(i, 1);
|
|
3051
2708
|
for (const home of homes) {
|
|
3052
2709
|
this.fragments.removeHome(home);
|
|
@@ -3075,7 +2732,7 @@ var Renderer = class {
|
|
|
3075
2732
|
clear() {
|
|
3076
2733
|
this.queue.length = 0;
|
|
3077
2734
|
this.active.clear();
|
|
3078
|
-
this.fires.
|
|
2735
|
+
for (const key of [...this.fires.keys()]) this.stopFire(key);
|
|
3079
2736
|
this.leaks.length = 0;
|
|
3080
2737
|
for (const id of [...this.bounds.keys()]) if (this.bounds.has(id)) this.restore(id);
|
|
3081
2738
|
for (const b of [...this.bounds.values()]) {
|
|
@@ -3089,15 +2746,16 @@ var Renderer = class {
|
|
|
3089
2746
|
this.fragments.clear();
|
|
3090
2747
|
this.ground.clear();
|
|
3091
2748
|
this.cages.clear();
|
|
3092
|
-
this.
|
|
3093
|
-
|
|
3094
|
-
this.light.intensity = 0;
|
|
3095
|
-
this.lightT = 0;
|
|
3096
|
-
this.scene.remove(this.light);
|
|
3097
|
-
}
|
|
2749
|
+
this.vfx.sprites().clear();
|
|
2750
|
+
this.ownVfx?.world.clear();
|
|
3098
2751
|
}
|
|
3099
2752
|
stats() {
|
|
3100
|
-
return { fragments: this.fragments.count, particles: this.
|
|
2753
|
+
return { fragments: this.fragments.count, particles: this.vfx.sprites().live + this.ownedVfxParticles(), pieces: this.pieceSys.list.length, decals: this.visuals.decalCount + this.ground.count };
|
|
2754
|
+
}
|
|
2755
|
+
ownedVfxParticles() {
|
|
2756
|
+
if (!this.ownVfx) return 0;
|
|
2757
|
+
const { puffs, sparks, glows, chunks } = this.vfx.stats();
|
|
2758
|
+
return puffs + sparks + glows + chunks;
|
|
3101
2759
|
}
|
|
3102
2760
|
rebarCount() {
|
|
3103
2761
|
return this.cages.count;
|
|
@@ -3110,10 +2768,9 @@ var Renderer = class {
|
|
|
3110
2768
|
this.fragments.dispose();
|
|
3111
2769
|
this.ground.dispose();
|
|
3112
2770
|
this.cages.dispose();
|
|
3113
|
-
this.
|
|
2771
|
+
this.stopLanding();
|
|
3114
2772
|
this.visuals.dispose();
|
|
3115
|
-
this.
|
|
3116
|
-
this.light = null;
|
|
2773
|
+
this.ownVfx?.dispose();
|
|
3117
2774
|
this.disposed = true;
|
|
3118
2775
|
}
|
|
3119
2776
|
};
|