@adatechnology/game-destruction 0.8.2 → 0.10.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 +19 -0
- package/dist/index.global.js +214 -46
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +214 -46
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -340,6 +340,62 @@ 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 splinterBox = (inp, box, limit = inp.maxFragments) => {
|
|
344
|
+
const { rnd } = inp;
|
|
345
|
+
const dims = box.s;
|
|
346
|
+
const [fmin, fmax] = inp.fragmentSize;
|
|
347
|
+
const g = inp.grain ?? [0, 1, 2].sort((a2, b2) => dims[b2] - dims[a2])[0];
|
|
348
|
+
const [a, b] = [0, 1, 2].filter((i) => i !== g);
|
|
349
|
+
const w = Math.max(fmin * 1.5, 0.01);
|
|
350
|
+
const c = clamp(Math.round(dims[a] * dims[b] / (w * w)), 3, Math.max(3, Math.floor(limit / 2)));
|
|
351
|
+
const maxLen = fmax * 4;
|
|
352
|
+
const cells = bsp(box, c, [a, b], rnd);
|
|
353
|
+
const pieces = [];
|
|
354
|
+
for (const cell of cells) {
|
|
355
|
+
const extra = rnd() < 0.4 ? 1 : 0;
|
|
356
|
+
const k = Math.max(1, Math.ceil(dims[g] / maxLen)) + extra;
|
|
357
|
+
for (const p of bsp(cell, k, [g], rnd)) pieces.push(p);
|
|
358
|
+
}
|
|
359
|
+
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
|
+
};
|
|
361
|
+
var severAt = (inp, root, at, gap) => {
|
|
362
|
+
const dims = root.s;
|
|
363
|
+
const L = [0, 1, 2].sort((x, y) => dims[y] - dims[x])[0];
|
|
364
|
+
const half = dims[L] / 2;
|
|
365
|
+
const lo = clamp(at - gap / 2, -half, half), hi = clamp(at + gap / 2, -half, half);
|
|
366
|
+
const out = [];
|
|
367
|
+
const stub = (from, to) => {
|
|
368
|
+
if (to - from < 0.02) return;
|
|
369
|
+
const c = [root.c[0], root.c[1], root.c[2]], sz = [dims[0], dims[1], dims[2]];
|
|
370
|
+
c[L] = (from + to) / 2;
|
|
371
|
+
sz[L] = to - from;
|
|
372
|
+
out.push({ center: c, size: sz, q: quat.identity(), shape: "slab", weight: sz[0] * sz[1] * sz[2] });
|
|
373
|
+
};
|
|
374
|
+
stub(-half, lo);
|
|
375
|
+
stub(hi, half);
|
|
376
|
+
const band = { c: [root.c[0], root.c[1], root.c[2]], s: [dims[0], dims[1], dims[2]] };
|
|
377
|
+
band.c[L] = (lo + hi) / 2;
|
|
378
|
+
band.s[L] = Math.max(hi - lo, 0.01);
|
|
379
|
+
for (const p of splinterBox(inp, band, Math.max(3, inp.maxFragments - out.length))) out.push(p);
|
|
380
|
+
return out;
|
|
381
|
+
};
|
|
382
|
+
var tearStack = (inp, root, t, u, v, n, sheet) => {
|
|
383
|
+
const { dims, rnd, maxFragments } = inp;
|
|
384
|
+
const layers = clamp(Math.ceil(dims[t] / STACK_LAYER), 2, 4);
|
|
385
|
+
const per = clamp(n, 3, Math.max(3, Math.floor(maxFragments / layers)));
|
|
386
|
+
const out = [];
|
|
387
|
+
for (let k = 0; k < layers; k++) {
|
|
388
|
+
const off = -dims[t] / 2 + (k + 0.5) * dims[t] / layers;
|
|
389
|
+
for (const b of bsp(root, per, [u, v], rnd)) {
|
|
390
|
+
if (out.length >= maxFragments) return out;
|
|
391
|
+
const c = [b.c[0], b.c[1], b.c[2]];
|
|
392
|
+
c[t] = off;
|
|
393
|
+
out.push({ center: c, size: [b.s[u], b.s[v], sheet], q: frame(u, v, t, (rnd() - 0.5) * 0.8), shape: "scrap", weight: b.s[u] * b.s[v], inner: out.length % 4 !== 0 });
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
return out;
|
|
397
|
+
};
|
|
398
|
+
var STACK_LAYER = 0.012;
|
|
343
399
|
var planFragments = (inp) => {
|
|
344
400
|
const { style, dims, rnd, maxFragments } = inp;
|
|
345
401
|
const [fmin, fmax] = inp.fragmentSize;
|
|
@@ -361,27 +417,14 @@ var planFragments = (inp) => {
|
|
|
361
417
|
return out;
|
|
362
418
|
}
|
|
363
419
|
if (style === "splinter") {
|
|
364
|
-
|
|
365
|
-
const
|
|
366
|
-
const w = Math.max(fmin * 1.5, 0.01);
|
|
367
|
-
const c = clamp(Math.round(dims[a] * dims[b] / (w * w)), 3, Math.max(3, Math.floor(maxFragments / 2)));
|
|
368
|
-
const maxLen = fmax * 4;
|
|
369
|
-
const cells = bsp(root, c, [a, b], rnd);
|
|
370
|
-
const pieces = [];
|
|
371
|
-
for (const cell of cells) {
|
|
372
|
-
let list = [cell];
|
|
373
|
-
const extra = rnd() < 0.4 ? 1 : 0;
|
|
374
|
-
const k = Math.max(1, Math.ceil(dims[g] / maxLen)) + extra;
|
|
375
|
-
list = bsp(cell, k, [g], rnd);
|
|
376
|
-
for (const p of list) pieces.push(p);
|
|
377
|
-
}
|
|
378
|
-
for (const p of pieces.slice(0, maxFragments)) {
|
|
379
|
-
out.push({ 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] });
|
|
380
|
-
}
|
|
420
|
+
if (inp.cut) return severAt(inp, root, inp.cut.at, inp.cut.gap);
|
|
421
|
+
for (const p of splinterBox(inp, root)) out.push(p);
|
|
381
422
|
return out;
|
|
382
423
|
}
|
|
383
424
|
if (style === "tear") {
|
|
384
425
|
const n2 = clamp(Math.round(dims[u] * dims[v] / (mean * mean)), 3, maxFragments);
|
|
426
|
+
const sheet = inp.sheet ?? 0;
|
|
427
|
+
if (sheet > 0 && dims[t] > sheet * 4) return tearStack(inp, root, t, u, v, n2, sheet);
|
|
385
428
|
const th2 = Math.min(inp.thickness, dims[t]);
|
|
386
429
|
for (const b of bsp(root, n2, [u, v], rnd)) {
|
|
387
430
|
out.push({ center: [b.c[0], b.c[1], b.c[2]], size: [b.s[u], b.s[v], th2], q: frame(u, v, t), shape: "scrap", weight: b.s[u] * b.s[v] });
|
|
@@ -2027,6 +2070,7 @@ var materials_default = [
|
|
|
2027
2070
|
0.03,
|
|
2028
2071
|
0.15
|
|
2029
2072
|
],
|
|
2073
|
+
sheet: 1e-3,
|
|
2030
2074
|
damageScale: {
|
|
2031
2075
|
slash: 3
|
|
2032
2076
|
},
|
|
@@ -2042,7 +2086,8 @@ var materials_default = [
|
|
|
2042
2086
|
smoke: "grey"
|
|
2043
2087
|
},
|
|
2044
2088
|
visual: {
|
|
2045
|
-
charColor: 1709072
|
|
2089
|
+
charColor: 1709072,
|
|
2090
|
+
innerColor: 15524048
|
|
2046
2091
|
},
|
|
2047
2092
|
tags: [
|
|
2048
2093
|
"fabric",
|
|
@@ -2436,9 +2481,14 @@ var BREACH_BLAST_FACTOR = 6;
|
|
|
2436
2481
|
var BREACH_AREA = 0.25;
|
|
2437
2482
|
var BREACH_BLAST_REACH = 0.12;
|
|
2438
2483
|
var RESIDUAL_CAPACITY = 0.15;
|
|
2484
|
+
var SEVER_BAND = 0.1;
|
|
2485
|
+
var SEVER_ASPECT = 3;
|
|
2486
|
+
var SEVER_BALLISTIC = 2;
|
|
2439
2487
|
var SUPPORT_TOLERANCE = 0.05;
|
|
2440
2488
|
var UNIT_FRAGMENT_FACTOR = 4;
|
|
2441
2489
|
var BREACH_GRID_CAP = 4096;
|
|
2490
|
+
var CHIP_ENERGY = 200;
|
|
2491
|
+
var CHIP_SPLASH = 0.5;
|
|
2442
2492
|
var CUT_SLICE = 1;
|
|
2443
2493
|
var cutFactorOf = (shape) => clamp2(Math.max(...shapeDims(shape)) / CUT_SLICE, 1, 50);
|
|
2444
2494
|
var clamp2 = (v, a, b) => Math.max(a, Math.min(b, v));
|
|
@@ -2482,6 +2532,7 @@ var createDestruction = (options = {}) => {
|
|
|
2482
2532
|
let windCfg = windOpts ? resolveWind(windOpts) : null;
|
|
2483
2533
|
const ignitionRules = resolveIgnitionRules(options.ignition);
|
|
2484
2534
|
const breachBlastFactor = options.breach?.blastFactor ?? BREACH_BLAST_FACTOR;
|
|
2535
|
+
const chipEnergy = options.breach?.chipEnergy ?? CHIP_ENERGY;
|
|
2485
2536
|
const rng = createRng(options.seed ?? 1);
|
|
2486
2537
|
const rnd = () => rng.next();
|
|
2487
2538
|
let time = 0;
|
|
@@ -2855,41 +2906,68 @@ var createDestruction = (options = {}) => {
|
|
|
2855
2906
|
markDamaged(p);
|
|
2856
2907
|
if (p.rt.hinge >= flexure.failRotationDeg * Math.PI / 180) hingeDrop(o, p, src);
|
|
2857
2908
|
};
|
|
2909
|
+
const freeSpan = (p, supports) => {
|
|
2910
|
+
if (supports.length === 0) return Infinity;
|
|
2911
|
+
const d = p.dims;
|
|
2912
|
+
const L = [0, 1, 2].sort((a, b) => d[b] - d[a])[0];
|
|
2913
|
+
const spans = supports.map((k) => contactBox(p, k)).map(([lo, hi]) => [lo[L], hi[L]]);
|
|
2914
|
+
let far = 0;
|
|
2915
|
+
for (let i = 0; i <= 16; i++) {
|
|
2916
|
+
const x = (i / 16 - 0.5) * d[L];
|
|
2917
|
+
let near = Infinity;
|
|
2918
|
+
for (const [lo, hi] of spans) near = Math.min(near, Math.max(lo - x, x - hi, 0));
|
|
2919
|
+
far = Math.max(far, near);
|
|
2920
|
+
}
|
|
2921
|
+
return 2 * far;
|
|
2922
|
+
};
|
|
2858
2923
|
const solveStructure = (o) => {
|
|
2859
2924
|
o.maxStress = 0;
|
|
2860
2925
|
if (!o.hasStructure) return;
|
|
2861
2926
|
const parts = o.parts;
|
|
2862
2927
|
const n = parts.length;
|
|
2863
2928
|
const dist = new Array(n).fill(-1);
|
|
2864
|
-
const queue = [];
|
|
2865
2929
|
const ok = (i) => parts[i].structural && !isLost(parts[i]);
|
|
2866
2930
|
const isPartition = (i) => parts[i].spec.structure?.role === "partition";
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
if (
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
const r = j.ia === s ? j.ib : j.ia;
|
|
2877
|
-
if (dist[r] >= 0 || !ok(r) || restsOn(o, j, r) !== s) continue;
|
|
2878
|
-
dist[r] = dist[s] + 1;
|
|
2879
|
-
queue.push(r);
|
|
2931
|
+
const out = /* @__PURE__ */ new Set();
|
|
2932
|
+
let queue = [];
|
|
2933
|
+
let lowerOf = /* @__PURE__ */ new Map();
|
|
2934
|
+
for (; ; ) {
|
|
2935
|
+
dist.fill(-1);
|
|
2936
|
+
queue = [];
|
|
2937
|
+
for (let i = 0; i < n; i++) if (ok(i) && !out.has(i) && parts[i].anchored) {
|
|
2938
|
+
dist[i] = 0;
|
|
2939
|
+
queue.push(i);
|
|
2880
2940
|
}
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2941
|
+
for (let h = 0; h < queue.length; h++) {
|
|
2942
|
+
const s = queue[h];
|
|
2943
|
+
if (isPartition(s)) continue;
|
|
2944
|
+
for (const j of o.byPart.get(parts[s].spec.id)) {
|
|
2945
|
+
if (j.st.open || !j.bears) continue;
|
|
2946
|
+
const r = j.ia === s ? j.ib : j.ia;
|
|
2947
|
+
if (dist[r] >= 0 || !ok(r) || out.has(r) || restsOn(o, j, r) !== s) continue;
|
|
2948
|
+
dist[r] = dist[s] + 1;
|
|
2949
|
+
queue.push(r);
|
|
2950
|
+
}
|
|
2951
|
+
}
|
|
2952
|
+
lowerOf = /* @__PURE__ */ new Map();
|
|
2953
|
+
let dropped = false;
|
|
2954
|
+
for (const i of queue) {
|
|
2955
|
+
const lower = [];
|
|
2956
|
+
for (const j of o.byPart.get(parts[i].spec.id)) {
|
|
2957
|
+
if (j.st.open || !j.bears) continue;
|
|
2958
|
+
const k = restsOn(o, j, i);
|
|
2959
|
+
if (k >= 0 && dist[k] >= 0 && dist[k] < dist[i] && !isPartition(k)) lower.push(k);
|
|
2960
|
+
}
|
|
2961
|
+
lowerOf.set(i, lower);
|
|
2962
|
+
const max = parts[i].spec.structure?.maxSpan;
|
|
2963
|
+
if (max !== void 0 && !parts[i].anchored && freeSpan(parts[i], lower.map((k) => parts[k])) > max) {
|
|
2964
|
+
out.add(i);
|
|
2965
|
+
dropped = true;
|
|
2966
|
+
}
|
|
2890
2967
|
}
|
|
2891
|
-
|
|
2968
|
+
if (!dropped) break;
|
|
2892
2969
|
}
|
|
2970
|
+
const incoming = new Array(n).fill(0);
|
|
2893
2971
|
const bending = new Array(n).fill(0);
|
|
2894
2972
|
const through = parts.map(() => []);
|
|
2895
2973
|
const pass = (k, at, mass) => {
|
|
@@ -3401,6 +3479,7 @@ var createDestruction = (options = {}) => {
|
|
|
3401
3479
|
const r = rayPart(o, p, origin, d);
|
|
3402
3480
|
if (!r || r.t0 < 0 || r.t0 > maxDist) continue;
|
|
3403
3481
|
if (p.st.opening && insideOpening(o, p, vec.madd(origin, d, r.t0))) continue;
|
|
3482
|
+
if (p.st.chipped && insideChip(p, objLocal(o, vec.madd(origin, d, r.t0)))) continue;
|
|
3404
3483
|
if (!best || r.t0 < best.distance) best = { object: o.id, part: p.spec.id, point: vec.madd(origin, d, r.t0), normal: r.n, distance: r.t0, pathLen: r.t1 - r.t0 };
|
|
3405
3484
|
}
|
|
3406
3485
|
}
|
|
@@ -3664,6 +3743,62 @@ var createDestruction = (options = {}) => {
|
|
|
3664
3743
|
if (total < (p.spec.breach?.area ?? BREACH_AREA)) return;
|
|
3665
3744
|
openBreach(o, p, [sum[0] / total, sum[1] / total, sum[2] / total], total, energy, dir, src);
|
|
3666
3745
|
};
|
|
3746
|
+
const unitGrids = /* @__PURE__ */ new WeakMap();
|
|
3747
|
+
const gridOf = (p) => {
|
|
3748
|
+
let grid = unitGrids.get(p);
|
|
3749
|
+
if (!grid) unitGrids.set(p, grid = unitGrid(p.dims, p.thin, unitsOf(p), BREACH_GRID_CAP));
|
|
3750
|
+
return grid;
|
|
3751
|
+
};
|
|
3752
|
+
const insideChip = (p, objectPoint) => {
|
|
3753
|
+
const cells = p.st.chipped;
|
|
3754
|
+
if (!cells?.length) return false;
|
|
3755
|
+
const [u, v] = planeAxes(p.thin);
|
|
3756
|
+
const lp = toLocal(p.pos, p.q, objectPoint);
|
|
3757
|
+
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);
|
|
3758
|
+
};
|
|
3759
|
+
const chipUnits = (o, p, wp, energy, dir, radius, src) => {
|
|
3760
|
+
var _a, _b;
|
|
3761
|
+
if (!p.spec.units || p.spec.reinforcement || !p.staged || chipEnergy <= 0 || energy <= 0 || p.st.stripped || isLost(p) || (p.st.stage ?? 0) >= 2) return;
|
|
3762
|
+
const [u, v] = planeAxes(p.thin);
|
|
3763
|
+
const lp = toLocal(p.pos, p.q, objLocal(o, wp));
|
|
3764
|
+
const grid = gridOf(p);
|
|
3765
|
+
const worn = (_a = p.rt).chips ?? (_a.chips = {});
|
|
3766
|
+
const out = [];
|
|
3767
|
+
for (let i = 0; i < grid.length; i++) {
|
|
3768
|
+
const cell = grid[i];
|
|
3769
|
+
if (worn[i] === -1) continue;
|
|
3770
|
+
const gap = Math.hypot(Math.max(0, Math.abs(lp[u] - cell.center[u]) - cell.size[u] / 2), Math.max(0, Math.abs(lp[v] - cell.center[v]) - cell.size[v] / 2));
|
|
3771
|
+
if (gap > radius) continue;
|
|
3772
|
+
const share = gap === 0 ? 1 : CHIP_SPLASH * (1 - gap / radius);
|
|
3773
|
+
const total = (worn[i] ?? 0) + energy * share;
|
|
3774
|
+
const mass = p.mat.density * cell.size[0] * cell.size[1] * cell.size[2];
|
|
3775
|
+
if (total < chipEnergy * mass) {
|
|
3776
|
+
worn[i] = total;
|
|
3777
|
+
continue;
|
|
3778
|
+
}
|
|
3779
|
+
worn[i] = -1;
|
|
3780
|
+
out.push(cell);
|
|
3781
|
+
}
|
|
3782
|
+
if (out.length === 0) return;
|
|
3783
|
+
const { c, q } = partXf(o, p);
|
|
3784
|
+
const chipped = (_b = p.st).chipped ?? (_b.chipped = []);
|
|
3785
|
+
const holes = localHolesOf(p);
|
|
3786
|
+
for (const cell of out) {
|
|
3787
|
+
const mass = Math.min(p.st.mass, p.mat.density * cell.size[0] * cell.size[1] * cell.size[2]);
|
|
3788
|
+
const center = toWorld(c, q, cell.center);
|
|
3789
|
+
const speed = clamp2(Math.sqrt(2 * energy * 0.05 / Math.max(mass, 0.05)), 0.5, 6);
|
|
3790
|
+
const fragments = cellFragments(o, p, [cell], wp, dir, speed, mass, `c${chipped.length}_`);
|
|
3791
|
+
p.st.holes = p.st.holes.filter((_, k) => marksInBox(cell.center, cell.size, void 0, [holes[k]]).length === 0);
|
|
3792
|
+
chipped.push({ center: [cell.center[0], cell.center[1], cell.center[2]], size: [cell.size[0], cell.size[1], cell.size[2]] });
|
|
3793
|
+
p.st.mass = Math.max(0, p.st.mass - mass);
|
|
3794
|
+
p.rt.removed = (p.rt.removed ?? 0) + mass;
|
|
3795
|
+
markDamaged(p);
|
|
3796
|
+
setStage(o, p, 1, {}, src);
|
|
3797
|
+
emit({ ...base(o, src), type: "chip", part: p.spec.id, center, size: [cell.size[0], cell.size[1], cell.size[2]], rotation: q, fragments });
|
|
3798
|
+
debris(o, p, p.mat.breakDebris, center, vec.scale(dir ?? [0, 1, 0], -1), 6, 2, src);
|
|
3799
|
+
}
|
|
3800
|
+
for (const cell of out) addBreach(o, p, toWorld(c, q, cell.center), cell.size[u] * cell.size[v], energy, dir, src);
|
|
3801
|
+
};
|
|
3667
3802
|
const openBreach = (o, p, center, total, energy, dir, src) => {
|
|
3668
3803
|
const [u, v] = planeAxes(p.thin);
|
|
3669
3804
|
const radius = clamp2(Math.sqrt(total / Math.PI), Math.sqrt((p.spec.breach?.area ?? BREACH_AREA) / Math.PI), breachRadius);
|
|
@@ -3675,7 +3810,8 @@ var createDestruction = (options = {}) => {
|
|
|
3675
3810
|
failPart(o, p, worldCenter, dir, energy, 12, src);
|
|
3676
3811
|
return;
|
|
3677
3812
|
}
|
|
3678
|
-
const
|
|
3813
|
+
const worn = p.rt.chips;
|
|
3814
|
+
const cells = cellsWithin(gridOf(p).filter((_, i) => worn?.[i] !== -1), p.thin, lc, radius);
|
|
3679
3815
|
const removed = p.st.mass * cells.reduce((sum, cell) => sum + cell.size[u] * cell.size[v], 0) / (p.dims[u] * p.dims[v]);
|
|
3680
3816
|
const speed = clamp2(Math.sqrt(2 * energy * 0.1 / Math.max(removed, 0.05)), 0.5, 8);
|
|
3681
3817
|
const fragments = cellFragments(o, p, cells, worldCenter, dir, speed, removed, "b");
|
|
@@ -3759,6 +3895,8 @@ var createDestruction = (options = {}) => {
|
|
|
3759
3895
|
const grain = p.spec.grain ? AXIS[p.spec.grain] : void 0;
|
|
3760
3896
|
const hitLocal = toLocal(c, q, hitW);
|
|
3761
3897
|
const unitPlan = p.spec.units ? unitFragmentPlan(p, maxFragments * UNIT_FRAGMENT_FACTOR) : [];
|
|
3898
|
+
const axis = style === "splinter" ? severAxis(p) : -1;
|
|
3899
|
+
const cutAt = axis < 0 ? void 0 : p.rt.cutAt ?? (origin ? hitLocal[axis] : void 0);
|
|
3762
3900
|
const plan = unitPlan.length > 0 ? unitPlan : planFragments({
|
|
3763
3901
|
style,
|
|
3764
3902
|
dims: p.dims,
|
|
@@ -3767,7 +3905,9 @@ var createDestruction = (options = {}) => {
|
|
|
3767
3905
|
hit: hitLocal,
|
|
3768
3906
|
fragmentSize: p.mat.fragmentSize ?? [0.04, 0.2],
|
|
3769
3907
|
maxFragments,
|
|
3770
|
-
rnd
|
|
3908
|
+
rnd,
|
|
3909
|
+
sheet: p.mat.sheet,
|
|
3910
|
+
...cutAt !== void 0 ? { cut: { at: cutAt, gap: SEVER_BAND } } : {}
|
|
3771
3911
|
});
|
|
3772
3912
|
const holes = p.st.holes.length > 0 ? localHolesOf(p) : [];
|
|
3773
3913
|
const mass = p.st.mass;
|
|
@@ -3791,7 +3931,8 @@ var createDestruction = (options = {}) => {
|
|
|
3791
3931
|
velocity: [dv[0] * k, dv[1] * k + 0.5 * rnd(), dv[2] * k],
|
|
3792
3932
|
angularVelocity: [(rnd() - 0.5) * 12, (rnd() - 0.5) * 12, (rnd() - 0.5) * 12],
|
|
3793
3933
|
mass: mass * f.weight / totW,
|
|
3794
|
-
...marks.length > 0 ? { marks } : {}
|
|
3934
|
+
...marks.length > 0 ? { marks } : {},
|
|
3935
|
+
...f.inner ? { inner: true } : {}
|
|
3795
3936
|
};
|
|
3796
3937
|
});
|
|
3797
3938
|
const reinforcement = p.spec.reinforcement;
|
|
@@ -3825,10 +3966,33 @@ var createDestruction = (options = {}) => {
|
|
|
3825
3966
|
emit({ ...base(o, src), type: "primed", part: p.spec.id, point: partXf(o, p).c, trigger, delay });
|
|
3826
3967
|
};
|
|
3827
3968
|
const CHAIN_TRIGGER_OF = { ballistic: "shot", explosive: "explosion" };
|
|
3969
|
+
const severAxis = (p) => {
|
|
3970
|
+
if (p.mat.fracture !== "splinter" || p.spec.shape.kind !== "box") return -1;
|
|
3971
|
+
const d = p.dims;
|
|
3972
|
+
const order = [0, 1, 2].sort((a, b) => d[b] - d[a]);
|
|
3973
|
+
return d[order[0]] >= SEVER_ASPECT * d[order[1]] && d[order[0]] >= 3 * SEVER_BAND ? order[0] : -1;
|
|
3974
|
+
};
|
|
3975
|
+
const bandOf = (o, p, axis, origin) => {
|
|
3976
|
+
const { c, q } = partXf(o, p);
|
|
3977
|
+
const along = toLocal(c, q, origin)[axis];
|
|
3978
|
+
const L = p.dims[axis];
|
|
3979
|
+
return clamp2(Math.floor((along + L / 2) / SEVER_BAND), 0, Math.ceil(L / SEVER_BAND) - 1);
|
|
3980
|
+
};
|
|
3828
3981
|
const applyDamage = (o, p, amount, type, origin, dir, fragEnergy, cap, src) => {
|
|
3982
|
+
var _a;
|
|
3829
3983
|
if (isLost(p) || amount <= 0) return 0;
|
|
3830
3984
|
const prev = p.st.hp;
|
|
3831
3985
|
p.st.hp -= amount;
|
|
3986
|
+
const axis = origin && type !== "fire" && type !== "heat" ? severAxis(p) : -1;
|
|
3987
|
+
if (axis >= 0 && p.st.hp > 0) {
|
|
3988
|
+
const band = bandOf(o, p, axis, origin);
|
|
3989
|
+
const cuts = (_a = p.rt).cuts ?? (_a.cuts = {});
|
|
3990
|
+
cuts[band] = (cuts[band] ?? 0) + amount * (type === "ballistic" ? SEVER_BALLISTIC : 1);
|
|
3991
|
+
if (cuts[band] >= p.st.maxHp * SEVER_BAND / p.dims[axis]) {
|
|
3992
|
+
p.rt.cutAt = -p.dims[axis] / 2 + (band + 0.5) * SEVER_BAND;
|
|
3993
|
+
p.st.hp = 0;
|
|
3994
|
+
}
|
|
3995
|
+
}
|
|
3832
3996
|
markDamaged(p);
|
|
3833
3997
|
const trigger = CHAIN_TRIGGER_OF[type];
|
|
3834
3998
|
const chained = trigger !== void 0 && chainDelayOf(p.spec, trigger) !== void 0;
|
|
@@ -3867,7 +4031,7 @@ var createDestruction = (options = {}) => {
|
|
|
3867
4031
|
const ballistic = (o, p, entry, n, d, E, caliber, pathLen, src, round = {}) => {
|
|
3868
4032
|
const mat = p.mat;
|
|
3869
4033
|
const piercing = round.piercing ?? 1;
|
|
3870
|
-
if (p.st.stripped || p.st.opening && insideOpening(o, p, objLocal(o, entry))) {
|
|
4034
|
+
if (p.st.stripped || p.st.opening && insideOpening(o, p, objLocal(o, entry)) || insideChip(p, objLocal(o, entry))) {
|
|
3871
4035
|
return { penetrated: true, exitEnergy: E, exitPoint: vec.madd(entry, d, Math.min(pathLen, p.thickness)), damage: 0 };
|
|
3872
4036
|
}
|
|
3873
4037
|
const sin = Math.min(1, Math.abs(vec.dot(d, n)));
|
|
@@ -3926,6 +4090,7 @@ var createDestruction = (options = {}) => {
|
|
|
3926
4090
|
debris(o, p, mat.hitDebris, entry, n, clamp2(Math.round(E / 60) + 2, 2, 14), clamp2(Math.sqrt(E) / 6, 1, 8), src);
|
|
3927
4091
|
wearJoints(o, p, (penetrated ? thr : E) * 0.02, void 0, 0, src, true);
|
|
3928
4092
|
const dmg = applyDamage(o, p, E * DMG_PER_J * scale, "ballistic", entry, d, E * 0.1, 12, src);
|
|
4093
|
+
chipUnits(o, p, entry, penetrated ? Math.min(E, Math.max(thr, E * 0.5)) : E, d, Math.max(0.05, caliber / 1e3 * 8), src);
|
|
3929
4094
|
return { penetrated, exitEnergy: penetrated ? penMm !== void 0 ? E * (1 - thr / Ep) : E - thr / (hard ? piercing : 1) : 0, exitPoint, damage: dmg };
|
|
3930
4095
|
};
|
|
3931
4096
|
const melee = (o, p, kind, E, point, n, d, contactR, src) => {
|
|
@@ -3953,6 +4118,7 @@ var createDestruction = (options = {}) => {
|
|
|
3953
4118
|
const scale = mat.damageScale?.[kind] ?? 1;
|
|
3954
4119
|
const done = applyDamage(o, p, Ec * DMG_PER_J * scale * (kind === "slash" ? p.cutFactor : 1), kind, point, d, E * 0.1, 12, src);
|
|
3955
4120
|
addBreach(o, p, point, breachAreaOf(p, done, false), E, d, src);
|
|
4121
|
+
if (kind === "blunt") chipUnits(o, p, point, E, d, Math.max(contactR, 0.03), src);
|
|
3956
4122
|
return done;
|
|
3957
4123
|
};
|
|
3958
4124
|
const blastPart = (o, p, point, energy, radius, src, res, reachAt) => {
|
|
@@ -5444,6 +5610,7 @@ var locales = {
|
|
|
5444
5610
|
sway: "sway",
|
|
5445
5611
|
spall: "plaster came off",
|
|
5446
5612
|
stage: "damage stage",
|
|
5613
|
+
chip: "brick knocked out",
|
|
5447
5614
|
expose: "rebar bare",
|
|
5448
5615
|
body: "body",
|
|
5449
5616
|
blast: "blast",
|
|
@@ -5534,6 +5701,7 @@ var locales = {
|
|
|
5534
5701
|
sway: "balan\xE7o",
|
|
5535
5702
|
spall: "reboco soltou",
|
|
5536
5703
|
stage: "est\xE1gio de dano",
|
|
5704
|
+
chip: "tijolo arrancado",
|
|
5537
5705
|
expose: "vergalh\xE3o \xE0 mostra",
|
|
5538
5706
|
body: "corpo",
|
|
5539
5707
|
blast: "explos\xE3o",
|