@adatechnology/game-destruction 0.11.0 → 0.13.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 +7 -0
- package/dist/index.global.js +159 -23
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +159 -23
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1864,6 +1864,7 @@ var materials_default = [
|
|
|
1864
1864
|
},
|
|
1865
1865
|
hitDebris: "chips",
|
|
1866
1866
|
breakDebris: "dust",
|
|
1867
|
+
visual: { innerColor: 10197652 },
|
|
1867
1868
|
thermal: {
|
|
1868
1869
|
conductivity: 0.15,
|
|
1869
1870
|
heatCapacity: 2.5
|
|
@@ -1894,6 +1895,7 @@ var materials_default = [
|
|
|
1894
1895
|
],
|
|
1895
1896
|
hitDebris: "chips",
|
|
1896
1897
|
breakDebris: "dust",
|
|
1898
|
+
visual: { innerColor: 12085567 },
|
|
1897
1899
|
thermal: {
|
|
1898
1900
|
conductivity: 0.15,
|
|
1899
1901
|
heatCapacity: 2.5
|
|
@@ -1924,6 +1926,7 @@ var materials_default = [
|
|
|
1924
1926
|
],
|
|
1925
1927
|
hitDebris: "dust",
|
|
1926
1928
|
breakDebris: "dust",
|
|
1929
|
+
visual: { innerColor: 14999507 },
|
|
1927
1930
|
thermal: {
|
|
1928
1931
|
conductivity: 0.1,
|
|
1929
1932
|
heatCapacity: 1.5
|
|
@@ -1949,6 +1952,7 @@ var materials_default = [
|
|
|
1949
1952
|
],
|
|
1950
1953
|
hitDebris: "chips",
|
|
1951
1954
|
breakDebris: "dust",
|
|
1955
|
+
visual: { innerColor: 9407364 },
|
|
1952
1956
|
thermal: {
|
|
1953
1957
|
conductivity: 0.2,
|
|
1954
1958
|
heatCapacity: 2.5
|
|
@@ -2702,6 +2706,7 @@ var CONFINEMENT_DEFAULTS = { rays: 26, reach: 1.5, gain: 1.5, power: 2, max: 3 }
|
|
|
2702
2706
|
var FLAMMABLE_FLUIDS = ["gas", "fuel"];
|
|
2703
2707
|
var VENT_DIAMETER = 8e-3;
|
|
2704
2708
|
var FLAME_LIGHTS_LEAK_C = 470;
|
|
2709
|
+
var RESTING_HOLD_COS = Math.cos(Math.PI / 6);
|
|
2705
2710
|
var BREACH_RADIUS = 0.55;
|
|
2706
2711
|
var BREACH_BLAST_FACTOR = 6;
|
|
2707
2712
|
var BREACH_AREA = 0.25;
|
|
@@ -2717,7 +2722,9 @@ var UNIT_FRAGMENT_FACTOR = 4;
|
|
|
2717
2722
|
var BREACH_GRID_CAP = 4096;
|
|
2718
2723
|
var CHIP_ENERGY = 200;
|
|
2719
2724
|
var CHIP_SPLASH = 0.5;
|
|
2725
|
+
var CRATER_SPENT = 0.6;
|
|
2720
2726
|
var BAR_BOW = 0.08;
|
|
2727
|
+
var BULLET_JOINT_SPAN = 0.3;
|
|
2721
2728
|
var CUT_SLICE = 1;
|
|
2722
2729
|
var cutFactorOf = (shape) => clamp2(Math.max(...shapeDims(shape)) / CUT_SLICE, 1, 50);
|
|
2723
2730
|
var clamp2 = (v, a, b) => Math.max(a, Math.min(b, v));
|
|
@@ -2736,12 +2743,22 @@ var sectionLoss = (p) => {
|
|
|
2736
2743
|
const cells = p.st.chipped;
|
|
2737
2744
|
if (!cells?.length) return 0;
|
|
2738
2745
|
const [u, v] = planeAxes(p.thin);
|
|
2739
|
-
const
|
|
2746
|
+
const role = p.spec.structure?.role;
|
|
2747
|
+
const up = rotateVec(p.q, [0, 0, 0].map((_, k) => k === v ? 1 : 0));
|
|
2748
|
+
const upright = Math.abs(up[1]) > 0.7 ? v : Math.abs(rotateVec(p.q, [0, 0, 0].map((_, k) => k === u ? 1 : 0))[1]) > 0.7 ? u : -1;
|
|
2749
|
+
const along = (role === "wall" || role === "partition" || role === "column") && upright >= 0 ? upright : p.dims[u] >= p.dims[v] ? u : v;
|
|
2740
2750
|
const across = along === u ? v : u;
|
|
2741
2751
|
let worst = 0;
|
|
2742
2752
|
for (const c of cells) {
|
|
2743
|
-
|
|
2744
|
-
for (const d of cells) if (Math.abs(d.center[along] - c.center[along]) < d.size[along] / 2 + 1e-6)
|
|
2753
|
+
const spans = [];
|
|
2754
|
+
for (const d of cells) if (Math.abs(d.center[along] - c.center[along]) < d.size[along] / 2 + 1e-6) spans.push([d.center[across] - d.size[across] / 2, d.center[across] + d.size[across] / 2]);
|
|
2755
|
+
spans.sort((a, b) => a[0] - b[0]);
|
|
2756
|
+
let gone = 0, end = -Infinity;
|
|
2757
|
+
for (const [a, b] of spans) {
|
|
2758
|
+
if (b <= end) continue;
|
|
2759
|
+
gone += b - Math.max(a, end);
|
|
2760
|
+
end = b;
|
|
2761
|
+
}
|
|
2745
2762
|
worst = Math.max(worst, gone / Math.max(p.dims[across], 1e-6));
|
|
2746
2763
|
}
|
|
2747
2764
|
return Math.min(1, worst);
|
|
@@ -3037,7 +3054,18 @@ var createDestruction = (options = {}) => {
|
|
|
3037
3054
|
if (k < 0 || j.spec.kind !== "tie") return k;
|
|
3038
3055
|
return partXf(o, o.parts[k]).c[1] > partXf(o, o.parts[r]).c[1] + 1e-6 ? k : -1;
|
|
3039
3056
|
};
|
|
3057
|
+
let poseVersion = 0;
|
|
3058
|
+
const contactMemo = /* @__PURE__ */ new WeakMap();
|
|
3040
3059
|
const contactBox = (p, support) => {
|
|
3060
|
+
let byTouching = contactMemo.get(p);
|
|
3061
|
+
if (!byTouching) contactMemo.set(p, byTouching = /* @__PURE__ */ new Map());
|
|
3062
|
+
const known = byTouching.get(support);
|
|
3063
|
+
if (known && known.version === poseVersion) return known.box;
|
|
3064
|
+
const box = measureContact(p, support);
|
|
3065
|
+
byTouching.set(support, { version: poseVersion, box });
|
|
3066
|
+
return box;
|
|
3067
|
+
};
|
|
3068
|
+
const measureContact = (p, support) => {
|
|
3041
3069
|
const A = aabbOf(p.spec.shape, p.pos, p.q), B = aabbOf(support.spec.shape, support.pos, support.q);
|
|
3042
3070
|
const lo = [0, 0, 0], hi = [0, 0, 0];
|
|
3043
3071
|
for (let k = 0; k < 3; k++) {
|
|
@@ -3832,7 +3860,7 @@ var createDestruction = (options = {}) => {
|
|
|
3832
3860
|
p.st.holes.push(hole);
|
|
3833
3861
|
if (p.st.holes.length > maxHoles) p.st.holes.shift();
|
|
3834
3862
|
markDamaged(p);
|
|
3835
|
-
emit({ ...base(o, src), type: "hole", part: p.spec.id, hole, worldPoint: wp, ...exit ? { exitPoint: exit } : {} });
|
|
3863
|
+
emit({ ...base(o, src), type: "hole", part: p.spec.id, hole, worldPoint: wp, ...exit ? { exitPoint: exit } : {}, ...p.spec.powder ? { powder: { ...p.spec.powder } } : {} });
|
|
3836
3864
|
setStage(o, p, 1, {}, src);
|
|
3837
3865
|
return hole;
|
|
3838
3866
|
};
|
|
@@ -3844,11 +3872,27 @@ var createDestruction = (options = {}) => {
|
|
|
3844
3872
|
emit({ ...base(o, src), type: "crack", part: p.spec.id, crack, worldPoint: wp });
|
|
3845
3873
|
setStage(o, p, 1, {}, src);
|
|
3846
3874
|
};
|
|
3847
|
-
const debris = (o, p, kind, point, normal, count, speed, src) => emit({ ...base(o, src), type: "debris", part: p.spec.id, kind, point, normal, count, speed });
|
|
3875
|
+
const debris = (o, p, kind, point, normal, count, speed, src, material) => emit({ ...base(o, src), type: "debris", part: p.spec.id, kind, point, normal, count, speed, ...material && material !== p.spec.material ? { material } : {} });
|
|
3848
3876
|
const holeRate = (hole, pressurized) => (pressurized ? 0.5 : 0.1) * (hole.diameter / 9e-3);
|
|
3849
3877
|
const detachEvents = (o, opened, by, src) => {
|
|
3850
3878
|
for (const j of opened) emit({ ...base(o, src), type: "detach", joint: j.spec.id, parts: [j.spec.a, j.spec.b], by, groups: o.st.groups.map((g) => [...g]) });
|
|
3851
3879
|
};
|
|
3880
|
+
const slideOffResting = (o, rotation) => {
|
|
3881
|
+
const upY = 1 - 2 * (rotation[0] * rotation[0] + rotation[2] * rotation[2]);
|
|
3882
|
+
if (upY >= RESTING_HOLD_COS) return;
|
|
3883
|
+
const opened = o.joints.filter((j) => j.spec.kind === "resting" && !j.st.open);
|
|
3884
|
+
if (opened.length === 0) return;
|
|
3885
|
+
run(() => {
|
|
3886
|
+
for (const j of opened) {
|
|
3887
|
+
j.st.open = true;
|
|
3888
|
+
j.st.openedBy = "break";
|
|
3889
|
+
}
|
|
3890
|
+
solveStructure(o);
|
|
3891
|
+
recomputeGroups(o);
|
|
3892
|
+
detachEvents(o, opened, "break");
|
|
3893
|
+
collapseCheck(o);
|
|
3894
|
+
});
|
|
3895
|
+
};
|
|
3852
3896
|
const checkDestroyed = (o, src) => {
|
|
3853
3897
|
if (!o.st.destroyed && o.parts.length > 0 && o.parts.every(isLost)) {
|
|
3854
3898
|
o.st.destroyed = true;
|
|
@@ -3885,7 +3929,13 @@ var createDestruction = (options = {}) => {
|
|
|
3885
3929
|
const small = pa.dims[0] * pa.dims[1] * pa.dims[2] <= pb.dims[0] * pb.dims[1] * pb.dims[2] ? pa : pb;
|
|
3886
3930
|
return Math.max(0.06, 0.5 * Math.min(small.dims[0], small.dims[1], small.dims[2]));
|
|
3887
3931
|
};
|
|
3888
|
-
const
|
|
3932
|
+
const jointLength = (o, j) => {
|
|
3933
|
+
const A = partWorldAabb(o, o.parts[j.ia]), B = partWorldAabb(o, o.parts[j.ib]);
|
|
3934
|
+
let len = 0;
|
|
3935
|
+
for (let k = 0; k < 3; k++) len = Math.max(len, Math.min(A.max[k], B.max[k]) - Math.max(A.min[k], B.min[k]));
|
|
3936
|
+
return len;
|
|
3937
|
+
};
|
|
3938
|
+
const wearJoints = (o, p, amount, near, radius = 0.06, src, split = false, span) => {
|
|
3889
3939
|
if (amount <= 0) return;
|
|
3890
3940
|
const mine = (o.byPart.get(p.spec.id) ?? []).filter((j) => !j.st.open);
|
|
3891
3941
|
if (split && mine.length) amount /= mine.length;
|
|
@@ -3893,7 +3943,7 @@ var createDestruction = (options = {}) => {
|
|
|
3893
3943
|
if (j.st.open) continue;
|
|
3894
3944
|
if (j.spec.kind === "rebar" && (isReinforced(o.parts[j.ia].spec) || isReinforced(o.parts[j.ib].spec))) continue;
|
|
3895
3945
|
if (near && jointReach(o, j, near) > Math.max(radius, jointReachLimit(o, j))) continue;
|
|
3896
|
-
j.st.hp -= amount;
|
|
3946
|
+
j.st.hp -= span === void 0 ? amount : amount * Math.min(1, span / Math.max(jointLength(o, j), 1e-6));
|
|
3897
3947
|
if (j.st.hp <= 0) {
|
|
3898
3948
|
j.st.hp = 0;
|
|
3899
3949
|
j.st.open = true;
|
|
@@ -3922,7 +3972,7 @@ var createDestruction = (options = {}) => {
|
|
|
3922
3972
|
if (spalls.length > MAX_SPALLS) spalls.shift();
|
|
3923
3973
|
markDamaged(p);
|
|
3924
3974
|
emit({ ...base(o, src), type: "spall", part: p.spec.id, layer: layer.index, point: wp, area: Math.PI * radius * radius });
|
|
3925
|
-
debris(o, p, layer.mat.hitDebris, wp, normal, 5, 2, src);
|
|
3975
|
+
debris(o, p, layer.mat.hitDebris, wp, normal, 5, 2, src, layer.mat.id);
|
|
3926
3976
|
setStage(o, p, 1, {}, src);
|
|
3927
3977
|
};
|
|
3928
3978
|
const crater = (o, p, wp, normal, radius, src) => {
|
|
@@ -3943,7 +3993,7 @@ var createDestruction = (options = {}) => {
|
|
|
3943
3993
|
const localHolesOf = (p) => p.st.holes.map((h) => ({ point: toLocal(p.pos, p.q, h.point), normal: rotateVec(quat.conjugate(p.q), h.normal), diameter: h.diameter }));
|
|
3944
3994
|
const unitsOf = (p) => p.spec.units ?? { size: [p.mat.fragmentSize?.[1] ?? 0.2, p.mat.fragmentSize?.[1] ?? 0.2, p.thickness], bond: "stack" };
|
|
3945
3995
|
const FRAGMENT_SHAPE = { shatter: "shard", splinter: "splinter", tear: "scrap" };
|
|
3946
|
-
const cellFragments = (o, p, cells, hitW, dir, speed, mass, tag) => {
|
|
3996
|
+
const cellFragments = (o, p, cells, hitW, dir, speed, mass, tag, inner = false) => {
|
|
3947
3997
|
const { c, q } = partXf(o, p);
|
|
3948
3998
|
const holes = p.st.holes.length > 0 ? localHolesOf(p) : [];
|
|
3949
3999
|
const [u, v] = planeAxes(p.thin);
|
|
@@ -3967,7 +4017,8 @@ var createDestruction = (options = {}) => {
|
|
|
3967
4017
|
velocity: [dv[0] * k, dv[1] * k + 0.5 * rnd(), dv[2] * k],
|
|
3968
4018
|
angularVelocity: [(rnd() - 0.5) * 8, (rnd() - 0.5) * 8, (rnd() - 0.5) * 8],
|
|
3969
4019
|
mass: mass * faceArea(cell) / total,
|
|
3970
|
-
...marks.length > 0 ? { marks } : {}
|
|
4020
|
+
...marks.length > 0 ? { marks } : {},
|
|
4021
|
+
...inner ? { inner } : {}
|
|
3971
4022
|
};
|
|
3972
4023
|
});
|
|
3973
4024
|
};
|
|
@@ -4008,7 +4059,7 @@ var createDestruction = (options = {}) => {
|
|
|
4008
4059
|
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);
|
|
4009
4060
|
};
|
|
4010
4061
|
const chipUnits = (o, p, kind, wp, hitEnergy, dir, radius, src) => {
|
|
4011
|
-
var _a, _b;
|
|
4062
|
+
var _a, _b, _c;
|
|
4012
4063
|
if (!p.spec.units || !p.staged || chipEnergy <= 0 || p.st.stripped || isLost(p) || (p.st.stage ?? 0) >= 2) return;
|
|
4013
4064
|
const rule = p.mat.hits?.[kind];
|
|
4014
4065
|
const perKg = rule?.unitJoules ?? chipEnergy;
|
|
@@ -4018,7 +4069,9 @@ var createDestruction = (options = {}) => {
|
|
|
4018
4069
|
const lp = toLocal(p.pos, p.q, objLocal(o, wp));
|
|
4019
4070
|
const grid = gridOf(p);
|
|
4020
4071
|
const worn = (_a = p.rt).chips ?? (_a.chips = {});
|
|
4072
|
+
const cut = (_b = p.rt).craters ?? (_b.craters = {});
|
|
4021
4073
|
const out = [];
|
|
4074
|
+
const shot = kind === "ballistic";
|
|
4022
4075
|
for (let i = 0; i < grid.length; i++) {
|
|
4023
4076
|
const cell = grid[i];
|
|
4024
4077
|
if (worn[i] === -1) continue;
|
|
@@ -4031,18 +4084,39 @@ var createDestruction = (options = {}) => {
|
|
|
4031
4084
|
worn[i] = total;
|
|
4032
4085
|
continue;
|
|
4033
4086
|
}
|
|
4034
|
-
|
|
4035
|
-
|
|
4087
|
+
if (!shot) {
|
|
4088
|
+
worn[i] = -1;
|
|
4089
|
+
out.push(cell);
|
|
4090
|
+
continue;
|
|
4091
|
+
}
|
|
4092
|
+
const size = [cell.size[0], cell.size[1], cell.size[2]];
|
|
4093
|
+
const center = [cell.center[0], cell.center[1], cell.center[2]];
|
|
4094
|
+
for (const a of [u, v]) {
|
|
4095
|
+
const w = Math.min(cell.size[a], craterAcross(hitEnergy) * (0.85 + 0.3 * rnd()));
|
|
4096
|
+
const lo = cell.center[a] - cell.size[a] / 2 + w / 2, hi = cell.center[a] + cell.size[a] / 2 - w / 2;
|
|
4097
|
+
size[a] = w;
|
|
4098
|
+
center[a] = clamp2(lp[a], lo, hi);
|
|
4099
|
+
}
|
|
4100
|
+
const taken2 = (cut[i] ?? 0) + size[u] * size[v];
|
|
4101
|
+
if (taken2 >= cell.size[u] * cell.size[v] * CRATER_SPENT) {
|
|
4102
|
+
worn[i] = -1;
|
|
4103
|
+
out.push({ ...cell, crater: true });
|
|
4104
|
+
continue;
|
|
4105
|
+
}
|
|
4106
|
+
worn[i] = 0;
|
|
4107
|
+
cut[i] = taken2;
|
|
4108
|
+
out.push({ center, size, crater: true });
|
|
4036
4109
|
}
|
|
4037
4110
|
if (out.length === 0) return;
|
|
4038
4111
|
const { c, q } = partXf(o, p);
|
|
4039
|
-
const chipped = (
|
|
4112
|
+
const chipped = (_c = p.st).chipped ?? (_c.chipped = []);
|
|
4040
4113
|
const holes = localHolesOf(p);
|
|
4041
4114
|
for (const cell of out) {
|
|
4042
4115
|
const mass = Math.min(p.st.mass, p.mat.density * cell.size[0] * cell.size[1] * cell.size[2]);
|
|
4043
4116
|
const center = toWorld(c, q, cell.center);
|
|
4044
4117
|
const speed = clamp2(Math.sqrt(2 * energy * 0.05 / Math.max(mass, 0.05)), 0.5, 6);
|
|
4045
|
-
const
|
|
4118
|
+
const tag = `c${chipped.length}_`;
|
|
4119
|
+
const fragments = cell.crater ? craterFragments(o, p, cell, dir, speed, mass, tag) : cellFragments(o, p, [cell], wp, dir, speed, mass, tag, true);
|
|
4046
4120
|
p.st.holes = p.st.holes.filter((_, k) => marksInBox(cell.center, cell.size, void 0, [holes[k]]).length === 0);
|
|
4047
4121
|
chipped.push({ center: [cell.center[0], cell.center[1], cell.center[2]], size: [cell.size[0], cell.size[1], cell.size[2]] });
|
|
4048
4122
|
p.st.mass = Math.max(0, p.st.mass - mass);
|
|
@@ -4059,6 +4133,44 @@ var createDestruction = (options = {}) => {
|
|
|
4059
4133
|
collapseCheck(o, src);
|
|
4060
4134
|
}
|
|
4061
4135
|
};
|
|
4136
|
+
const craterAcross = (energy) => clamp2(0.03 + 12e-4 * Math.sqrt(Math.max(0, energy)), 0.03, 0.14);
|
|
4137
|
+
const craterFragments = (o, p, cell, dir, speed, mass, tag) => {
|
|
4138
|
+
const { c, q } = partXf(o, p);
|
|
4139
|
+
const [u, v] = planeAxes(p.thin);
|
|
4140
|
+
const d = dir ?? rotateVec(q, [p.thin === 0 ? 1 : 0, p.thin === 1 ? 1 : 0, p.thin === 2 ? 1 : 0]);
|
|
4141
|
+
const exitSign = Math.sign(rotateVec(quat.conjugate(q), d)[p.thin]) || 1;
|
|
4142
|
+
const bit = Math.max(0.01, Math.min(cell.size[u], cell.size[v]) / 3);
|
|
4143
|
+
const n = clamp2(Math.round(cell.size[u] * cell.size[v] / (bit * bit * 3)), 4, 10) + Math.floor(rnd() * 3);
|
|
4144
|
+
const out = [];
|
|
4145
|
+
for (let i = 0; i < n; i++) {
|
|
4146
|
+
const back = i % 3 === 2;
|
|
4147
|
+
const lc = [cell.center[0], cell.center[1], cell.center[2]];
|
|
4148
|
+
lc[u] += (rnd() - 0.5) * cell.size[u] * 0.7;
|
|
4149
|
+
lc[v] += (rnd() - 0.5) * cell.size[v] * 0.7;
|
|
4150
|
+
lc[p.thin] += (back ? -exitSign : exitSign) * (p.thickness / 2 + 0.01);
|
|
4151
|
+
const size = [0, 0, 0];
|
|
4152
|
+
size[u] = bit * (0.6 + 0.8 * rnd());
|
|
4153
|
+
size[v] = bit * (0.5 + 0.7 * rnd());
|
|
4154
|
+
size[p.thin] = bit * (0.4 + 0.6 * rnd());
|
|
4155
|
+
const spread3 = [(rnd() - 0.5) * 0.9, (rnd() - 0.3) * 0.9, (rnd() - 0.5) * 0.9];
|
|
4156
|
+
const k = speed * (back ? 0.4 + 0.4 * rnd() : 0.7 + 0.8 * rnd());
|
|
4157
|
+
const vdir = vec.normalize(vec.add(back ? vec.scale(d, -1) : d, spread3));
|
|
4158
|
+
out.push({
|
|
4159
|
+
id: `${p.spec.id}#${tag}${i}`,
|
|
4160
|
+
part: p.spec.id,
|
|
4161
|
+
material: p.spec.material,
|
|
4162
|
+
shape: "chunk",
|
|
4163
|
+
center: toWorld(c, q, lc),
|
|
4164
|
+
size,
|
|
4165
|
+
rotation: q,
|
|
4166
|
+
velocity: [vdir[0] * k, vdir[1] * k + 0.5 * rnd(), vdir[2] * k],
|
|
4167
|
+
angularVelocity: [(rnd() - 0.5) * 20, (rnd() - 0.5) * 20, (rnd() - 0.5) * 20],
|
|
4168
|
+
mass: mass / n,
|
|
4169
|
+
inner: true
|
|
4170
|
+
});
|
|
4171
|
+
}
|
|
4172
|
+
return out;
|
|
4173
|
+
};
|
|
4062
4174
|
const exposeRebar = (o, p, src) => {
|
|
4063
4175
|
const r = p.spec.reinforcement;
|
|
4064
4176
|
if (!r) return;
|
|
@@ -4346,7 +4458,7 @@ var createDestruction = (options = {}) => {
|
|
|
4346
4458
|
});
|
|
4347
4459
|
}
|
|
4348
4460
|
const patch = [scale * 4, scale * 4, scale * 4];
|
|
4349
|
-
emit({ ...base(o, src), type: "chip", part: p.spec.id, center: exit ?? entry, size: patch, rotation: q, fragments });
|
|
4461
|
+
emit({ ...base(o, src), type: "chip", part: p.spec.id, center: exit ?? entry, size: patch, rotation: q, fragments, torn: true });
|
|
4350
4462
|
};
|
|
4351
4463
|
const ballistic = (o, p, entry, n, d, E, caliber, pathLen, src, round = {}) => {
|
|
4352
4464
|
const mat = p.mat;
|
|
@@ -4405,12 +4517,13 @@ var createDestruction = (options = {}) => {
|
|
|
4405
4517
|
} else if (mat.ductility >= 0.5) {
|
|
4406
4518
|
addDent(o, p, entry, n, Math.max(caliber * 3 / 1e3, 0.012), clamp2(Math.min(Math.max(p.thickness * 3, 3e-3), ratio * 0.01), 1e-3, 0.05), src);
|
|
4407
4519
|
} else if (brittle) {
|
|
4408
|
-
addCrack(o, p, entry, n, crackLen, src);
|
|
4520
|
+
if (!p.spec.units) addCrack(o, p, entry, n, crackLen, src);
|
|
4409
4521
|
if (mat.fracture !== "shatter") addHole(o, p, entry, n, caliber / 1e3, false, void 0, src);
|
|
4410
4522
|
} else addHole(o, p, entry, n, caliber / 1e3, false, void 0, src);
|
|
4411
4523
|
debris(o, p, mat.hitDebris, entry, n, clamp2(Math.round(E / 60) + 2, 2, 14), clamp2(Math.sqrt(E) / 6, 1, 8), src);
|
|
4412
|
-
wearJoints(o, p, (penetrated ? thr : E) * 0.02, void 0, 0, src, true);
|
|
4413
|
-
const
|
|
4524
|
+
wearJoints(o, p, (penetrated ? thr : E) * 0.02, void 0, 0, src, true, BULLET_JOINT_SPAN);
|
|
4525
|
+
const hollowTear = mat.fracture === "tear" && p.spec.fill !== void 0 && p.spec.fill < 0.5;
|
|
4526
|
+
const absorbed = penetrated && (mat.ductility >= 0.5 && mat.fracture !== "tear" || hollowTear) ? Math.min(E, thr) : E;
|
|
4414
4527
|
const dmg = applyDamage(o, p, absorbed * DMG_PER_J * scale, "ballistic", entry, d, absorbed * 0.1, 12, src);
|
|
4415
4528
|
if (!isLost(p)) tearAt(o, p, "ballistic", entry, penetrated ? exitPoint : void 0, d, E, caliber / 1e3, src);
|
|
4416
4529
|
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);
|
|
@@ -4495,10 +4608,25 @@ var createDestruction = (options = {}) => {
|
|
|
4495
4608
|
if (dist <= BREACH_BLAST_REACH * Math.cbrt(Math.max(0, energy) / 1e3)) addBreach(o, p, wp, breachAreaOf(p, dealt, true), Ein, null, src);
|
|
4496
4609
|
return reach;
|
|
4497
4610
|
};
|
|
4611
|
+
const blastProbe = { partsTested: 0, partsNear: 0 };
|
|
4612
|
+
const blastCandidates = (o, point, radius) => {
|
|
4613
|
+
const local = toLocal(o.st.position, o.st.rotation, point);
|
|
4614
|
+
const near = [];
|
|
4615
|
+
for (const p of o.parts) {
|
|
4616
|
+
const reach = radius + p.bound;
|
|
4617
|
+
const dx = local[0] - p.pos[0];
|
|
4618
|
+
const dy = local[1] - p.pos[1];
|
|
4619
|
+
const dz = local[2] - p.pos[2];
|
|
4620
|
+
if (dx * dx + dy * dy + dz * dz < reach * reach) near.push(p);
|
|
4621
|
+
}
|
|
4622
|
+
blastProbe.partsTested += o.parts.length;
|
|
4623
|
+
blastProbe.partsNear += near.length;
|
|
4624
|
+
return near;
|
|
4625
|
+
};
|
|
4498
4626
|
const blastObject = (o, point, energy, radius, src) => {
|
|
4499
4627
|
const start = cur.length;
|
|
4500
4628
|
const res = { object: o.id, damage: 0, penetrated: false, exitEnergy: 0, events: [] };
|
|
4501
|
-
for (const p of o
|
|
4629
|
+
for (const p of blastCandidates(o, point, radius)) blastPart(o, p, point, energy, radius, src, res);
|
|
4502
4630
|
res.events = cur.slice(start);
|
|
4503
4631
|
return res;
|
|
4504
4632
|
};
|
|
@@ -4622,7 +4750,7 @@ var createDestruction = (options = {}) => {
|
|
|
4622
4750
|
let order = 0;
|
|
4623
4751
|
for (const o of inRange) {
|
|
4624
4752
|
if (only && only !== o) continue;
|
|
4625
|
-
for (const p of o
|
|
4753
|
+
for (const p of blastCandidates(o, point, radius)) {
|
|
4626
4754
|
if (isLost(p)) continue;
|
|
4627
4755
|
const dist = distToPart(o, p, point);
|
|
4628
4756
|
if (dist < radius) items.push({ dist, order: order++, o, p });
|
|
@@ -5169,6 +5297,7 @@ var createDestruction = (options = {}) => {
|
|
|
5169
5297
|
const p = getPart(o, partId);
|
|
5170
5298
|
p.pos = [position[0], position[1], position[2]];
|
|
5171
5299
|
p.q = q;
|
|
5300
|
+
poseVersion++;
|
|
5172
5301
|
updateBound(o);
|
|
5173
5302
|
},
|
|
5174
5303
|
remove(id) {
|
|
@@ -5187,6 +5316,7 @@ var createDestruction = (options = {}) => {
|
|
|
5187
5316
|
const o = getObj(id);
|
|
5188
5317
|
o.base = { pos: [position[0], position[1], position[2]], rot: [rotation[0], rotation[1], rotation[2], rotation[3]] };
|
|
5189
5318
|
applySoilPose(o);
|
|
5319
|
+
slideOffResting(o, rotation);
|
|
5190
5320
|
},
|
|
5191
5321
|
splitGroup(id, group, newId) {
|
|
5192
5322
|
if (impls.has(id)) throw new Error(`object "${id}" is a registered DestructibleImpl and cannot be split; its module owns its pieces`);
|
|
@@ -5210,7 +5340,7 @@ var createDestruction = (options = {}) => {
|
|
|
5210
5340
|
if (o.spec.name !== void 0) s.name = o.spec.name;
|
|
5211
5341
|
if (o.spec.tags) s.tags = [...o.spec.tags];
|
|
5212
5342
|
if (o.spec.soil !== void 0) s.soil = o.spec.soil;
|
|
5213
|
-
return
|
|
5343
|
+
return s;
|
|
5214
5344
|
};
|
|
5215
5345
|
const inNew = (pid) => moved.has(pid);
|
|
5216
5346
|
const inOld = (pid) => !moved.has(pid);
|
|
@@ -5237,7 +5367,12 @@ var createDestruction = (options = {}) => {
|
|
|
5237
5367
|
const p = getPart(o, partId);
|
|
5238
5368
|
return whole(() => doHit(o, h, p));
|
|
5239
5369
|
},
|
|
5370
|
+
blastProbe() {
|
|
5371
|
+
return { ...blastProbe };
|
|
5372
|
+
},
|
|
5240
5373
|
explode(point, energy, radius, source, blast) {
|
|
5374
|
+
blastProbe.partsTested = 0;
|
|
5375
|
+
blastProbe.partsNear = 0;
|
|
5241
5376
|
return withCollapses(run(() => blastWorld(point, energy, radius, source, blast)));
|
|
5242
5377
|
},
|
|
5243
5378
|
raycast(origin, direction, maxDistance = Infinity) {
|
|
@@ -5247,7 +5382,8 @@ var createDestruction = (options = {}) => {
|
|
|
5247
5382
|
sweepBox(box, direction, distance, options2 = {}) {
|
|
5248
5383
|
const d = vec.normalize(direction);
|
|
5249
5384
|
const ignored = options2.ignore === void 0 ? void 0 : objects.get(options2.ignore);
|
|
5250
|
-
const
|
|
5385
|
+
const hosted = options2.ignore === void 0 || ignored ? void 0 : impls.get(options2.ignore);
|
|
5386
|
+
const ignore = ignored ? new Set(ignored.parts.map((p) => `${ignored.id}\0${p.spec.id}`)) : hosted ? new Set(hosted.impl.describe().parts.map((p) => `${hosted.impl.id}\0${p.id}`)) : void 0;
|
|
5251
5387
|
let best = null;
|
|
5252
5388
|
for (const origin of sweepOrigins(box, d, options2.spacing ?? 0.2)) {
|
|
5253
5389
|
const r = raycastInternal(origin, d, distance + SWEEP_BACK, ignore);
|