@adatechnology/game-destruction 0.15.0 → 0.17.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 +13 -1
- package/dist/index.d.ts +12 -1
- package/dist/index.global.js +436 -17
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +435 -17
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -340,6 +340,22 @@ var frame = (ax, ay, az, angle = 0) => {
|
|
|
340
340
|
return angle === 0 ? q : quat.mul(q, quat.fromAxisAngle([0, 0, 1], angle));
|
|
341
341
|
};
|
|
342
342
|
var clamp = (v, a, b) => Math.max(a, Math.min(b, v));
|
|
343
|
+
var splitAlong = (box, count, axis, rnd) => {
|
|
344
|
+
const shares = Array.from({ length: count }, () => 0.25 + 1.5 * rnd());
|
|
345
|
+
const total = shares.reduce((sum, share) => sum + share, 0);
|
|
346
|
+
const out = [];
|
|
347
|
+
let from = box.c[axis] - box.s[axis] / 2;
|
|
348
|
+
for (const share of shares) {
|
|
349
|
+
const length = box.s[axis] * share / total;
|
|
350
|
+
const c = [box.c[0], box.c[1], box.c[2]];
|
|
351
|
+
const size = [box.s[0], box.s[1], box.s[2]];
|
|
352
|
+
c[axis] = from + length / 2;
|
|
353
|
+
size[axis] = length;
|
|
354
|
+
out.push({ c, s: size });
|
|
355
|
+
from += length;
|
|
356
|
+
}
|
|
357
|
+
return out;
|
|
358
|
+
};
|
|
343
359
|
var splinterBox = (inp, box, limit = inp.maxFragments) => {
|
|
344
360
|
const { rnd } = inp;
|
|
345
361
|
const dims = box.s;
|
|
@@ -349,35 +365,81 @@ var splinterBox = (inp, box, limit = inp.maxFragments) => {
|
|
|
349
365
|
const w = Math.max(fmin * 1.5, 0.01);
|
|
350
366
|
const c = clamp(Math.round(dims[a] * dims[b] / (w * w)), 3, Math.max(3, Math.floor(limit / 2)));
|
|
351
367
|
const maxLen = fmax * 4;
|
|
352
|
-
const cells =
|
|
368
|
+
const cells = [];
|
|
369
|
+
for (const cell of bsp(box, c, [a, b], rnd)) {
|
|
370
|
+
const wide = cell.s[a] >= cell.s[b] ? a : b;
|
|
371
|
+
if (rnd() < 0.4 && cell.s[wide] > fmin * 0.5) for (const part of splitAlong(cell, 2, wide, rnd)) cells.push(part);
|
|
372
|
+
else cells.push(cell);
|
|
373
|
+
}
|
|
353
374
|
const pieces = [];
|
|
354
375
|
for (const cell of cells) {
|
|
355
|
-
const extra = rnd()
|
|
376
|
+
const extra = Math.floor(rnd() * 3);
|
|
356
377
|
const k = Math.max(1, Math.ceil(dims[g] / maxLen)) + extra;
|
|
357
|
-
for (const p of
|
|
378
|
+
for (const p of splitAlong(cell, k, g, rnd)) pieces.push(p);
|
|
358
379
|
}
|
|
359
380
|
return pieces.slice(0, limit).map((p) => ({ center: p.c, size: [p.s[g], p.s[a], p.s[b]], q: frame(g, a, b), shape: "splinter", weight: p.s[0] * p.s[1] * p.s[2] }));
|
|
360
381
|
};
|
|
382
|
+
var PLY = 3e-3;
|
|
383
|
+
var PLY_GAP = 0.6;
|
|
384
|
+
var PLY_SHEET = 0.012;
|
|
385
|
+
var plyBreak = (inp, root, limit) => {
|
|
386
|
+
const { rnd } = inp;
|
|
387
|
+
const dims = root.s;
|
|
388
|
+
const sorted = [0, 1, 2].sort((x, y) => dims[x] - dims[y]);
|
|
389
|
+
const t = sorted[0];
|
|
390
|
+
const [u, v] = [sorted[1], sorted[2]].sort((x, y) => x - y);
|
|
391
|
+
const along = dims[u] >= dims[v] ? u : v;
|
|
392
|
+
const across = along === u ? v : u;
|
|
393
|
+
const layers = clamp(Math.round(dims[t] / PLY / 2), 2, 3);
|
|
394
|
+
const pitch = dims[t] / layers;
|
|
395
|
+
const th = Math.min(pitch, PLY_SHEET);
|
|
396
|
+
const per = Math.max(2, Math.floor(limit / layers));
|
|
397
|
+
const out = [];
|
|
398
|
+
for (let k = 0; k < layers; k++) {
|
|
399
|
+
const layer = { c: [root.c[0], root.c[1], root.c[2]], s: [dims[0], dims[1], dims[2]] };
|
|
400
|
+
layer.c[t] = root.c[t] + (-dims[t] / 2 + (k + 0.5) * pitch) * (pitch > PLY_SHEET ? 1 : 1 + PLY_GAP);
|
|
401
|
+
layer.s[t] = th;
|
|
402
|
+
for (const cell of bsp(layer, per, [u, v], rnd)) {
|
|
403
|
+
if (out.length >= limit) return out;
|
|
404
|
+
if (rnd() < 0.35) {
|
|
405
|
+
const strand = cell.s[across] * (0.15 + 0.2 * rnd());
|
|
406
|
+
out.push({ center: cell.c, size: [cell.s[along], strand, th * 0.7], q: frame(along, across, t), shape: "splinter", weight: cell.s[along] * strand * th });
|
|
407
|
+
} else {
|
|
408
|
+
out.push({ center: cell.c, size: [cell.s[u], cell.s[v], th], q: frame(u, v, t, (rnd() - 0.5) * 0.5), shape: "scrap", weight: cell.s[u] * cell.s[v] * th });
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return out;
|
|
413
|
+
};
|
|
414
|
+
var granules = (inp, box, count) => bsp(box, count, [0, 1, 2], inp.rnd).map((b) => ({ center: b.c, size: b.s, q: quat.identity(), shape: "chunk", weight: b.s[0] * b.s[1] * b.s[2] }));
|
|
415
|
+
var CRUMBS = 6;
|
|
361
416
|
var severAt = (inp, root, at, gap) => {
|
|
362
417
|
const dims = root.s;
|
|
363
418
|
const L = [0, 1, 2].sort((x, y) => dims[y] - dims[x])[0];
|
|
364
419
|
const half = dims[L] / 2;
|
|
365
420
|
const lo = clamp(at - gap / 2, -half, half), hi = clamp(at + gap / 2, -half, half);
|
|
366
421
|
const out = [];
|
|
367
|
-
const
|
|
422
|
+
const fibrous = inp.form === "slivers" || inp.form === "plies";
|
|
423
|
+
const [a, b] = [0, 1, 2].filter((i) => i !== L);
|
|
424
|
+
const stub = (from, to, breakAtEnd) => {
|
|
368
425
|
if (to - from < 0.02) return;
|
|
369
426
|
const c = [root.c[0], root.c[1], root.c[2]], sz = [dims[0], dims[1], dims[2]];
|
|
370
427
|
c[L] = (from + to) / 2;
|
|
371
428
|
sz[L] = to - from;
|
|
372
|
-
|
|
429
|
+
const weight = sz[0] * sz[1] * sz[2];
|
|
430
|
+
if (fibrous) out.push({ center: c, size: [sz[L], sz[a], sz[b]], q: frame(L, a, b, breakAtEnd ? 0 : Math.PI), shape: "slab", weight });
|
|
431
|
+
else out.push({ center: c, size: sz, q: quat.identity(), shape: "slab", weight });
|
|
373
432
|
};
|
|
374
|
-
stub(-half, lo);
|
|
375
|
-
stub(hi, half);
|
|
433
|
+
stub(-half, lo, true);
|
|
434
|
+
stub(hi, half, false);
|
|
376
435
|
const band = { c: [root.c[0], root.c[1], root.c[2]], s: [dims[0], dims[1], dims[2]] };
|
|
377
436
|
band.c[L] = (lo + hi) / 2;
|
|
378
437
|
band.s[L] = Math.max(hi - lo, 0.01);
|
|
379
|
-
|
|
380
|
-
|
|
438
|
+
const room = inp.maxFragments - out.length;
|
|
439
|
+
if (inp.style === "snap" && inp.form === "plies") for (const p of plyBreak(inp, band, Math.max(3, room))) out.push(p);
|
|
440
|
+
else if (inp.style === "snap" && inp.form === "granules") for (const p of granules(inp, band, clamp(room, 3, 12))) out.push(p);
|
|
441
|
+
else if (inp.style === "snap") {
|
|
442
|
+
for (const b2 of bsp(band, Math.max(3, Math.min(6, room)), [0, 1, 2], inp.rnd)) out.push({ center: b2.c, size: b2.s, q: quat.identity(), shape: "chunk", weight: b2.s[0] * b2.s[1] * b2.s[2] });
|
|
381
443
|
} else for (const p of splinterBox(inp, band, Math.max(3, inp.maxFragments - out.length))) out.push(p);
|
|
382
444
|
return out;
|
|
383
445
|
};
|
|
@@ -410,8 +472,18 @@ var planFragments = (inp) => {
|
|
|
410
472
|
const [u, v] = [sorted[1], sorted[2]].sort((a, b) => a - b);
|
|
411
473
|
if (style === "snap") {
|
|
412
474
|
if (inp.cut) return severAt(inp, root, inp.cut.at, inp.cut.gap);
|
|
475
|
+
if (inp.form === "plies") return plyBreak(inp, root, maxFragments);
|
|
413
476
|
const n2 = Math.min(maxFragments, 2 + Math.floor(rnd() * 3));
|
|
414
477
|
for (const b of bsp(root, n2, [0, 1, 2], rnd)) out.push({ center: b.c, size: b.s, q: idq, shape: "slab", weight: b.s[0] * b.s[1] * b.s[2] });
|
|
478
|
+
if (inp.form !== "granules") return out;
|
|
479
|
+
const crumbs = Math.min(CRUMBS, maxFragments - out.length);
|
|
480
|
+
if (crumbs <= 0) return out;
|
|
481
|
+
const band = { c: [0, 0, 0], s: [0, 0, 0] };
|
|
482
|
+
for (let i = 0; i < 3; i++) {
|
|
483
|
+
band.s[i] = Math.min(dims[i], Math.max(fmin, dims[i] * 0.2));
|
|
484
|
+
band.c[i] = clamp(inp.hit[i], -dims[i] / 2 + band.s[i] / 2, dims[i] / 2 - band.s[i] / 2);
|
|
485
|
+
}
|
|
486
|
+
for (const p of granules(inp, band, crumbs)) out.push({ ...p, weight: p.weight * 0.5 });
|
|
415
487
|
return out;
|
|
416
488
|
}
|
|
417
489
|
if (style === "crumble" || style === "none") {
|
|
@@ -520,6 +592,88 @@ var marksInBox = (center, size, rotation, holes) => {
|
|
|
520
592
|
}
|
|
521
593
|
return marks;
|
|
522
594
|
};
|
|
595
|
+
var JOINT_GAP = 1e-4;
|
|
596
|
+
var MIN_BEARING_SHARE = 0.25;
|
|
597
|
+
var unsupportedCells = (cells, thin, dims, standing) => {
|
|
598
|
+
if (thin === 1) return [];
|
|
599
|
+
const [u, v] = planeAxes(thin);
|
|
600
|
+
const bottom = -dims[v] / 2;
|
|
601
|
+
const rowOf = (y) => Math.round((y - bottom) * 1e4);
|
|
602
|
+
const rows = /* @__PURE__ */ new Map();
|
|
603
|
+
for (let i = 0; i < cells.length; i++) {
|
|
604
|
+
if (!standing(i)) continue;
|
|
605
|
+
const cell = cells[i];
|
|
606
|
+
const key = rowOf(cell.center[v] - cell.size[v] / 2);
|
|
607
|
+
const row = rows.get(key);
|
|
608
|
+
if (row) row.push(i);
|
|
609
|
+
else rows.set(key, [i]);
|
|
610
|
+
}
|
|
611
|
+
const held = /* @__PURE__ */ new Set();
|
|
612
|
+
const queue = [];
|
|
613
|
+
for (const i of rows.get(0) ?? []) {
|
|
614
|
+
held.add(i);
|
|
615
|
+
queue.push(i);
|
|
616
|
+
}
|
|
617
|
+
const overlap = (a, b) => Math.min(a.center[u] + a.size[u] / 2, b.center[u] + b.size[u] / 2) - Math.max(a.center[u] - a.size[u] / 2, b.center[u] - b.size[u] / 2);
|
|
618
|
+
const climb = () => {
|
|
619
|
+
while (queue.length > 0) {
|
|
620
|
+
const cell = cells[queue.pop()];
|
|
621
|
+
for (const j of rows.get(rowOf(cell.center[v] + cell.size[v] / 2)) ?? []) {
|
|
622
|
+
const other = cells[j];
|
|
623
|
+
if (held.has(j) || overlap(cell, other) <= Math.max(JOINT_GAP, MIN_BEARING_SHARE * Math.min(cell.size[u], other.size[u]))) continue;
|
|
624
|
+
held.add(j);
|
|
625
|
+
queue.push(j);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
};
|
|
629
|
+
for (const row of rows.values()) row.sort((a, b) => cells[a].center[u] - cells[b].center[u]);
|
|
630
|
+
const touching = (a, b) => Math.abs(a.center[u] + a.size[u] / 2 - (b.center[u] - b.size[u] / 2)) <= 1e-3;
|
|
631
|
+
for (let wedged = true; wedged; ) {
|
|
632
|
+
climb();
|
|
633
|
+
wedged = false;
|
|
634
|
+
for (const row of rows.values()) {
|
|
635
|
+
for (let k = 1; k < row.length - 1; k++) {
|
|
636
|
+
const i = row[k];
|
|
637
|
+
if (held.has(i) || !held.has(row[k - 1]) || !held.has(row[k + 1])) continue;
|
|
638
|
+
if (!touching(cells[row[k - 1]], cells[i]) || !touching(cells[i], cells[row[k + 1]])) continue;
|
|
639
|
+
held.add(i);
|
|
640
|
+
queue.push(i);
|
|
641
|
+
wedged = true;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
const loose = [];
|
|
646
|
+
for (const row of rows.values()) for (const i of row) if (!held.has(i)) loose.push(i);
|
|
647
|
+
return loose;
|
|
648
|
+
};
|
|
649
|
+
var clumpCells = (cells, thin, cap) => {
|
|
650
|
+
if (cells.length <= cap) return [...cells];
|
|
651
|
+
const [u, v] = planeAxes(thin);
|
|
652
|
+
let lo0 = Infinity, lo1 = Infinity, hi0 = -Infinity, hi1 = -Infinity;
|
|
653
|
+
for (const cell of cells) {
|
|
654
|
+
lo0 = Math.min(lo0, cell.center[u] - cell.size[u] / 2);
|
|
655
|
+
hi0 = Math.max(hi0, cell.center[u] + cell.size[u] / 2);
|
|
656
|
+
lo1 = Math.min(lo1, cell.center[v] - cell.size[v] / 2);
|
|
657
|
+
hi1 = Math.max(hi1, cell.center[v] + cell.size[v] / 2);
|
|
658
|
+
}
|
|
659
|
+
let side = Math.sqrt((hi0 - lo0) * (hi1 - lo1) / cap);
|
|
660
|
+
for (; ; ) {
|
|
661
|
+
const bins = /* @__PURE__ */ new Map();
|
|
662
|
+
for (const cell of cells) {
|
|
663
|
+
const key = `${Math.floor((cell.center[u] - lo0) / side)}:${Math.floor((cell.center[v] - lo1) / side)}`;
|
|
664
|
+
const bin = bins.get(key) ?? { lo: [Infinity, Infinity, Infinity], hi: [-Infinity, -Infinity, -Infinity] };
|
|
665
|
+
for (let k = 0; k < 3; k++) {
|
|
666
|
+
bin.lo[k] = Math.min(bin.lo[k], cell.center[k] - cell.size[k] / 2);
|
|
667
|
+
bin.hi[k] = Math.max(bin.hi[k], cell.center[k] + cell.size[k] / 2);
|
|
668
|
+
}
|
|
669
|
+
bins.set(key, bin);
|
|
670
|
+
}
|
|
671
|
+
if (bins.size <= cap) {
|
|
672
|
+
return [...bins.values()].map(({ lo, hi }) => ({ center: [(lo[0] + hi[0]) / 2, (lo[1] + hi[1]) / 2, (lo[2] + hi[2]) / 2], size: [hi[0] - lo[0], hi[1] - lo[1], hi[2] - lo[2]] }));
|
|
673
|
+
}
|
|
674
|
+
side *= 1.25;
|
|
675
|
+
}
|
|
676
|
+
};
|
|
523
677
|
|
|
524
678
|
// src/layers.ts
|
|
525
679
|
var MAX_SPALLS = 64;
|
|
@@ -1560,6 +1714,7 @@ var materials_default = [
|
|
|
1560
1714
|
},
|
|
1561
1715
|
{
|
|
1562
1716
|
id: "wood_soft",
|
|
1717
|
+
splitForm: "slivers",
|
|
1563
1718
|
density: 500,
|
|
1564
1719
|
hpPerKg: 90,
|
|
1565
1720
|
penetration: 9,
|
|
@@ -1605,6 +1760,7 @@ var materials_default = [
|
|
|
1605
1760
|
},
|
|
1606
1761
|
{
|
|
1607
1762
|
id: "wood_hard",
|
|
1763
|
+
splitForm: "slivers",
|
|
1608
1764
|
density: 750,
|
|
1609
1765
|
hpPerKg: 140,
|
|
1610
1766
|
penetration: 14,
|
|
@@ -1649,6 +1805,7 @@ var materials_default = [
|
|
|
1649
1805
|
},
|
|
1650
1806
|
{
|
|
1651
1807
|
id: "plywood",
|
|
1808
|
+
splitForm: "plies",
|
|
1652
1809
|
density: 600,
|
|
1653
1810
|
hpPerKg: 70,
|
|
1654
1811
|
penetration: 7,
|
|
@@ -1696,6 +1853,7 @@ var materials_default = [
|
|
|
1696
1853
|
},
|
|
1697
1854
|
{
|
|
1698
1855
|
id: "mdf",
|
|
1856
|
+
splitForm: "granules",
|
|
1699
1857
|
density: 750,
|
|
1700
1858
|
hpPerKg: 45,
|
|
1701
1859
|
penetration: 6,
|
|
@@ -2207,6 +2365,7 @@ var materials_default = [
|
|
|
2207
2365
|
},
|
|
2208
2366
|
{
|
|
2209
2367
|
id: "green_wood",
|
|
2368
|
+
splitForm: "slivers",
|
|
2210
2369
|
density: 650,
|
|
2211
2370
|
hpPerKg: 5,
|
|
2212
2371
|
penetration: 10,
|
|
@@ -2487,6 +2646,125 @@ var materials_default = [
|
|
|
2487
2646
|
"brittle"
|
|
2488
2647
|
],
|
|
2489
2648
|
compressive: 1e5
|
|
2649
|
+
},
|
|
2650
|
+
{
|
|
2651
|
+
id: "mud",
|
|
2652
|
+
density: 1700,
|
|
2653
|
+
hpPerKg: 1,
|
|
2654
|
+
penetration: 5,
|
|
2655
|
+
ductility: 0.8,
|
|
2656
|
+
fracture: "crumble",
|
|
2657
|
+
fragmentSize: [
|
|
2658
|
+
0.02,
|
|
2659
|
+
0.08
|
|
2660
|
+
],
|
|
2661
|
+
hitDebris: "dust",
|
|
2662
|
+
breakDebris: "dust",
|
|
2663
|
+
thermal: {
|
|
2664
|
+
conductivity: 0.5,
|
|
2665
|
+
heatCapacity: 3.2
|
|
2666
|
+
},
|
|
2667
|
+
tags: [
|
|
2668
|
+
"soil",
|
|
2669
|
+
"wet"
|
|
2670
|
+
],
|
|
2671
|
+
compressive: 5e3
|
|
2672
|
+
},
|
|
2673
|
+
{
|
|
2674
|
+
id: "clay",
|
|
2675
|
+
density: 1800,
|
|
2676
|
+
hpPerKg: 5,
|
|
2677
|
+
penetration: 7,
|
|
2678
|
+
ductility: 0.05,
|
|
2679
|
+
fracture: "crumble",
|
|
2680
|
+
fragmentSize: [
|
|
2681
|
+
0.02,
|
|
2682
|
+
0.1
|
|
2683
|
+
],
|
|
2684
|
+
hitDebris: "chips",
|
|
2685
|
+
breakDebris: "dust",
|
|
2686
|
+
thermal: {
|
|
2687
|
+
conductivity: 0.15,
|
|
2688
|
+
heatCapacity: 2.2
|
|
2689
|
+
},
|
|
2690
|
+
tags: [
|
|
2691
|
+
"soil",
|
|
2692
|
+
"brittle"
|
|
2693
|
+
],
|
|
2694
|
+
compressive: 6e4
|
|
2695
|
+
},
|
|
2696
|
+
{
|
|
2697
|
+
id: "snow",
|
|
2698
|
+
density: 300,
|
|
2699
|
+
hpPerKg: 1,
|
|
2700
|
+
penetration: 3,
|
|
2701
|
+
ductility: 0.6,
|
|
2702
|
+
fracture: "crumble",
|
|
2703
|
+
fragmentSize: [
|
|
2704
|
+
0.01,
|
|
2705
|
+
0.05
|
|
2706
|
+
],
|
|
2707
|
+
hitDebris: "dust",
|
|
2708
|
+
breakDebris: "dust",
|
|
2709
|
+
thermal: {
|
|
2710
|
+
conductivity: 0.1,
|
|
2711
|
+
heatCapacity: 2.1
|
|
2712
|
+
},
|
|
2713
|
+
tags: [
|
|
2714
|
+
"soil",
|
|
2715
|
+
"frozen"
|
|
2716
|
+
],
|
|
2717
|
+
compressive: 1e3
|
|
2718
|
+
},
|
|
2719
|
+
{
|
|
2720
|
+
id: "cobble",
|
|
2721
|
+
density: 2650,
|
|
2722
|
+
hpPerKg: 25,
|
|
2723
|
+
penetration: 25,
|
|
2724
|
+
penetrationClass: "concrete",
|
|
2725
|
+
penetrationScale: 0.9,
|
|
2726
|
+
ductility: 0.03,
|
|
2727
|
+
fracture: "crumble",
|
|
2728
|
+
fragmentSize: [
|
|
2729
|
+
0.05,
|
|
2730
|
+
0.15
|
|
2731
|
+
],
|
|
2732
|
+
hitDebris: "chips",
|
|
2733
|
+
breakDebris: "chips",
|
|
2734
|
+
thermal: {
|
|
2735
|
+
conductivity: 0.2,
|
|
2736
|
+
heatCapacity: 2.5
|
|
2737
|
+
},
|
|
2738
|
+
tags: [
|
|
2739
|
+
"masonry",
|
|
2740
|
+
"brittle"
|
|
2741
|
+
],
|
|
2742
|
+
compressive: 3e5
|
|
2743
|
+
},
|
|
2744
|
+
{
|
|
2745
|
+
id: "paver",
|
|
2746
|
+
density: 2300,
|
|
2747
|
+
hpPerKg: 14,
|
|
2748
|
+
penetration: 18,
|
|
2749
|
+
penetrationClass: "concrete",
|
|
2750
|
+
penetrationScale: 1,
|
|
2751
|
+
ductility: 0.03,
|
|
2752
|
+
fracture: "crumble",
|
|
2753
|
+
fragmentSize: [
|
|
2754
|
+
0.04,
|
|
2755
|
+
0.12
|
|
2756
|
+
],
|
|
2757
|
+
hitDebris: "chips",
|
|
2758
|
+
breakDebris: "chips",
|
|
2759
|
+
thermal: {
|
|
2760
|
+
conductivity: 0.15,
|
|
2761
|
+
heatCapacity: 2.5
|
|
2762
|
+
},
|
|
2763
|
+
tags: [
|
|
2764
|
+
"masonry",
|
|
2765
|
+
"brittle"
|
|
2766
|
+
],
|
|
2767
|
+
compressive: 35e4
|
|
2490
2768
|
}
|
|
2491
2769
|
];
|
|
2492
2770
|
|
|
@@ -2711,6 +2989,11 @@ var BREACH_RADIUS = 0.55;
|
|
|
2711
2989
|
var BREACH_BLAST_FACTOR = 6;
|
|
2712
2990
|
var BREACH_AREA = 0.25;
|
|
2713
2991
|
var BREACH_BLAST_REACH = 0.12;
|
|
2992
|
+
var MEMBER_CUT_REACH = 0.5;
|
|
2993
|
+
var BLAST_REFERENCE_RESISTANCE = 200 * 1900 * 0.13;
|
|
2994
|
+
var BLAST_EDGE_RAGGED = 0.3;
|
|
2995
|
+
var BLAST_EDGE_LOOSE = 1.35;
|
|
2996
|
+
var BLAST_EDGE_WEAR = 0.6;
|
|
2714
2997
|
var RESIDUAL_CAPACITY = 0.15;
|
|
2715
2998
|
var SEVER_BAND = 0.1;
|
|
2716
2999
|
var SEVER_ASPECT = 3;
|
|
@@ -3963,7 +4246,7 @@ var createDestruction = (options = {}) => {
|
|
|
3963
4246
|
if (j.st.open) continue;
|
|
3964
4247
|
if (j.spec.kind === "rebar" && (isReinforced(o.parts[j.ia].spec) || isReinforced(o.parts[j.ib].spec))) continue;
|
|
3965
4248
|
if (near && jointReach(o, j, near) > Math.max(radius, jointReachLimit(o, j))) continue;
|
|
3966
|
-
j.st.hp -= span === void 0 ? amount : amount * Math.min(1, span / Math.max(jointLength(o, j), 1e-6));
|
|
4249
|
+
j.st.hp -= span === void 0 ? amount : Math.min(amount, j.st.maxHp) * Math.min(1, span / Math.max(jointLength(o, j), 1e-6));
|
|
3967
4250
|
if (j.st.hp <= 0) {
|
|
3968
4251
|
j.st.hp = 0;
|
|
3969
4252
|
j.st.open = true;
|
|
@@ -4091,6 +4374,7 @@ var createDestruction = (options = {}) => {
|
|
|
4091
4374
|
const worn = (_a = p.rt).chips ?? (_a.chips = {});
|
|
4092
4375
|
const cut = (_b = p.rt).craters ?? (_b.craters = {});
|
|
4093
4376
|
const out = [];
|
|
4377
|
+
const wholeBefore = Object.values(worn).filter((w) => w === -1).length;
|
|
4094
4378
|
const shot = kind === "ballistic";
|
|
4095
4379
|
for (let i = 0; i < grid.length; i++) {
|
|
4096
4380
|
const cell = grid[i];
|
|
@@ -4149,6 +4433,7 @@ var createDestruction = (options = {}) => {
|
|
|
4149
4433
|
emit({ ...base(o, src), type: "chip", part: p.spec.id, center, size: [cell.size[0], cell.size[1], cell.size[2]], rotation: q, fragments });
|
|
4150
4434
|
debris(o, p, p.mat.breakDebris, center, vec.scale(dir ?? [0, 1, 0], -1), 6, 2, src);
|
|
4151
4435
|
}
|
|
4436
|
+
if (Object.values(worn).filter((w) => w === -1).length > wholeBefore) dropUnsupported(o, p, src);
|
|
4152
4437
|
if (p.spec.reinforcement) exposeRebar(o, p, src);
|
|
4153
4438
|
for (const cell of out) addBreach(o, p, toWorld(c, q, cell.center), cell.size[u] * cell.size[v], energy, dir, src);
|
|
4154
4439
|
if (p.structural && !isLost(p)) {
|
|
@@ -4252,10 +4537,122 @@ var createDestruction = (options = {}) => {
|
|
|
4252
4537
|
setStage(o, p, 1, {}, src);
|
|
4253
4538
|
setStage(o, p, 2, { opening: { center: worldCenter, radius }, fragments }, src);
|
|
4254
4539
|
debris(o, p, p.mat.breakDebris, worldCenter, vec.scale(dir ?? [0, 1, 0], -1), 14, 3, src);
|
|
4540
|
+
dropUnsupported(o, p, src);
|
|
4255
4541
|
const reinforcement = p.spec.reinforcement;
|
|
4256
4542
|
if (reinforcement) exposeRebar(o, p, src);
|
|
4257
4543
|
if (p.structural) solveStructure(o);
|
|
4258
4544
|
};
|
|
4545
|
+
const blastReachOf = (p, energy) => {
|
|
4546
|
+
const perKg = p.mat.hits?.blunt?.unitJoules ?? chipEnergy;
|
|
4547
|
+
const resistance = Math.max(1, perKg * p.mat.density * p.thickness);
|
|
4548
|
+
return BREACH_BLAST_REACH * Math.cbrt(Math.max(0, energy) / 1e3) * clamp2(Math.cbrt(BLAST_REFERENCE_RESISTANCE / resistance), 0.3, 2);
|
|
4549
|
+
};
|
|
4550
|
+
const blastUnits = (o, p, wp, energy, dist, src) => {
|
|
4551
|
+
var _a, _b;
|
|
4552
|
+
if (!p.staged || p.st.stripped || isLost(p) || chipEnergy <= 0) return false;
|
|
4553
|
+
const reach = blastReachOf(p, energy);
|
|
4554
|
+
if (dist >= reach) return false;
|
|
4555
|
+
const radius = Math.sqrt(reach * reach - dist * dist);
|
|
4556
|
+
const [u, v] = planeAxes(p.thin);
|
|
4557
|
+
const lp = toLocal(p.pos, p.q, objLocal(o, wp));
|
|
4558
|
+
const op = p.st.opening ? toLocal(p.pos, p.q, p.st.opening.center) : null;
|
|
4559
|
+
const grid = gridOf(p);
|
|
4560
|
+
const worn = (_a = p.rt).chips ?? (_a.chips = {});
|
|
4561
|
+
const perKg = p.mat.hits?.blunt?.unitJoules ?? chipEnergy;
|
|
4562
|
+
const lobes = [rnd() * 2 * Math.PI, rnd() * 2 * Math.PI];
|
|
4563
|
+
const out = [];
|
|
4564
|
+
for (let i = 0; i < grid.length; i++) {
|
|
4565
|
+
const cell = grid[i];
|
|
4566
|
+
if (worn[i] === -1) continue;
|
|
4567
|
+
if (op && p.st.opening && Math.hypot(cell.center[u] - op[u], cell.center[v] - op[v]) <= p.st.opening.radius) continue;
|
|
4568
|
+
const du = cell.center[u] - lp[u], dv = cell.center[v] - lp[v];
|
|
4569
|
+
const angle = Math.atan2(dv, du);
|
|
4570
|
+
const edge = radius * (1 + BLAST_EDGE_RAGGED * (0.5 * Math.sin(3 * angle + lobes[0]) + 0.3 * Math.sin(5 * angle + lobes[1]) + 0.4 * (rnd() - 0.5)));
|
|
4571
|
+
const d = Math.hypot(du, dv);
|
|
4572
|
+
if (d <= edge) {
|
|
4573
|
+
worn[i] = -1;
|
|
4574
|
+
out.push(i);
|
|
4575
|
+
} else if (d <= edge * BLAST_EDGE_LOOSE) {
|
|
4576
|
+
const mass = p.mat.density * cell.size[0] * cell.size[1] * cell.size[2];
|
|
4577
|
+
worn[i] = Math.max(worn[i] ?? 0, perKg * mass * BLAST_EDGE_WEAR);
|
|
4578
|
+
}
|
|
4579
|
+
}
|
|
4580
|
+
if (out.length === 0) return false;
|
|
4581
|
+
const loose = unsupportedCells(grid, p.thin, p.dims, (i) => worn[i] !== -1 && !(op && p.st.opening && Math.hypot(grid[i].center[u] - op[u], grid[i].center[v] - op[v]) <= p.st.opening.radius));
|
|
4582
|
+
for (const i of loose) worn[i] = -1;
|
|
4583
|
+
const blown = out.map((i) => grid[i]), fallen = loose.map((i) => grid[i]);
|
|
4584
|
+
const volume = (cells2) => cells2.reduce((sum, cell) => sum + cell.size[0] * cell.size[1] * cell.size[2], 0);
|
|
4585
|
+
const blownMass = Math.min(p.st.mass, p.mat.density * volume(blown));
|
|
4586
|
+
const fallenMass = Math.min(p.st.mass - blownMass, p.mat.density * volume(fallen));
|
|
4587
|
+
const speed = clamp2(Math.sqrt(2 * energy * 0.02 / Math.max(blownMass, 0.05)), 1, 12);
|
|
4588
|
+
const { c, q } = partXf(o, p);
|
|
4589
|
+
const chipped = (_b = p.st).chipped ?? (_b.chipped = []);
|
|
4590
|
+
const tag = `x${chipped.length}_`;
|
|
4591
|
+
const fragments = [
|
|
4592
|
+
...cellFragments(o, p, clumpCells(blown, p.thin, maxFragments), wp, null, speed, blownMass, tag, true),
|
|
4593
|
+
...fallen.length > 0 ? cellFragments(o, p, clumpCells(fallen, p.thin, Math.max(2, Math.round(maxFragments / 4))), wp, null, 0.3, fallenMass, `${tag}f`, true) : []
|
|
4594
|
+
];
|
|
4595
|
+
const cells = [...blown, ...fallen];
|
|
4596
|
+
const holes = localHolesOf(p);
|
|
4597
|
+
p.st.holes = p.st.holes.filter((_, k) => !cells.some((cell) => marksInBox(cell.center, cell.size, void 0, [holes[k]]).length > 0));
|
|
4598
|
+
const lo = [Infinity, Infinity, Infinity], hi = [-Infinity, -Infinity, -Infinity];
|
|
4599
|
+
for (const cell of cells) {
|
|
4600
|
+
chipped.push({ center: [cell.center[0], cell.center[1], cell.center[2]], size: [cell.size[0], cell.size[1], cell.size[2]] });
|
|
4601
|
+
for (let k = 0; k < 3; k++) {
|
|
4602
|
+
lo[k] = Math.min(lo[k], cell.center[k] - cell.size[k] / 2);
|
|
4603
|
+
hi[k] = Math.max(hi[k], cell.center[k] + cell.size[k] / 2);
|
|
4604
|
+
}
|
|
4605
|
+
}
|
|
4606
|
+
p.st.mass = Math.max(0, p.st.mass - blownMass - fallenMass);
|
|
4607
|
+
p.rt.removed = (p.rt.removed ?? 0) + blownMass + fallenMass;
|
|
4608
|
+
markDamaged(p);
|
|
4609
|
+
setStage(o, p, 1, {}, src);
|
|
4610
|
+
const center = toWorld(c, q, [(lo[0] + hi[0]) / 2, (lo[1] + hi[1]) / 2, (lo[2] + hi[2]) / 2]);
|
|
4611
|
+
const units = cells.map((cell) => ({ center: toWorld(c, q, cell.center), size: [cell.size[0], cell.size[1], cell.size[2]] }));
|
|
4612
|
+
emit({ ...base(o, src), type: "chip", part: p.spec.id, center, size: [hi[0] - lo[0], hi[1] - lo[1], hi[2] - lo[2]], rotation: q, fragments, units });
|
|
4613
|
+
debris(o, p, p.mat.breakDebris, center, vec.normalize(vec.sub(center, wp), [0, 1, 0]), 14, 3, src);
|
|
4614
|
+
if (p.spec.reinforcement) exposeRebar(o, p, src);
|
|
4615
|
+
if (p.structural && !isLost(p)) {
|
|
4616
|
+
solveStructure(o);
|
|
4617
|
+
collapseCheck(o, src);
|
|
4618
|
+
}
|
|
4619
|
+
return true;
|
|
4620
|
+
};
|
|
4621
|
+
const dropUnsupported = (o, p, src) => {
|
|
4622
|
+
var _a, _b;
|
|
4623
|
+
if (!p.spec.units || p.spec.units.mortar === p.spec.material || p.st.stripped || isLost(p)) return 0;
|
|
4624
|
+
const [u, v] = planeAxes(p.thin);
|
|
4625
|
+
const grid = gridOf(p);
|
|
4626
|
+
const worn = (_a = p.rt).chips ?? (_a.chips = {});
|
|
4627
|
+
const op = p.st.opening ? toLocal(p.pos, p.q, p.st.opening.center) : null;
|
|
4628
|
+
const opened = (cell) => !!op && !!p.st.opening && Math.hypot(cell.center[u] - op[u], cell.center[v] - op[v]) <= p.st.opening.radius;
|
|
4629
|
+
const loose = unsupportedCells(grid, p.thin, p.dims, (i) => worn[i] !== -1 && !opened(grid[i]));
|
|
4630
|
+
if (loose.length === 0) return 0;
|
|
4631
|
+
for (const i of loose) worn[i] = -1;
|
|
4632
|
+
const fallen = loose.map((i) => grid[i]);
|
|
4633
|
+
const mass = Math.min(p.st.mass, p.mat.density * fallen.reduce((sum, cell) => sum + cell.size[0] * cell.size[1] * cell.size[2], 0));
|
|
4634
|
+
const { c, q } = partXf(o, p);
|
|
4635
|
+
const chipped = (_b = p.st).chipped ?? (_b.chipped = []);
|
|
4636
|
+
const lo = [Infinity, Infinity, Infinity], hi = [-Infinity, -Infinity, -Infinity];
|
|
4637
|
+
for (const cell of fallen) {
|
|
4638
|
+
for (let k = 0; k < 3; k++) {
|
|
4639
|
+
lo[k] = Math.min(lo[k], cell.center[k] - cell.size[k] / 2);
|
|
4640
|
+
hi[k] = Math.max(hi[k], cell.center[k] + cell.size[k] / 2);
|
|
4641
|
+
}
|
|
4642
|
+
}
|
|
4643
|
+
const center = toWorld(c, q, [(lo[0] + hi[0]) / 2, (lo[1] + hi[1]) / 2, (lo[2] + hi[2]) / 2]);
|
|
4644
|
+
const fragments = cellFragments(o, p, clumpCells(fallen, p.thin, Math.max(2, Math.round(maxFragments / 4))), vec.add(center, [0, 1, 0]), null, 0.3, mass, `u${chipped.length}_`, true);
|
|
4645
|
+
const holes = localHolesOf(p);
|
|
4646
|
+
p.st.holes = p.st.holes.filter((_, k) => !fallen.some((cell) => marksInBox(cell.center, cell.size, void 0, [holes[k]]).length > 0));
|
|
4647
|
+
for (const cell of fallen) chipped.push({ center: [cell.center[0], cell.center[1], cell.center[2]], size: [cell.size[0], cell.size[1], cell.size[2]] });
|
|
4648
|
+
p.st.mass = Math.max(0, p.st.mass - mass);
|
|
4649
|
+
p.rt.removed = (p.rt.removed ?? 0) + mass;
|
|
4650
|
+
markDamaged(p);
|
|
4651
|
+
const units = fallen.map((cell) => ({ center: toWorld(c, q, cell.center), size: [cell.size[0], cell.size[1], cell.size[2]] }));
|
|
4652
|
+
emit({ ...base(o, src), type: "chip", part: p.spec.id, center, size: [hi[0] - lo[0], hi[1] - lo[1], hi[2] - lo[2]], rotation: q, fragments, units });
|
|
4653
|
+
debris(o, p, p.mat.breakDebris, center, [0, -1, 0], 6, 1, src);
|
|
4654
|
+
return loose.length;
|
|
4655
|
+
};
|
|
4259
4656
|
const stripPart = (o, p, reinforcement, src) => {
|
|
4260
4657
|
const bars = rebarBars(p.dims, p.thin, reinforcement.spacing);
|
|
4261
4658
|
p.st.stripped = true;
|
|
@@ -4322,6 +4719,7 @@ var createDestruction = (options = {}) => {
|
|
|
4322
4719
|
const unitPlan = p.spec.units ? unitFragmentPlan(p, maxFragments * UNIT_FRAGMENT_FACTOR) : [];
|
|
4323
4720
|
const axis = style === "splinter" || style === "snap" ? severAxis(p) : -1;
|
|
4324
4721
|
const cutAt = axis < 0 ? void 0 : p.rt.cutAt ?? (origin ? hitLocal[axis] : void 0);
|
|
4722
|
+
const planRnd = p.mat.splitForm ? splitStream(o.id, p.spec.id, hitLocal) : rnd;
|
|
4325
4723
|
const plan = unitPlan.length > 0 ? unitPlan : planFragments({
|
|
4326
4724
|
style,
|
|
4327
4725
|
dims: p.dims,
|
|
@@ -4330,8 +4728,9 @@ var createDestruction = (options = {}) => {
|
|
|
4330
4728
|
hit: hitLocal,
|
|
4331
4729
|
fragmentSize: p.mat.fragmentSize ?? [0.04, 0.2],
|
|
4332
4730
|
maxFragments,
|
|
4333
|
-
rnd,
|
|
4731
|
+
rnd: planRnd,
|
|
4334
4732
|
sheet: p.mat.sheet,
|
|
4733
|
+
...p.mat.splitForm ? { form: p.mat.splitForm } : {},
|
|
4335
4734
|
...cutAt !== void 0 ? { cut: { at: cutAt, gap: SEVER_BAND } } : {}
|
|
4336
4735
|
});
|
|
4337
4736
|
const holes = p.st.holes.length > 0 ? localHolesOf(p) : [];
|
|
@@ -4376,7 +4775,7 @@ var createDestruction = (options = {}) => {
|
|
|
4376
4775
|
return;
|
|
4377
4776
|
}
|
|
4378
4777
|
if (ex) {
|
|
4379
|
-
emit({ ...base(o, src), type: "explode", part: p.spec.id, point: c, energy: ex.energy, radius: ex.radius, ...ex.lethalRadius !== void 0 ? { lethalRadius: ex.lethalRadius } : {}, ...ex.effect !== void 0 ? { effect: ex.effect } : {} });
|
|
4778
|
+
emit({ ...base(o, src), type: "explode", part: p.spec.id, point: c, energy: ex.energy, radius: ex.radius, ...ex.lethalRadius !== void 0 ? { lethalRadius: ex.lethalRadius } : {}, ...ex.tnt !== void 0 ? { tnt: ex.tnt } : {}, ...ex.effect !== void 0 ? { effect: ex.effect } : {} });
|
|
4380
4779
|
blasts.push({ point: c, energy: ex.energy, radius: ex.radius, ...ex.lethalRadius !== void 0 ? { lethalRadius: ex.lethalRadius } : {}, source: src });
|
|
4381
4780
|
}
|
|
4382
4781
|
partLost(o, p, src);
|
|
@@ -4470,7 +4869,8 @@ var createDestruction = (options = {}) => {
|
|
|
4470
4869
|
const fragments = [];
|
|
4471
4870
|
for (let i = 0; i < n; i++) {
|
|
4472
4871
|
const back = !exit || i % 3 === 2;
|
|
4473
|
-
const
|
|
4872
|
+
const plies = p.mat.splitForm === "plies";
|
|
4873
|
+
const len = scale * (fibre ? 1.2 + 1.2 * rnd() : 3 + 4 * rnd()), w = scale * (0.8 + 0.8 * rnd()), t = scale * (fibre ? 0.7 + 0.6 * rnd() : plies ? 0.15 + 0.15 * rnd() : 0.4 + 0.4 * rnd());
|
|
4474
4874
|
const size = [w, w, w];
|
|
4475
4875
|
size[along] = len;
|
|
4476
4876
|
size[(along + 1) % 3] = w;
|
|
@@ -4631,7 +5031,7 @@ var createDestruction = (options = {}) => {
|
|
|
4631
5031
|
checkIgnite(o, p, src);
|
|
4632
5032
|
}
|
|
4633
5033
|
}
|
|
4634
|
-
wearJoints(o, p, Ein * 0.5, void 0, 0, src);
|
|
5034
|
+
wearJoints(o, p, Ein * 0.5, void 0, 0, src, false, 2 * BREACH_BLAST_REACH * Math.cbrt(Math.max(0, energy) / 1e3));
|
|
4635
5035
|
const amount = Ein * DMG_PER_J * (mat.damageScale?.explosive ?? 1);
|
|
4636
5036
|
if (blastFrontSpeed > 0 && dist > 0 && mat.tags?.includes("glass")) {
|
|
4637
5037
|
frontQueue.push({ o, p, due: time + dist / blastFrontSpeed, amount, fragEnergy: Ein * 0.3, point, ...src !== void 0 ? { src } : {} });
|
|
@@ -4641,7 +5041,10 @@ var createDestruction = (options = {}) => {
|
|
|
4641
5041
|
const dealt = applyDamage(o, p, amount, "explosive", point, null, Ein * 0.3, 40, src);
|
|
4642
5042
|
res.damage += dealt;
|
|
4643
5043
|
if (p.bend && flexure.enabled) bendByBlast(o, p, point, energy, radius, reach, src);
|
|
4644
|
-
|
|
5044
|
+
const breachReach = BREACH_BLAST_REACH * Math.cbrt(Math.max(0, energy) / 1e3);
|
|
5045
|
+
if (p.spec.units && p.spec.units.mortar !== p.spec.material) blastUnits(o, p, wp, energy, dist, src);
|
|
5046
|
+
else if (p.spec.reinforcement && p.spec.units && dist > MEMBER_CUT_REACH * breachReach) blastUnits(o, p, wp, energy, dist, src);
|
|
5047
|
+
else if (dist <= breachReach) addBreach(o, p, wp, breachAreaOf(p, dealt, true), Ein, null, src);
|
|
4645
5048
|
return reach;
|
|
4646
5049
|
};
|
|
4647
5050
|
const blastProbe = { partsTested: 0, partsNear: 0 };
|
|
@@ -4887,6 +5290,11 @@ var createDestruction = (options = {}) => {
|
|
|
4887
5290
|
}
|
|
4888
5291
|
return h | 0;
|
|
4889
5292
|
};
|
|
5293
|
+
const splitStream = (objectId, partId, hit) => {
|
|
5294
|
+
const codes = Array.from(`${objectId}|${partId}`, (char) => char.charCodeAt(0) / 1e3);
|
|
5295
|
+
const stream = createRng(seedOf(rng.state() / 1e3, ...codes, hit[0], hit[1], hit[2]));
|
|
5296
|
+
return () => stream.next();
|
|
5297
|
+
};
|
|
4890
5298
|
const throwFragment = (frag, objectId, from, g, centre, index, src, shields, results, reports, mult) => {
|
|
4891
5299
|
const v = frag.velocity;
|
|
4892
5300
|
const speed = Math.hypot(v[0], v[1], v[2]);
|
|
@@ -6151,7 +6559,12 @@ var locales = {
|
|
|
6151
6559
|
sand: "sand",
|
|
6152
6560
|
gravel: "gravel",
|
|
6153
6561
|
turf: "turf",
|
|
6154
|
-
mortar: "mortar"
|
|
6562
|
+
mortar: "mortar",
|
|
6563
|
+
mud: "mud",
|
|
6564
|
+
clay: "dry clay",
|
|
6565
|
+
snow: "snow",
|
|
6566
|
+
cobble: "cobblestone",
|
|
6567
|
+
paver: "concrete paver"
|
|
6155
6568
|
},
|
|
6156
6569
|
partStatus: { intact: "intact", damaged: "damaged", collapsed: "collapsed", broken: "broken", melted: "melted", ashes: "ashes" },
|
|
6157
6570
|
jointKinds: {
|
|
@@ -6247,7 +6660,12 @@ var locales = {
|
|
|
6247
6660
|
sand: "areia",
|
|
6248
6661
|
gravel: "brita",
|
|
6249
6662
|
turf: "grama",
|
|
6250
|
-
mortar: "argamassa"
|
|
6663
|
+
mortar: "argamassa",
|
|
6664
|
+
mud: "lama",
|
|
6665
|
+
clay: "barro seco",
|
|
6666
|
+
snow: "neve",
|
|
6667
|
+
cobble: "paralelep\xEDpedo",
|
|
6668
|
+
paver: "bloquete"
|
|
6251
6669
|
},
|
|
6252
6670
|
partStatus: { intact: "intacto", damaged: "danificado", collapsed: "desabado", broken: "quebrado", melted: "derretido", ashes: "cinzas" },
|
|
6253
6671
|
jointKinds: {
|