@adatechnology/game-destruction 0.9.0 → 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 +12 -0
- package/dist/index.global.js +189 -44
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +189 -44
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -340,6 +340,45 @@ 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
|
+
};
|
|
343
382
|
var tearStack = (inp, root, t, u, v, n, sheet) => {
|
|
344
383
|
const { dims, rnd, maxFragments } = inp;
|
|
345
384
|
const layers = clamp(Math.ceil(dims[t] / STACK_LAYER), 2, 4);
|
|
@@ -378,23 +417,8 @@ var planFragments = (inp) => {
|
|
|
378
417
|
return out;
|
|
379
418
|
}
|
|
380
419
|
if (style === "splinter") {
|
|
381
|
-
|
|
382
|
-
const
|
|
383
|
-
const w = Math.max(fmin * 1.5, 0.01);
|
|
384
|
-
const c = clamp(Math.round(dims[a] * dims[b] / (w * w)), 3, Math.max(3, Math.floor(maxFragments / 2)));
|
|
385
|
-
const maxLen = fmax * 4;
|
|
386
|
-
const cells = bsp(root, c, [a, b], rnd);
|
|
387
|
-
const pieces = [];
|
|
388
|
-
for (const cell of cells) {
|
|
389
|
-
let list = [cell];
|
|
390
|
-
const extra = rnd() < 0.4 ? 1 : 0;
|
|
391
|
-
const k = Math.max(1, Math.ceil(dims[g] / maxLen)) + extra;
|
|
392
|
-
list = bsp(cell, k, [g], rnd);
|
|
393
|
-
for (const p of list) pieces.push(p);
|
|
394
|
-
}
|
|
395
|
-
for (const p of pieces.slice(0, maxFragments)) {
|
|
396
|
-
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] });
|
|
397
|
-
}
|
|
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);
|
|
398
422
|
return out;
|
|
399
423
|
}
|
|
400
424
|
if (style === "tear") {
|
|
@@ -2457,9 +2481,14 @@ var BREACH_BLAST_FACTOR = 6;
|
|
|
2457
2481
|
var BREACH_AREA = 0.25;
|
|
2458
2482
|
var BREACH_BLAST_REACH = 0.12;
|
|
2459
2483
|
var RESIDUAL_CAPACITY = 0.15;
|
|
2484
|
+
var SEVER_BAND = 0.1;
|
|
2485
|
+
var SEVER_ASPECT = 3;
|
|
2486
|
+
var SEVER_BALLISTIC = 2;
|
|
2460
2487
|
var SUPPORT_TOLERANCE = 0.05;
|
|
2461
2488
|
var UNIT_FRAGMENT_FACTOR = 4;
|
|
2462
2489
|
var BREACH_GRID_CAP = 4096;
|
|
2490
|
+
var CHIP_ENERGY = 200;
|
|
2491
|
+
var CHIP_SPLASH = 0.5;
|
|
2463
2492
|
var CUT_SLICE = 1;
|
|
2464
2493
|
var cutFactorOf = (shape) => clamp2(Math.max(...shapeDims(shape)) / CUT_SLICE, 1, 50);
|
|
2465
2494
|
var clamp2 = (v, a, b) => Math.max(a, Math.min(b, v));
|
|
@@ -2503,6 +2532,7 @@ var createDestruction = (options = {}) => {
|
|
|
2503
2532
|
let windCfg = windOpts ? resolveWind(windOpts) : null;
|
|
2504
2533
|
const ignitionRules = resolveIgnitionRules(options.ignition);
|
|
2505
2534
|
const breachBlastFactor = options.breach?.blastFactor ?? BREACH_BLAST_FACTOR;
|
|
2535
|
+
const chipEnergy = options.breach?.chipEnergy ?? CHIP_ENERGY;
|
|
2506
2536
|
const rng = createRng(options.seed ?? 1);
|
|
2507
2537
|
const rnd = () => rng.next();
|
|
2508
2538
|
let time = 0;
|
|
@@ -2876,41 +2906,68 @@ var createDestruction = (options = {}) => {
|
|
|
2876
2906
|
markDamaged(p);
|
|
2877
2907
|
if (p.rt.hinge >= flexure.failRotationDeg * Math.PI / 180) hingeDrop(o, p, src);
|
|
2878
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
|
+
};
|
|
2879
2923
|
const solveStructure = (o) => {
|
|
2880
2924
|
o.maxStress = 0;
|
|
2881
2925
|
if (!o.hasStructure) return;
|
|
2882
2926
|
const parts = o.parts;
|
|
2883
2927
|
const n = parts.length;
|
|
2884
2928
|
const dist = new Array(n).fill(-1);
|
|
2885
|
-
const queue = [];
|
|
2886
2929
|
const ok = (i) => parts[i].structural && !isLost(parts[i]);
|
|
2887
2930
|
const isPartition = (i) => parts[i].spec.structure?.role === "partition";
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
if (
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
const r = j.ia === s ? j.ib : j.ia;
|
|
2898
|
-
if (dist[r] >= 0 || !ok(r) || restsOn(o, j, r) !== s) continue;
|
|
2899
|
-
dist[r] = dist[s] + 1;
|
|
2900
|
-
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);
|
|
2901
2940
|
}
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
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
|
+
}
|
|
2911
2951
|
}
|
|
2912
|
-
lowerOf
|
|
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
|
+
}
|
|
2967
|
+
}
|
|
2968
|
+
if (!dropped) break;
|
|
2913
2969
|
}
|
|
2970
|
+
const incoming = new Array(n).fill(0);
|
|
2914
2971
|
const bending = new Array(n).fill(0);
|
|
2915
2972
|
const through = parts.map(() => []);
|
|
2916
2973
|
const pass = (k, at, mass) => {
|
|
@@ -3422,6 +3479,7 @@ var createDestruction = (options = {}) => {
|
|
|
3422
3479
|
const r = rayPart(o, p, origin, d);
|
|
3423
3480
|
if (!r || r.t0 < 0 || r.t0 > maxDist) continue;
|
|
3424
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;
|
|
3425
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 };
|
|
3426
3484
|
}
|
|
3427
3485
|
}
|
|
@@ -3685,6 +3743,62 @@ var createDestruction = (options = {}) => {
|
|
|
3685
3743
|
if (total < (p.spec.breach?.area ?? BREACH_AREA)) return;
|
|
3686
3744
|
openBreach(o, p, [sum[0] / total, sum[1] / total, sum[2] / total], total, energy, dir, src);
|
|
3687
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
|
+
};
|
|
3688
3802
|
const openBreach = (o, p, center, total, energy, dir, src) => {
|
|
3689
3803
|
const [u, v] = planeAxes(p.thin);
|
|
3690
3804
|
const radius = clamp2(Math.sqrt(total / Math.PI), Math.sqrt((p.spec.breach?.area ?? BREACH_AREA) / Math.PI), breachRadius);
|
|
@@ -3696,7 +3810,8 @@ var createDestruction = (options = {}) => {
|
|
|
3696
3810
|
failPart(o, p, worldCenter, dir, energy, 12, src);
|
|
3697
3811
|
return;
|
|
3698
3812
|
}
|
|
3699
|
-
const
|
|
3813
|
+
const worn = p.rt.chips;
|
|
3814
|
+
const cells = cellsWithin(gridOf(p).filter((_, i) => worn?.[i] !== -1), p.thin, lc, radius);
|
|
3700
3815
|
const removed = p.st.mass * cells.reduce((sum, cell) => sum + cell.size[u] * cell.size[v], 0) / (p.dims[u] * p.dims[v]);
|
|
3701
3816
|
const speed = clamp2(Math.sqrt(2 * energy * 0.1 / Math.max(removed, 0.05)), 0.5, 8);
|
|
3702
3817
|
const fragments = cellFragments(o, p, cells, worldCenter, dir, speed, removed, "b");
|
|
@@ -3780,6 +3895,8 @@ var createDestruction = (options = {}) => {
|
|
|
3780
3895
|
const grain = p.spec.grain ? AXIS[p.spec.grain] : void 0;
|
|
3781
3896
|
const hitLocal = toLocal(c, q, hitW);
|
|
3782
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);
|
|
3783
3900
|
const plan = unitPlan.length > 0 ? unitPlan : planFragments({
|
|
3784
3901
|
style,
|
|
3785
3902
|
dims: p.dims,
|
|
@@ -3789,7 +3906,8 @@ var createDestruction = (options = {}) => {
|
|
|
3789
3906
|
fragmentSize: p.mat.fragmentSize ?? [0.04, 0.2],
|
|
3790
3907
|
maxFragments,
|
|
3791
3908
|
rnd,
|
|
3792
|
-
sheet: p.mat.sheet
|
|
3909
|
+
sheet: p.mat.sheet,
|
|
3910
|
+
...cutAt !== void 0 ? { cut: { at: cutAt, gap: SEVER_BAND } } : {}
|
|
3793
3911
|
});
|
|
3794
3912
|
const holes = p.st.holes.length > 0 ? localHolesOf(p) : [];
|
|
3795
3913
|
const mass = p.st.mass;
|
|
@@ -3848,10 +3966,33 @@ var createDestruction = (options = {}) => {
|
|
|
3848
3966
|
emit({ ...base(o, src), type: "primed", part: p.spec.id, point: partXf(o, p).c, trigger, delay });
|
|
3849
3967
|
};
|
|
3850
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
|
+
};
|
|
3851
3981
|
const applyDamage = (o, p, amount, type, origin, dir, fragEnergy, cap, src) => {
|
|
3982
|
+
var _a;
|
|
3852
3983
|
if (isLost(p) || amount <= 0) return 0;
|
|
3853
3984
|
const prev = p.st.hp;
|
|
3854
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
|
+
}
|
|
3855
3996
|
markDamaged(p);
|
|
3856
3997
|
const trigger = CHAIN_TRIGGER_OF[type];
|
|
3857
3998
|
const chained = trigger !== void 0 && chainDelayOf(p.spec, trigger) !== void 0;
|
|
@@ -3890,7 +4031,7 @@ var createDestruction = (options = {}) => {
|
|
|
3890
4031
|
const ballistic = (o, p, entry, n, d, E, caliber, pathLen, src, round = {}) => {
|
|
3891
4032
|
const mat = p.mat;
|
|
3892
4033
|
const piercing = round.piercing ?? 1;
|
|
3893
|
-
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))) {
|
|
3894
4035
|
return { penetrated: true, exitEnergy: E, exitPoint: vec.madd(entry, d, Math.min(pathLen, p.thickness)), damage: 0 };
|
|
3895
4036
|
}
|
|
3896
4037
|
const sin = Math.min(1, Math.abs(vec.dot(d, n)));
|
|
@@ -3949,6 +4090,7 @@ var createDestruction = (options = {}) => {
|
|
|
3949
4090
|
debris(o, p, mat.hitDebris, entry, n, clamp2(Math.round(E / 60) + 2, 2, 14), clamp2(Math.sqrt(E) / 6, 1, 8), src);
|
|
3950
4091
|
wearJoints(o, p, (penetrated ? thr : E) * 0.02, void 0, 0, src, true);
|
|
3951
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);
|
|
3952
4094
|
return { penetrated, exitEnergy: penetrated ? penMm !== void 0 ? E * (1 - thr / Ep) : E - thr / (hard ? piercing : 1) : 0, exitPoint, damage: dmg };
|
|
3953
4095
|
};
|
|
3954
4096
|
const melee = (o, p, kind, E, point, n, d, contactR, src) => {
|
|
@@ -3976,6 +4118,7 @@ var createDestruction = (options = {}) => {
|
|
|
3976
4118
|
const scale = mat.damageScale?.[kind] ?? 1;
|
|
3977
4119
|
const done = applyDamage(o, p, Ec * DMG_PER_J * scale * (kind === "slash" ? p.cutFactor : 1), kind, point, d, E * 0.1, 12, src);
|
|
3978
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);
|
|
3979
4122
|
return done;
|
|
3980
4123
|
};
|
|
3981
4124
|
const blastPart = (o, p, point, energy, radius, src, res, reachAt) => {
|
|
@@ -5467,6 +5610,7 @@ var locales = {
|
|
|
5467
5610
|
sway: "sway",
|
|
5468
5611
|
spall: "plaster came off",
|
|
5469
5612
|
stage: "damage stage",
|
|
5613
|
+
chip: "brick knocked out",
|
|
5470
5614
|
expose: "rebar bare",
|
|
5471
5615
|
body: "body",
|
|
5472
5616
|
blast: "blast",
|
|
@@ -5557,6 +5701,7 @@ var locales = {
|
|
|
5557
5701
|
sway: "balan\xE7o",
|
|
5558
5702
|
spall: "reboco soltou",
|
|
5559
5703
|
stage: "est\xE1gio de dano",
|
|
5704
|
+
chip: "tijolo arrancado",
|
|
5560
5705
|
expose: "vergalh\xE3o \xE0 mostra",
|
|
5561
5706
|
body: "corpo",
|
|
5562
5707
|
blast: "explos\xE3o",
|