@adatechnology/game-destruction 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +39 -1
- package/dist/index.global.js +382 -29
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +382 -29
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.global.js
CHANGED
|
@@ -486,7 +486,9 @@ var GameDestruction = (() => {
|
|
|
486
486
|
const band = { c: [root.c[0], root.c[1], root.c[2]], s: [dims[0], dims[1], dims[2]] };
|
|
487
487
|
band.c[L] = (lo + hi) / 2;
|
|
488
488
|
band.s[L] = Math.max(hi - lo, 0.01);
|
|
489
|
-
|
|
489
|
+
if (inp.style === "snap") {
|
|
490
|
+
for (const b of bsp(band, Math.max(3, Math.min(6, inp.maxFragments - out.length)), [0, 1, 2], inp.rnd)) out.push({ center: b.c, size: b.s, q: quat.identity(), shape: "chunk", weight: b.s[0] * b.s[1] * b.s[2] });
|
|
491
|
+
} else for (const p of splinterBox(inp, band, Math.max(3, inp.maxFragments - out.length))) out.push(p);
|
|
490
492
|
return out;
|
|
491
493
|
};
|
|
492
494
|
var tearStack = (inp, root, t, u, v, n, sheet) => {
|
|
@@ -517,6 +519,7 @@ var GameDestruction = (() => {
|
|
|
517
519
|
const t = sorted[0];
|
|
518
520
|
const [u, v] = [sorted[1], sorted[2]].sort((a, b) => a - b);
|
|
519
521
|
if (style === "snap") {
|
|
522
|
+
if (inp.cut) return severAt(inp, root, inp.cut.at, inp.cut.gap);
|
|
520
523
|
const n2 = Math.min(maxFragments, 2 + Math.floor(rnd() * 3));
|
|
521
524
|
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] });
|
|
522
525
|
return out;
|
|
@@ -696,6 +699,27 @@ var GameDestruction = (() => {
|
|
|
696
699
|
for (const x of spread(dims[u], COVER, spacing, MAX_BARS_PER_DIRECTION)) out.push(bar(v, dims[v] / 2 - 0.02, [[u, x], [thin, 0]]));
|
|
697
700
|
return out;
|
|
698
701
|
};
|
|
702
|
+
var clipBarsToBox = (bars, u, v, center, size) => {
|
|
703
|
+
const out = [];
|
|
704
|
+
for (const [a, b] of bars) {
|
|
705
|
+
const axis = a[u] !== b[u] ? u : v;
|
|
706
|
+
const other = axis === u ? v : u;
|
|
707
|
+
if (Math.abs(a[other] - center[other]) > size[other] / 2) continue;
|
|
708
|
+
const lo = Math.max(Math.min(a[axis], b[axis]), center[axis] - size[axis] / 2), hi = Math.min(Math.max(a[axis], b[axis]), center[axis] + size[axis] / 2);
|
|
709
|
+
if (hi - lo < 1e-6) continue;
|
|
710
|
+
const p = [a[0], a[1], a[2]], q = [b[0], b[1], b[2]];
|
|
711
|
+
p[axis] = lo;
|
|
712
|
+
q[axis] = hi;
|
|
713
|
+
out.push([p, q]);
|
|
714
|
+
}
|
|
715
|
+
return out;
|
|
716
|
+
};
|
|
717
|
+
var bendBar = ([a, b], thin, bow, i) => {
|
|
718
|
+
const length = Math.hypot(b[0] - a[0], b[1] - a[1], b[2] - a[2]);
|
|
719
|
+
const m = [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2, (a[2] + b[2]) / 2];
|
|
720
|
+
m[thin] += (i % 2 === 0 ? 1 : -1) * bow * length;
|
|
721
|
+
return [[a, m], [m, b]];
|
|
722
|
+
};
|
|
699
723
|
var clipBarsToDisc = (bars, u, v, center, radius) => {
|
|
700
724
|
const out = [];
|
|
701
725
|
for (const [a, b] of bars) {
|
|
@@ -1673,14 +1697,21 @@ var GameDestruction = (() => {
|
|
|
1673
1697
|
smoke: "grey"
|
|
1674
1698
|
},
|
|
1675
1699
|
visual: {
|
|
1676
|
-
charColor: 1709072
|
|
1700
|
+
charColor: 1709072,
|
|
1701
|
+
innerColor: 14730131
|
|
1677
1702
|
},
|
|
1678
1703
|
tags: [
|
|
1679
1704
|
"wood",
|
|
1680
1705
|
"organic",
|
|
1681
1706
|
"flammable"
|
|
1682
1707
|
],
|
|
1683
|
-
compressive: 1e5
|
|
1708
|
+
compressive: 1e5,
|
|
1709
|
+
hits: {
|
|
1710
|
+
blunt: {
|
|
1711
|
+
tear: "splinters",
|
|
1712
|
+
tearJoules: 60
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1684
1715
|
},
|
|
1685
1716
|
{
|
|
1686
1717
|
id: "wood_hard",
|
|
@@ -1710,14 +1741,21 @@ var GameDestruction = (() => {
|
|
|
1710
1741
|
smoke: "grey"
|
|
1711
1742
|
},
|
|
1712
1743
|
visual: {
|
|
1713
|
-
charColor: 1709072
|
|
1744
|
+
charColor: 1709072,
|
|
1745
|
+
innerColor: 13213802
|
|
1714
1746
|
},
|
|
1715
1747
|
tags: [
|
|
1716
1748
|
"wood",
|
|
1717
1749
|
"organic",
|
|
1718
1750
|
"flammable"
|
|
1719
1751
|
],
|
|
1720
|
-
compressive: 15e4
|
|
1752
|
+
compressive: 15e4,
|
|
1753
|
+
hits: {
|
|
1754
|
+
blunt: {
|
|
1755
|
+
tear: "splinters",
|
|
1756
|
+
tearJoules: 150
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1721
1759
|
},
|
|
1722
1760
|
{
|
|
1723
1761
|
id: "plywood",
|
|
@@ -1747,14 +1785,24 @@ var GameDestruction = (() => {
|
|
|
1747
1785
|
smoke: "grey"
|
|
1748
1786
|
},
|
|
1749
1787
|
visual: {
|
|
1750
|
-
charColor: 1709072
|
|
1788
|
+
charColor: 1709072,
|
|
1789
|
+
innerColor: 14862234
|
|
1751
1790
|
},
|
|
1752
1791
|
tags: [
|
|
1753
1792
|
"wood",
|
|
1754
1793
|
"organic",
|
|
1755
1794
|
"flammable"
|
|
1756
1795
|
],
|
|
1757
|
-
compressive: 6e4
|
|
1796
|
+
compressive: 6e4,
|
|
1797
|
+
hits: {
|
|
1798
|
+
ballistic: {
|
|
1799
|
+
tear: "splinters"
|
|
1800
|
+
},
|
|
1801
|
+
blunt: {
|
|
1802
|
+
tear: "splinters",
|
|
1803
|
+
tearJoules: 80
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1758
1806
|
},
|
|
1759
1807
|
{
|
|
1760
1808
|
id: "mdf",
|
|
@@ -1781,14 +1829,25 @@ var GameDestruction = (() => {
|
|
|
1781
1829
|
smoke: "grey"
|
|
1782
1830
|
},
|
|
1783
1831
|
visual: {
|
|
1784
|
-
charColor: 1709072
|
|
1832
|
+
charColor: 1709072,
|
|
1833
|
+
innerColor: 12096362
|
|
1785
1834
|
},
|
|
1786
1835
|
tags: [
|
|
1787
1836
|
"wood",
|
|
1788
1837
|
"organic",
|
|
1789
1838
|
"flammable"
|
|
1790
1839
|
],
|
|
1791
|
-
compressive: 3e4
|
|
1840
|
+
compressive: 3e4,
|
|
1841
|
+
hits: {
|
|
1842
|
+
ballistic: {
|
|
1843
|
+
tear: "lumps",
|
|
1844
|
+
tearJoules: 120
|
|
1845
|
+
},
|
|
1846
|
+
blunt: {
|
|
1847
|
+
tear: "lumps",
|
|
1848
|
+
tearJoules: 20
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1792
1851
|
},
|
|
1793
1852
|
{
|
|
1794
1853
|
id: "glass",
|
|
@@ -1902,6 +1961,17 @@ var GameDestruction = (() => {
|
|
|
1902
1961
|
damageScale: {
|
|
1903
1962
|
explosive: 1.2
|
|
1904
1963
|
},
|
|
1964
|
+
hits: {
|
|
1965
|
+
ballistic: {
|
|
1966
|
+
unitJoules: 500,
|
|
1967
|
+
floor: 900,
|
|
1968
|
+
crater: true
|
|
1969
|
+
},
|
|
1970
|
+
blunt: {
|
|
1971
|
+
unitJoules: 1500,
|
|
1972
|
+
floor: 60
|
|
1973
|
+
}
|
|
1974
|
+
},
|
|
1905
1975
|
hitDebris: "chips",
|
|
1906
1976
|
breakDebris: "dust",
|
|
1907
1977
|
thermal: {
|
|
@@ -2270,7 +2340,8 @@ var GameDestruction = (() => {
|
|
|
2270
2340
|
smoke: "white"
|
|
2271
2341
|
},
|
|
2272
2342
|
visual: {
|
|
2273
|
-
charColor: 1709072
|
|
2343
|
+
charColor: 1709072,
|
|
2344
|
+
innerColor: 14207126
|
|
2274
2345
|
},
|
|
2275
2346
|
tags: [
|
|
2276
2347
|
"plant",
|
|
@@ -2278,7 +2349,12 @@ var GameDestruction = (() => {
|
|
|
2278
2349
|
"organic",
|
|
2279
2350
|
"flammable"
|
|
2280
2351
|
],
|
|
2281
|
-
compressive: 6e4
|
|
2352
|
+
compressive: 6e4,
|
|
2353
|
+
hits: {
|
|
2354
|
+
blunt: {
|
|
2355
|
+
tear: "none"
|
|
2356
|
+
}
|
|
2357
|
+
}
|
|
2282
2358
|
},
|
|
2283
2359
|
{
|
|
2284
2360
|
id: "cactus",
|
|
@@ -2393,6 +2469,130 @@ var GameDestruction = (() => {
|
|
|
2393
2469
|
"masonry"
|
|
2394
2470
|
],
|
|
2395
2471
|
compressive: 2e4
|
|
2472
|
+
},
|
|
2473
|
+
{
|
|
2474
|
+
id: "asphalt",
|
|
2475
|
+
density: 2350,
|
|
2476
|
+
hpPerKg: 10,
|
|
2477
|
+
penetration: 14,
|
|
2478
|
+
penetrationClass: "concrete",
|
|
2479
|
+
penetrationScale: 0.8,
|
|
2480
|
+
ductility: 0.1,
|
|
2481
|
+
fracture: "crumble",
|
|
2482
|
+
fragmentSize: [
|
|
2483
|
+
0.03,
|
|
2484
|
+
0.15
|
|
2485
|
+
],
|
|
2486
|
+
hitDebris: "chips",
|
|
2487
|
+
breakDebris: "dust",
|
|
2488
|
+
thermal: {
|
|
2489
|
+
conductivity: 0.1,
|
|
2490
|
+
heatCapacity: 2,
|
|
2491
|
+
ignitionC: 400,
|
|
2492
|
+
burnRate: 2e-3,
|
|
2493
|
+
smoke: "black"
|
|
2494
|
+
},
|
|
2495
|
+
tags: [
|
|
2496
|
+
"masonry",
|
|
2497
|
+
"brittle"
|
|
2498
|
+
],
|
|
2499
|
+
compressive: 3e4
|
|
2500
|
+
},
|
|
2501
|
+
{
|
|
2502
|
+
id: "sand",
|
|
2503
|
+
density: 1600,
|
|
2504
|
+
hpPerKg: 2,
|
|
2505
|
+
penetration: 6,
|
|
2506
|
+
ductility: 0.4,
|
|
2507
|
+
fracture: "crumble",
|
|
2508
|
+
fragmentSize: [
|
|
2509
|
+
0.01,
|
|
2510
|
+
0.04
|
|
2511
|
+
],
|
|
2512
|
+
hitDebris: "dust",
|
|
2513
|
+
breakDebris: "dust",
|
|
2514
|
+
thermal: {
|
|
2515
|
+
conductivity: 0.1,
|
|
2516
|
+
heatCapacity: 2
|
|
2517
|
+
},
|
|
2518
|
+
tags: [
|
|
2519
|
+
"soil"
|
|
2520
|
+
],
|
|
2521
|
+
compressive: 1e4
|
|
2522
|
+
},
|
|
2523
|
+
{
|
|
2524
|
+
id: "gravel",
|
|
2525
|
+
density: 1800,
|
|
2526
|
+
hpPerKg: 3,
|
|
2527
|
+
penetration: 8,
|
|
2528
|
+
ductility: 0.3,
|
|
2529
|
+
fracture: "crumble",
|
|
2530
|
+
fragmentSize: [
|
|
2531
|
+
0.02,
|
|
2532
|
+
0.06
|
|
2533
|
+
],
|
|
2534
|
+
hitDebris: "chips",
|
|
2535
|
+
breakDebris: "dust",
|
|
2536
|
+
thermal: {
|
|
2537
|
+
conductivity: 0.1,
|
|
2538
|
+
heatCapacity: 2
|
|
2539
|
+
},
|
|
2540
|
+
tags: [
|
|
2541
|
+
"soil",
|
|
2542
|
+
"masonry"
|
|
2543
|
+
],
|
|
2544
|
+
compressive: 2e4
|
|
2545
|
+
},
|
|
2546
|
+
{
|
|
2547
|
+
id: "turf",
|
|
2548
|
+
density: 900,
|
|
2549
|
+
hpPerKg: 3,
|
|
2550
|
+
penetration: 4,
|
|
2551
|
+
ductility: 0.5,
|
|
2552
|
+
fracture: "crumble",
|
|
2553
|
+
fragmentSize: [
|
|
2554
|
+
0.03,
|
|
2555
|
+
0.1
|
|
2556
|
+
],
|
|
2557
|
+
hitDebris: "dust",
|
|
2558
|
+
breakDebris: "dust",
|
|
2559
|
+
thermal: {
|
|
2560
|
+
conductivity: 0.1,
|
|
2561
|
+
heatCapacity: 1.5,
|
|
2562
|
+
ignitionC: 250,
|
|
2563
|
+
burnRate: 0.03,
|
|
2564
|
+
smoke: "white"
|
|
2565
|
+
},
|
|
2566
|
+
tags: [
|
|
2567
|
+
"soil",
|
|
2568
|
+
"organic"
|
|
2569
|
+
],
|
|
2570
|
+
compressive: 15e3
|
|
2571
|
+
},
|
|
2572
|
+
{
|
|
2573
|
+
id: "mortar",
|
|
2574
|
+
density: 1900,
|
|
2575
|
+
hpPerKg: 10,
|
|
2576
|
+
penetration: 12,
|
|
2577
|
+
penetrationClass: "concrete",
|
|
2578
|
+
penetrationScale: 1.2,
|
|
2579
|
+
ductility: 0.03,
|
|
2580
|
+
fracture: "crumble",
|
|
2581
|
+
fragmentSize: [
|
|
2582
|
+
0.02,
|
|
2583
|
+
0.08
|
|
2584
|
+
],
|
|
2585
|
+
hitDebris: "chips",
|
|
2586
|
+
breakDebris: "dust",
|
|
2587
|
+
thermal: {
|
|
2588
|
+
conductivity: 0.15,
|
|
2589
|
+
heatCapacity: 2.5
|
|
2590
|
+
},
|
|
2591
|
+
tags: [
|
|
2592
|
+
"masonry",
|
|
2593
|
+
"brittle"
|
|
2594
|
+
],
|
|
2595
|
+
compressive: 1e5
|
|
2396
2596
|
}
|
|
2397
2597
|
];
|
|
2398
2598
|
|
|
@@ -2729,6 +2929,32 @@ var GameDestruction = (() => {
|
|
|
2729
2929
|
|
|
2730
2930
|
// src/world.ts
|
|
2731
2931
|
var THIN_SHEET_DISHING = 5e-3;
|
|
2932
|
+
var SWEEP_BACK = 0.01;
|
|
2933
|
+
function sweepOrigins(box, d, spacing) {
|
|
2934
|
+
const origins = [];
|
|
2935
|
+
const line = (from, to) => {
|
|
2936
|
+
const n = Math.max(1, Math.ceil((to - from) / spacing));
|
|
2937
|
+
return Array.from({ length: n + 1 }, (_, i) => from + (to - from) * i / n);
|
|
2938
|
+
};
|
|
2939
|
+
for (const axis of [0, 1, 2]) {
|
|
2940
|
+
if (Math.abs(d[axis]) < 1e-3) continue;
|
|
2941
|
+
const lead = d[axis] > 0 ? box.max[axis] - SWEEP_BACK : box.min[axis] + SWEEP_BACK;
|
|
2942
|
+
const u = (axis + 1) % 3;
|
|
2943
|
+
const v = (axis + 2) % 3;
|
|
2944
|
+
for (const a of line(box.min[u], box.max[u])) {
|
|
2945
|
+
for (const b of line(box.min[v], box.max[v])) {
|
|
2946
|
+
const point = [0, 0, 0];
|
|
2947
|
+
point[axis] = lead;
|
|
2948
|
+
point[u] = a;
|
|
2949
|
+
point[v] = b;
|
|
2950
|
+
origins.push(point);
|
|
2951
|
+
}
|
|
2952
|
+
}
|
|
2953
|
+
}
|
|
2954
|
+
return origins;
|
|
2955
|
+
}
|
|
2956
|
+
var THIN_SHEET_CRUSH = 5e-4;
|
|
2957
|
+
var THIN_SHEET_CONTACT = 0.025;
|
|
2732
2958
|
var DMG_PER_J = 0.02;
|
|
2733
2959
|
var BLAST_COUPLING = 0.1;
|
|
2734
2960
|
var SHELTER_DEFAULTS = { k: 2800, brokenTransmission: 0.5, minPath: 5e-3 };
|
|
@@ -2745,11 +2971,14 @@ var GameDestruction = (() => {
|
|
|
2745
2971
|
var SEVER_BAND = 0.1;
|
|
2746
2972
|
var SEVER_ASPECT = 3;
|
|
2747
2973
|
var SEVER_BALLISTIC = 2;
|
|
2974
|
+
var TEAR_JOULES = 150;
|
|
2975
|
+
var tears = 0;
|
|
2748
2976
|
var SUPPORT_TOLERANCE = 0.05;
|
|
2749
2977
|
var UNIT_FRAGMENT_FACTOR = 4;
|
|
2750
2978
|
var BREACH_GRID_CAP = 4096;
|
|
2751
2979
|
var CHIP_ENERGY = 200;
|
|
2752
2980
|
var CHIP_SPLASH = 0.5;
|
|
2981
|
+
var BAR_BOW = 0.08;
|
|
2753
2982
|
var CUT_SLICE = 1;
|
|
2754
2983
|
var cutFactorOf = (shape) => clamp2(Math.max(...shapeDims(shape)) / CUT_SLICE, 1, 50);
|
|
2755
2984
|
var clamp2 = (v, a, b) => Math.max(a, Math.min(b, v));
|
|
@@ -2759,8 +2988,24 @@ var GameDestruction = (() => {
|
|
|
2759
2988
|
return v === void 0 ? void 0 : v * (mat.penetrationScale ?? 1);
|
|
2760
2989
|
};
|
|
2761
2990
|
var capacityScale = (p) => {
|
|
2762
|
-
|
|
2763
|
-
|
|
2991
|
+
const residual = p.spec.reinforcement?.residualCapacity ?? RESIDUAL_CAPACITY;
|
|
2992
|
+
if (p.st.stripped) return residual;
|
|
2993
|
+
const kept = Math.min(p.rt.removed ? 1 - p.rt.removed / p.mass0 : 1, 1 - sectionLoss(p));
|
|
2994
|
+
return Math.max(p.spec.reinforcement ? residual : 0.05, kept);
|
|
2995
|
+
};
|
|
2996
|
+
var sectionLoss = (p) => {
|
|
2997
|
+
const cells = p.st.chipped;
|
|
2998
|
+
if (!cells?.length) return 0;
|
|
2999
|
+
const [u, v] = planeAxes(p.thin);
|
|
3000
|
+
const along = p.dims[u] >= p.dims[v] ? u : v;
|
|
3001
|
+
const across = along === u ? v : u;
|
|
3002
|
+
let worst = 0;
|
|
3003
|
+
for (const c of cells) {
|
|
3004
|
+
let gone = 0;
|
|
3005
|
+
for (const d of cells) if (Math.abs(d.center[along] - c.center[along]) < d.size[along] / 2 + 1e-6) gone += d.size[across];
|
|
3006
|
+
worst = Math.max(worst, gone / Math.max(p.dims[across], 1e-6));
|
|
3007
|
+
}
|
|
3008
|
+
return Math.min(1, worst);
|
|
2764
3009
|
};
|
|
2765
3010
|
var isLost = (p) => p.st.status === "broken" || p.st.status === "melted" || p.st.status === "ashes";
|
|
2766
3011
|
var clone = (v) => structuredClone(v);
|
|
@@ -3941,6 +4186,12 @@ var GameDestruction = (() => {
|
|
|
3941
4186
|
debris(o, p, layer.mat.hitDebris, wp, normal, 5, 2, src);
|
|
3942
4187
|
setStage(o, p, 1, {}, src);
|
|
3943
4188
|
};
|
|
4189
|
+
const crater = (o, p, wp, normal, radius, src) => {
|
|
4190
|
+
markDamaged(p);
|
|
4191
|
+
emit({ ...base(o, src), type: "spall", part: p.spec.id, layer: -1, point: wp, area: Math.PI * radius * radius });
|
|
4192
|
+
debris(o, p, p.mat.breakDebris, wp, normal, 6, 1.5, src);
|
|
4193
|
+
setStage(o, p, 1, {}, src);
|
|
4194
|
+
};
|
|
3944
4195
|
const insideOpening = (o, p, objectPoint) => {
|
|
3945
4196
|
const op = p.st.opening;
|
|
3946
4197
|
if (!op) return false;
|
|
@@ -4017,9 +4268,13 @@ var GameDestruction = (() => {
|
|
|
4017
4268
|
const lp = toLocal(p.pos, p.q, objectPoint);
|
|
4018
4269
|
return cells.some((c) => Math.abs(lp[u] - c.center[u]) <= c.size[u] / 2 && Math.abs(lp[v] - c.center[v]) <= c.size[v] / 2);
|
|
4019
4270
|
};
|
|
4020
|
-
const chipUnits = (o, p, wp,
|
|
4271
|
+
const chipUnits = (o, p, kind, wp, hitEnergy, dir, radius, src) => {
|
|
4021
4272
|
var _a, _b;
|
|
4022
|
-
if (!p.spec.units ||
|
|
4273
|
+
if (!p.spec.units || !p.staged || chipEnergy <= 0 || p.st.stripped || isLost(p) || (p.st.stage ?? 0) >= 2) return;
|
|
4274
|
+
const rule = p.mat.hits?.[kind];
|
|
4275
|
+
const perKg = rule?.unitJoules ?? chipEnergy;
|
|
4276
|
+
const energy = hitEnergy - (rule?.floor ?? 0);
|
|
4277
|
+
if (perKg <= 0 || energy <= 0) return;
|
|
4023
4278
|
const [u, v] = planeAxes(p.thin);
|
|
4024
4279
|
const lp = toLocal(p.pos, p.q, objLocal(o, wp));
|
|
4025
4280
|
const grid = gridOf(p);
|
|
@@ -4033,7 +4288,7 @@ var GameDestruction = (() => {
|
|
|
4033
4288
|
const share = gap === 0 ? 1 : CHIP_SPLASH * (1 - gap / radius);
|
|
4034
4289
|
const total = (worn[i] ?? 0) + energy * share;
|
|
4035
4290
|
const mass = p.mat.density * cell.size[0] * cell.size[1] * cell.size[2];
|
|
4036
|
-
if (total <
|
|
4291
|
+
if (total < perKg * mass) {
|
|
4037
4292
|
worn[i] = total;
|
|
4038
4293
|
continue;
|
|
4039
4294
|
}
|
|
@@ -4058,7 +4313,32 @@ var GameDestruction = (() => {
|
|
|
4058
4313
|
emit({ ...base(o, src), type: "chip", part: p.spec.id, center, size: [cell.size[0], cell.size[1], cell.size[2]], rotation: q, fragments });
|
|
4059
4314
|
debris(o, p, p.mat.breakDebris, center, vec.scale(dir ?? [0, 1, 0], -1), 6, 2, src);
|
|
4060
4315
|
}
|
|
4316
|
+
if (p.spec.reinforcement) exposeRebar(o, p, src);
|
|
4061
4317
|
for (const cell of out) addBreach(o, p, toWorld(c, q, cell.center), cell.size[u] * cell.size[v], energy, dir, src);
|
|
4318
|
+
if (p.structural && !isLost(p)) {
|
|
4319
|
+
solveStructure(o);
|
|
4320
|
+
collapseCheck(o, src);
|
|
4321
|
+
}
|
|
4322
|
+
};
|
|
4323
|
+
const exposeRebar = (o, p, src) => {
|
|
4324
|
+
const r = p.spec.reinforcement;
|
|
4325
|
+
if (!r) return;
|
|
4326
|
+
const [u, v] = planeAxes(p.thin);
|
|
4327
|
+
const all = rebarBars(p.dims, p.thin, r.spacing);
|
|
4328
|
+
let bars;
|
|
4329
|
+
if (p.st.stripped) bars = all;
|
|
4330
|
+
else {
|
|
4331
|
+
bars = [];
|
|
4332
|
+
for (const cell of p.st.chipped ?? []) bars.push(...clipBarsToBox(all, u, v, cell.center, cell.size));
|
|
4333
|
+
const op = p.st.opening;
|
|
4334
|
+
if (op) {
|
|
4335
|
+
const lc = toLocal(p.pos, p.q, op.center);
|
|
4336
|
+
bars.push(...clipBarsToDisc(all, u, v, [lc[u], lc[v]], op.radius + 0.05));
|
|
4337
|
+
}
|
|
4338
|
+
}
|
|
4339
|
+
const bow = clamp2(((p.st.stress ?? 0) - 1) / 0.5, 0, 1);
|
|
4340
|
+
if (bow > 0) bars = bars.flatMap((bar2, i) => bendBar(bar2, p.thin, bow * BAR_BOW, i));
|
|
4341
|
+
emit({ ...base(o, src), type: "expose", part: p.spec.id, bars: worldBars(o, p, bars) });
|
|
4062
4342
|
};
|
|
4063
4343
|
const openBreach = (o, p, center, total, energy, dir, src) => {
|
|
4064
4344
|
const [u, v] = planeAxes(p.thin);
|
|
@@ -4086,10 +4366,7 @@ var GameDestruction = (() => {
|
|
|
4086
4366
|
setStage(o, p, 2, { opening: { center: worldCenter, radius }, fragments }, src);
|
|
4087
4367
|
debris(o, p, p.mat.breakDebris, worldCenter, vec.scale(dir ?? [0, 1, 0], -1), 14, 3, src);
|
|
4088
4368
|
const reinforcement = p.spec.reinforcement;
|
|
4089
|
-
if (reinforcement)
|
|
4090
|
-
const bars = worldBars(o, p, clipBarsToDisc(rebarBars(p.dims, p.thin, reinforcement.spacing), u, v, [lc[u], lc[v]], radius + 0.05));
|
|
4091
|
-
if (bars.length > 0) emit({ ...base(o, src), type: "expose", part: p.spec.id, bars });
|
|
4092
|
-
}
|
|
4369
|
+
if (reinforcement) exposeRebar(o, p, src);
|
|
4093
4370
|
if (p.structural) solveStructure(o);
|
|
4094
4371
|
};
|
|
4095
4372
|
const stripPart = (o, p, reinforcement, src) => {
|
|
@@ -4101,8 +4378,8 @@ var GameDestruction = (() => {
|
|
|
4101
4378
|
p.st.burning = false;
|
|
4102
4379
|
p.rt.jet = false;
|
|
4103
4380
|
markDamaged(p);
|
|
4104
|
-
emit({ ...base(o, src), type: "expose", part: p.spec.id, bars: worldBars(o, p, bars) });
|
|
4105
4381
|
solveStructure(o);
|
|
4382
|
+
exposeRebar(o, p, src);
|
|
4106
4383
|
collapseCheck(o, src);
|
|
4107
4384
|
};
|
|
4108
4385
|
const unitFragmentPlan = (p, cap) => {
|
|
@@ -4156,7 +4433,7 @@ var GameDestruction = (() => {
|
|
|
4156
4433
|
const grain = p.spec.grain ? AXIS[p.spec.grain] : void 0;
|
|
4157
4434
|
const hitLocal = toLocal(c, q, hitW);
|
|
4158
4435
|
const unitPlan = p.spec.units ? unitFragmentPlan(p, maxFragments * UNIT_FRAGMENT_FACTOR) : [];
|
|
4159
|
-
const axis = style === "splinter" ? severAxis(p) : -1;
|
|
4436
|
+
const axis = style === "splinter" || style === "snap" ? severAxis(p) : -1;
|
|
4160
4437
|
const cutAt = axis < 0 ? void 0 : p.rt.cutAt ?? (origin ? hitLocal[axis] : void 0);
|
|
4161
4438
|
const plan = unitPlan.length > 0 ? unitPlan : planFragments({
|
|
4162
4439
|
style,
|
|
@@ -4228,7 +4505,7 @@ var GameDestruction = (() => {
|
|
|
4228
4505
|
};
|
|
4229
4506
|
const CHAIN_TRIGGER_OF = { ballistic: "shot", explosive: "explosion" };
|
|
4230
4507
|
const severAxis = (p) => {
|
|
4231
|
-
if (p.mat.fracture !== "splinter" || p.spec.shape.kind !== "box") return -1;
|
|
4508
|
+
if (p.mat.fracture !== "splinter" && p.mat.fracture !== "snap" || p.spec.shape.kind !== "box") return -1;
|
|
4232
4509
|
const d = p.dims;
|
|
4233
4510
|
const order = [0, 1, 2].sort((a, b) => d[b] - d[a]);
|
|
4234
4511
|
return d[order[0]] >= SEVER_ASPECT * d[order[1]] && d[order[0]] >= 3 * SEVER_BAND ? order[0] : -1;
|
|
@@ -4289,6 +4566,49 @@ var GameDestruction = (() => {
|
|
|
4289
4566
|
}
|
|
4290
4567
|
}
|
|
4291
4568
|
};
|
|
4569
|
+
const tearOf = (mat, kind) => {
|
|
4570
|
+
const h = mat.hits?.[kind];
|
|
4571
|
+
const tear = h?.tear ?? (kind === "ballistic" && mat.penetrationClass === "wood" ? "splinters" : "none");
|
|
4572
|
+
return { tear, joules: h?.tearJoules ?? TEAR_JOULES };
|
|
4573
|
+
};
|
|
4574
|
+
const tearAt = (o, p, kind, entry, exit, d, E, scale, src) => {
|
|
4575
|
+
const { tear, joules } = tearOf(p.mat, kind);
|
|
4576
|
+
if (tear === "none") return;
|
|
4577
|
+
const shot = kind === "ballistic";
|
|
4578
|
+
const n = shot ? exit ? clamp2(Math.round(E / joules), 1, 7) : 1 : clamp2(Math.floor(E / joules), 0, 5);
|
|
4579
|
+
if (n === 0) return;
|
|
4580
|
+
const fibre = tear === "lumps";
|
|
4581
|
+
const { q } = partXf(o, p);
|
|
4582
|
+
const along = (p.spec.grain ? AXIS[p.spec.grain] : void 0) ?? [0, 1, 2].sort((a, b) => p.dims[b] - p.dims[a])[0];
|
|
4583
|
+
const fragments = [];
|
|
4584
|
+
for (let i = 0; i < n; i++) {
|
|
4585
|
+
const back = !exit || i % 3 === 2;
|
|
4586
|
+
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() : 0.4 + 0.4 * rnd());
|
|
4587
|
+
const size = [w, w, w];
|
|
4588
|
+
size[along] = len;
|
|
4589
|
+
size[(along + 1) % 3] = w;
|
|
4590
|
+
size[(along + 2) % 3] = t;
|
|
4591
|
+
const spread3 = [(rnd() - 0.5) * 0.8, (rnd() - 0.2) * 0.8, (rnd() - 0.5) * 0.8];
|
|
4592
|
+
const speed = shot ? back ? 1.5 + 2 * rnd() : 2 + 4 * rnd() : 0.8 + 1.2 * rnd();
|
|
4593
|
+
const v = vec.scale(vec.normalize(vec.add(back ? vec.scale(d, -1) : d, spread3)), speed);
|
|
4594
|
+
const mass = size[0] * size[1] * size[2] * p.mat.density;
|
|
4595
|
+
fragments.push({
|
|
4596
|
+
id: `${p.spec.id}#t${tears++}-${i}`,
|
|
4597
|
+
part: p.spec.id,
|
|
4598
|
+
material: p.spec.material,
|
|
4599
|
+
shape: fibre ? "chunk" : "splinter",
|
|
4600
|
+
center: back || !exit ? vec.madd(entry, d, -0.01) : vec.madd(exit, d, 0.01),
|
|
4601
|
+
size,
|
|
4602
|
+
rotation: q,
|
|
4603
|
+
velocity: v,
|
|
4604
|
+
inner: true,
|
|
4605
|
+
angularVelocity: [(rnd() - 0.5) * 30, (rnd() - 0.5) * 30, (rnd() - 0.5) * 30],
|
|
4606
|
+
mass
|
|
4607
|
+
});
|
|
4608
|
+
}
|
|
4609
|
+
const patch = [scale * 4, scale * 4, scale * 4];
|
|
4610
|
+
emit({ ...base(o, src), type: "chip", part: p.spec.id, center: exit ?? entry, size: patch, rotation: q, fragments });
|
|
4611
|
+
};
|
|
4292
4612
|
const ballistic = (o, p, entry, n, d, E, caliber, pathLen, src, round = {}) => {
|
|
4293
4613
|
const mat = p.mat;
|
|
4294
4614
|
const piercing = round.piercing ?? 1;
|
|
@@ -4317,6 +4637,7 @@ var GameDestruction = (() => {
|
|
|
4317
4637
|
return { penetrated: false, exitEnergy: 0, damage: dmg2 };
|
|
4318
4638
|
}
|
|
4319
4639
|
if (p.layers.length > 0) spallOuter(o, p, entry, face, n, Math.max(0.03, caliber / 1e3 * 4), src);
|
|
4640
|
+
else if (mat.hits?.ballistic?.crater) crater(o, p, entry, n, clamp2(0.02 + Math.sqrt(E) * 15e-4, 0.02, 0.15), src);
|
|
4320
4641
|
const brittle = mat.ductility < 0.3;
|
|
4321
4642
|
const ratio = Ep / Math.max(thr, 1e-6);
|
|
4322
4643
|
const crackLen = clamp2(0.03 + ratio * 0.2, 0.03, 0.8);
|
|
@@ -4350,8 +4671,10 @@ var GameDestruction = (() => {
|
|
|
4350
4671
|
} else addHole(o, p, entry, n, caliber / 1e3, false, void 0, src);
|
|
4351
4672
|
debris(o, p, mat.hitDebris, entry, n, clamp2(Math.round(E / 60) + 2, 2, 14), clamp2(Math.sqrt(E) / 6, 1, 8), src);
|
|
4352
4673
|
wearJoints(o, p, (penetrated ? thr : E) * 0.02, void 0, 0, src, true);
|
|
4353
|
-
const
|
|
4354
|
-
|
|
4674
|
+
const absorbed = penetrated && mat.ductility >= 0.5 && mat.fracture !== "tear" ? Math.min(E, thr) : E;
|
|
4675
|
+
const dmg = applyDamage(o, p, absorbed * DMG_PER_J * scale, "ballistic", entry, d, absorbed * 0.1, 12, src);
|
|
4676
|
+
if (!isLost(p)) tearAt(o, p, "ballistic", entry, penetrated ? exitPoint : void 0, d, E, caliber / 1e3, src);
|
|
4677
|
+
chipUnits(o, p, "ballistic", entry, penetrated ? Math.min(E, Math.max(thr, E * 0.5)) : E, d, Math.max(0.05, caliber / 1e3 * 8), src);
|
|
4355
4678
|
return { penetrated, exitEnergy: penetrated ? penMm !== void 0 ? E * (1 - thr / Ep) : E - thr / (hard ? piercing : 1) : 0, exitPoint, damage: dmg };
|
|
4356
4679
|
};
|
|
4357
4680
|
const melee = (o, p, kind, E, point, n, d, contactR, src) => {
|
|
@@ -4359,14 +4682,20 @@ var GameDestruction = (() => {
|
|
|
4359
4682
|
const thrMm = mat.penetration * p.thickness * 1e3;
|
|
4360
4683
|
const fragile = kind === "blunt" ? fragility(mat.ductility, p.thickness) : 1;
|
|
4361
4684
|
const Ec = E * fragile;
|
|
4685
|
+
const sheet = mat.ductility >= 0.5 && p.thickness <= THIN_SHEET_CRUSH;
|
|
4686
|
+
const fold = sheet ? Math.min(4, Math.sqrt(1e-3 / p.thickness)) : 1;
|
|
4687
|
+
const patch = sheet ? Math.min(contactR, THIN_SHEET_CONTACT) : contactR;
|
|
4362
4688
|
if (E > 0) {
|
|
4363
4689
|
if (kind === "blunt") {
|
|
4364
|
-
if (mat.ductility >= 0.5) addDent(o, p, point, n, Math.max(
|
|
4690
|
+
if (mat.ductility >= 0.5) addDent(o, p, point, n, Math.max(patch, 0.015), clamp2(E * 1e-4 * fold, 1e-3, sheet ? Math.min(0.06, Math.min(...p.dims) * 0.45) : 0.06), src);
|
|
4365
4691
|
else if (mat.ductility < 0.3) {
|
|
4366
4692
|
if (Ec >= 5) addCrack(o, p, point, n, clamp2(0.04 + Math.min(Ec, E * 6) * 8e-4, 0.04, 0.6), src);
|
|
4367
4693
|
} else if (E >= thrMm * 0.2) addDent(o, p, point, n, Math.max(contactR, 0.015), clamp2(E * 6e-5, 1e-3, 0.04), src);
|
|
4368
4694
|
} else if (mat.fracture === "tear") {
|
|
4369
4695
|
addHole(o, p, point, n, clamp2(0.02 + E * 5e-4, 0.02, 0.3), true, void 0, src);
|
|
4696
|
+
} else if (mat.ductility >= 0.3 && E >= thrMm) {
|
|
4697
|
+
addHole(o, p, point, n, clamp2(contactR, 0.01, 0.08), true, void 0, src);
|
|
4698
|
+
if (sheet) addDent(o, p, point, n, Math.max(patch * 0.6, 0.01), clamp2(E * 4e-5 * fold, 5e-4, 0.02), src);
|
|
4370
4699
|
} else if (mat.ductility >= 0.5) {
|
|
4371
4700
|
addDent(o, p, point, n, Math.max(contactR, 0.01), clamp2(E * 4e-5, 5e-4, 0.02), src);
|
|
4372
4701
|
}
|
|
@@ -4379,7 +4708,8 @@ var GameDestruction = (() => {
|
|
|
4379
4708
|
const scale = mat.damageScale?.[kind] ?? 1;
|
|
4380
4709
|
const done = applyDamage(o, p, Ec * DMG_PER_J * scale * (kind === "slash" ? p.cutFactor : 1), kind, point, d, E * 0.1, 12, src);
|
|
4381
4710
|
addBreach(o, p, point, breachAreaOf(p, done, false), E, d, src);
|
|
4382
|
-
if (kind === "blunt") chipUnits(o, p, point, E, d, Math.max(contactR, 0.03), src);
|
|
4711
|
+
if (kind === "blunt") chipUnits(o, p, "blunt", point, E, d, Math.max(contactR, 0.03), src);
|
|
4712
|
+
if (E > 0 && !isLost(p)) tearAt(o, p, kind, point, void 0, d, E, clamp2(contactR * 0.2, 6e-3, 0.02), src);
|
|
4383
4713
|
return done;
|
|
4384
4714
|
};
|
|
4385
4715
|
const blastPart = (o, p, point, energy, radius, src, res, reachAt) => {
|
|
@@ -4958,6 +5288,7 @@ var GameDestruction = (() => {
|
|
|
4958
5288
|
if (p.rt.stressT <= 0) {
|
|
4959
5289
|
p.rt.stressT = 0.5;
|
|
4960
5290
|
emit({ ...base(o), type: "stress", part: p.spec.id, stress, load: p.st.load ?? 0 });
|
|
5291
|
+
if (p.spec.reinforcement && (p.st.stripped || p.st.chipped?.length || p.st.opening)) exposeRebar(o, p);
|
|
4961
5292
|
}
|
|
4962
5293
|
applyDamage(o, p, overloadRate(stress) * p.st.maxHp * dt, "blunt", null, null, 5, 3);
|
|
4963
5294
|
} else if (stress > 0.8 && p.mat.ductility < 0.3 && p.rt.stressT <= 0 && p.st.cracks.length < 6) {
|
|
@@ -5174,6 +5505,18 @@ var GameDestruction = (() => {
|
|
|
5174
5505
|
const r = raycastInternal(origin, direction, maxDistance);
|
|
5175
5506
|
return r ? { object: r.object, part: r.part, point: r.point, normal: r.normal, distance: r.distance } : null;
|
|
5176
5507
|
},
|
|
5508
|
+
sweepBox(box, direction, distance, options2 = {}) {
|
|
5509
|
+
const d = vec.normalize(direction);
|
|
5510
|
+
const ignored = options2.ignore === void 0 ? void 0 : objects.get(options2.ignore);
|
|
5511
|
+
const ignore = ignored ? new Set(ignored.parts.map((p) => `${ignored.id}\0${p.spec.id}`)) : void 0;
|
|
5512
|
+
let best = null;
|
|
5513
|
+
for (const origin of sweepOrigins(box, d, options2.spacing ?? 0.2)) {
|
|
5514
|
+
const r = raycastInternal(origin, d, distance + SWEEP_BACK, ignore);
|
|
5515
|
+
if (!r || best && r.distance >= best.distance) continue;
|
|
5516
|
+
best = { object: r.object, part: r.part, point: r.point, normal: r.normal, distance: Math.max(0, r.distance - SWEEP_BACK) };
|
|
5517
|
+
}
|
|
5518
|
+
return best;
|
|
5519
|
+
},
|
|
5177
5520
|
shoot(origin, direction, energy, caliber, source, round) {
|
|
5178
5521
|
const extras = typeof round === "number" ? { piercing: round } : round ?? {};
|
|
5179
5522
|
return withCollapses(run(() => {
|
|
@@ -5818,7 +6161,12 @@ var GameDestruction = (() => {
|
|
|
5818
6161
|
cactus: "cactus",
|
|
5819
6162
|
flesh_fruit: "fruit",
|
|
5820
6163
|
wax: "wax",
|
|
5821
|
-
soil: "soil"
|
|
6164
|
+
soil: "soil",
|
|
6165
|
+
asphalt: "asphalt",
|
|
6166
|
+
sand: "sand",
|
|
6167
|
+
gravel: "gravel",
|
|
6168
|
+
turf: "turf",
|
|
6169
|
+
mortar: "mortar"
|
|
5822
6170
|
},
|
|
5823
6171
|
partStatus: { intact: "intact", damaged: "damaged", collapsed: "collapsed", broken: "broken", melted: "melted", ashes: "ashes" },
|
|
5824
6172
|
jointKinds: {
|
|
@@ -5909,7 +6257,12 @@ var GameDestruction = (() => {
|
|
|
5909
6257
|
cactus: "cacto",
|
|
5910
6258
|
flesh_fruit: "fruta",
|
|
5911
6259
|
wax: "cera",
|
|
5912
|
-
soil: "terra"
|
|
6260
|
+
soil: "terra",
|
|
6261
|
+
asphalt: "asfalto",
|
|
6262
|
+
sand: "areia",
|
|
6263
|
+
gravel: "brita",
|
|
6264
|
+
turf: "grama",
|
|
6265
|
+
mortar: "argamassa"
|
|
5913
6266
|
},
|
|
5914
6267
|
partStatus: { intact: "intacto", damaged: "danificado", collapsed: "desabado", broken: "quebrado", melted: "derretido", ashes: "cinzas" },
|
|
5915
6268
|
jointKinds: {
|