@adatechnology/game-destruction 0.18.2 → 0.20.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.global.js +161 -14
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +142 -15
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.global.js
CHANGED
|
@@ -3068,6 +3068,26 @@ var GameDestruction = (() => {
|
|
|
3068
3068
|
|
|
3069
3069
|
// ../game-contracts/dist/index.js
|
|
3070
3070
|
var DEFAULT_BLAST_IMPULSE = { bodyArea: 0.7, lift: 0.15, minSpeed: 0.3, tntPerJoule: 0.24 / 2e5 };
|
|
3071
|
+
var NEAREST = 0.3;
|
|
3072
|
+
function sideOnImpulse(tnt, distance) {
|
|
3073
|
+
if (!(tnt > 0)) return 0;
|
|
3074
|
+
const w3 = Math.cbrt(tnt);
|
|
3075
|
+
const z = Math.max(distance, NEAREST) / w3;
|
|
3076
|
+
const scaled = 0.067 * Math.sqrt(1 + (z / 0.23) ** 4) / (z * z * Math.cbrt(1 + (z / 1.55) ** 3));
|
|
3077
|
+
return scaled * w3 * 100;
|
|
3078
|
+
}
|
|
3079
|
+
var ATMOSPHERE = 101325;
|
|
3080
|
+
function sideOnOverpressure(tnt, distance) {
|
|
3081
|
+
if (!(tnt > 0)) return 0;
|
|
3082
|
+
const z = Math.max(distance, NEAREST) / Math.cbrt(tnt);
|
|
3083
|
+
const ratio = 808 * (1 + (z / 4.5) ** 2) / (Math.sqrt(1 + (z / 0.048) ** 2) * Math.sqrt(1 + (z / 0.32) ** 2) * Math.sqrt(1 + (z / 1.35) ** 2));
|
|
3084
|
+
return ratio * ATMOSPHERE;
|
|
3085
|
+
}
|
|
3086
|
+
function bodyImpulse(tnt, distance) {
|
|
3087
|
+
if (!(tnt > 0)) return 0;
|
|
3088
|
+
const z = Math.max(distance, NEAREST) / Math.cbrt(tnt);
|
|
3089
|
+
return sideOnImpulse(tnt, distance) * (2 + 4 / (1 + z * z));
|
|
3090
|
+
}
|
|
3071
3091
|
var DEFAULT_COST_READS = 1e4;
|
|
3072
3092
|
var DEFAULT_COST_CEILING_MS = 50;
|
|
3073
3093
|
var DEFAULT_COST_ALLOCATION_BYTES = 1024 * 1024;
|
|
@@ -3389,9 +3409,15 @@ var GameDestruction = (() => {
|
|
|
3389
3409
|
var BREACH_AREA = 0.25;
|
|
3390
3410
|
var BREACH_BLAST_REACH = 0.12;
|
|
3391
3411
|
var MEMBER_CUT_REACH = 0.5;
|
|
3412
|
+
var FAR_MEMBER_HP_FLOOR = 0.25;
|
|
3392
3413
|
var BLAST_REFERENCE_RESISTANCE = 200 * 1900 * 0.13;
|
|
3393
3414
|
var BLAST_EDGE_RAGGED = 0.3;
|
|
3394
3415
|
var BLAST_EDGE_LOOSE = 1.35;
|
|
3416
|
+
var PANEL_PRESSURE = 7e4;
|
|
3417
|
+
var PANEL_IMPULSE = 150;
|
|
3418
|
+
var PANEL_PI = 6e6;
|
|
3419
|
+
var PANEL_POINTS = [[0, 0], [-0.3, 0], [0.3, 0], [0, -0.3], [0, 0.3]];
|
|
3420
|
+
var PANEL_POINTS_LOADED = 4;
|
|
3395
3421
|
var BLAST_EDGE_WEAR = 0.6;
|
|
3396
3422
|
var RESIDUAL_CAPACITY = 0.15;
|
|
3397
3423
|
var SEVER_BAND = 0.1;
|
|
@@ -3416,23 +3442,42 @@ var GameDestruction = (() => {
|
|
|
3416
3442
|
const v = table[mat.penetrationClass ?? mat.id] ?? table[mat.id];
|
|
3417
3443
|
return v === void 0 ? void 0 : v * (mat.penetrationScale ?? 1);
|
|
3418
3444
|
};
|
|
3419
|
-
var
|
|
3445
|
+
var BURIED_BLAST_REACH = 0.5;
|
|
3446
|
+
var GIVE_AT_CUT_HP = 0.5;
|
|
3447
|
+
var capacityScale = (p, borne, through) => {
|
|
3420
3448
|
const residual = p.spec.reinforcement?.residualCapacity ?? RESIDUAL_CAPACITY;
|
|
3421
3449
|
if (p.st.stripped) return residual;
|
|
3422
|
-
const kept = Math.min(p.rt.removed ? 1 - p.rt.removed / p.mass0 : 1,
|
|
3450
|
+
const kept = Math.min(p.rt.removed ? 1 - p.rt.removed / p.mass0 : 1, sectionKept(p, residual, borne, through));
|
|
3423
3451
|
return Math.max(p.spec.reinforcement ? residual : 0.05, kept);
|
|
3424
3452
|
};
|
|
3425
|
-
var
|
|
3453
|
+
var sectionKept = (p, residual, borne, through) => {
|
|
3454
|
+
let kept = 1;
|
|
3455
|
+
forEachSection(p, borne, (c, along, gone) => {
|
|
3456
|
+
const share = through?.(c.center, along) ?? 1;
|
|
3457
|
+
if (share <= 0) return;
|
|
3458
|
+
kept = Math.min(kept, Math.max(p.spec.reinforcement ? residual : 0, 1 - gone) / share);
|
|
3459
|
+
});
|
|
3460
|
+
return kept;
|
|
3461
|
+
};
|
|
3462
|
+
var sectionLoss = (p, borne) => {
|
|
3463
|
+
let worst = 0;
|
|
3464
|
+
forEachSection(p, borne, (_c, _along, gone) => {
|
|
3465
|
+
worst = Math.max(worst, gone);
|
|
3466
|
+
});
|
|
3467
|
+
return Math.min(1, worst);
|
|
3468
|
+
};
|
|
3469
|
+
var forEachSection = (p, borne, visit) => {
|
|
3426
3470
|
const cells = p.st.chipped;
|
|
3427
|
-
if (!cells?.length) return
|
|
3471
|
+
if (!cells?.length) return;
|
|
3428
3472
|
const [u, v] = planeAxes(p.thin);
|
|
3429
3473
|
const role = p.spec.structure?.role;
|
|
3430
3474
|
const up = rotateVec(p.q, [0, 0, 0].map((_, k) => k === v ? 1 : 0));
|
|
3431
3475
|
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;
|
|
3432
3476
|
const along = (role === "wall" || role === "partition" || role === "column") && upright >= 0 ? upright : p.dims[u] >= p.dims[v] ? u : v;
|
|
3433
3477
|
const across = along === u ? v : u;
|
|
3434
|
-
|
|
3478
|
+
const lying = along !== upright || upright < 0;
|
|
3435
3479
|
for (const c of cells) {
|
|
3480
|
+
if (lying && borne?.(c.center, along)) continue;
|
|
3436
3481
|
const spans = [];
|
|
3437
3482
|
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]);
|
|
3438
3483
|
spans.sort((a, b) => a[0] - b[0]);
|
|
@@ -3442,9 +3487,8 @@ var GameDestruction = (() => {
|
|
|
3442
3487
|
gone += b - Math.max(a, end);
|
|
3443
3488
|
end = b;
|
|
3444
3489
|
}
|
|
3445
|
-
|
|
3490
|
+
visit(c, along, Math.min(1, gone / Math.max(p.dims[across], 1e-6)));
|
|
3446
3491
|
}
|
|
3447
|
-
return Math.min(1, worst);
|
|
3448
3492
|
};
|
|
3449
3493
|
var isLost = (p) => p.st.status === "broken" || p.st.status === "melted" || p.st.status === "ashes";
|
|
3450
3494
|
var clone = (v) => structuredClone(v);
|
|
@@ -3755,6 +3799,26 @@ var GameDestruction = (() => {
|
|
|
3755
3799
|
byTouching.set(support, { version: poseVersion, box });
|
|
3756
3800
|
return box;
|
|
3757
3801
|
};
|
|
3802
|
+
const throughOf = (p) => {
|
|
3803
|
+
const load = p.st.load ?? 0;
|
|
3804
|
+
if (load <= 0) return void 0;
|
|
3805
|
+
const above = Math.max(0, load - p.st.mass);
|
|
3806
|
+
const up = rotateVec(p.q, [0, 1, 0]);
|
|
3807
|
+
return (local, along) => {
|
|
3808
|
+
const axis = [0, 0, 0];
|
|
3809
|
+
axis[along] = 1;
|
|
3810
|
+
const sign = Math.sign(rotateVec(p.q, axis)[1]);
|
|
3811
|
+
if (Math.abs(up[1]) < 0.7 && Math.abs(rotateVec(p.q, axis)[1]) < 0.7) return 1;
|
|
3812
|
+
const half = p.dims[along] / 2;
|
|
3813
|
+
const share = clamp2((half - sign * local[along]) / Math.max(p.dims[along], 1e-6), 0, 1);
|
|
3814
|
+
return (above + p.st.mass * share) / load;
|
|
3815
|
+
};
|
|
3816
|
+
};
|
|
3817
|
+
const borneOn = (p, supports) => {
|
|
3818
|
+
if (supports.length === 0) return void 0;
|
|
3819
|
+
const boxes = supports.map((support) => contactBox(p, support));
|
|
3820
|
+
return (local, along) => boxes.some(([lo, hi]) => local[along] >= lo[along] - SUPPORT_TOLERANCE && local[along] <= hi[along] + SUPPORT_TOLERANCE);
|
|
3821
|
+
};
|
|
3758
3822
|
const measureContact = (p, support) => {
|
|
3759
3823
|
const A = aabbOf(p.spec.shape, p.pos, p.q), B = aabbOf(support.spec.shape, support.pos, support.q);
|
|
3760
3824
|
const lo = [0, 0, 0], hi = [0, 0, 0];
|
|
@@ -3962,7 +4026,7 @@ var GameDestruction = (() => {
|
|
|
3962
4026
|
o.unsup[i] = o.anyAnchor && !isLost(p) && !p.orphan;
|
|
3963
4027
|
continue;
|
|
3964
4028
|
}
|
|
3965
|
-
p.st.stress = p.st.load * structureSafety / (p.cap * capacityScale(p));
|
|
4029
|
+
p.st.stress = p.st.load * structureSafety / (p.cap * capacityScale(p, borneOn(p, (lowerOf.get(i) ?? []).map((k) => parts[k])), throughOf(p)));
|
|
3966
4030
|
if (p.bend) {
|
|
3967
4031
|
p.bendLoad = p.st.mass + bending[i];
|
|
3968
4032
|
const bent = restFlexure(o, p, lowerOf.get(i) ?? []);
|
|
@@ -4947,7 +5011,7 @@ var GameDestruction = (() => {
|
|
|
4947
5011
|
return BREACH_BLAST_REACH * Math.cbrt(Math.max(0, energy) / 1e3) * clamp2(Math.cbrt(BLAST_REFERENCE_RESISTANCE / resistance), 0.3, 2);
|
|
4948
5012
|
};
|
|
4949
5013
|
const blastUnits = (o, p, wp, energy, dist, src) => {
|
|
4950
|
-
var _a
|
|
5014
|
+
var _a;
|
|
4951
5015
|
if (!p.staged || p.st.stripped || isLost(p) || chipEnergy <= 0) return false;
|
|
4952
5016
|
const reach = blastReachOf(p, energy);
|
|
4953
5017
|
if (dist >= reach) return false;
|
|
@@ -4977,6 +5041,62 @@ var GameDestruction = (() => {
|
|
|
4977
5041
|
}
|
|
4978
5042
|
}
|
|
4979
5043
|
if (out.length === 0) return false;
|
|
5044
|
+
return knockOutUnits(o, p, wp, energy, out, src);
|
|
5045
|
+
};
|
|
5046
|
+
const blastPanel = (o, p, point, energy, src) => {
|
|
5047
|
+
var _a;
|
|
5048
|
+
if (!p.staged || p.st.stripped || isLost(p) || !p.spec.units || p.spec.units.mortar === p.spec.material || p.spec.reinforcement) return false;
|
|
5049
|
+
const [u, v] = planeAxes(p.thin);
|
|
5050
|
+
const lp = toLocal(p.pos, p.q, objLocal(o, point));
|
|
5051
|
+
const half = p.dims[u] / 2;
|
|
5052
|
+
const posts = [];
|
|
5053
|
+
for (const m of o.parts) {
|
|
5054
|
+
if (m === p || !m.spec.reinforcement || isLost(m) || m.spec.shape.kind !== "box") continue;
|
|
5055
|
+
const mp = toLocal(p.pos, p.q, m.pos);
|
|
5056
|
+
if (Math.abs(mp[p.thin]) > p.dims[p.thin] + 0.15 || Math.abs(mp[u]) > half + 0.15 || m.dims[v] < p.dims[v] * 0.5) continue;
|
|
5057
|
+
posts.push(mp[u]);
|
|
5058
|
+
}
|
|
5059
|
+
let from = -half, to = half;
|
|
5060
|
+
for (const x of posts) {
|
|
5061
|
+
if (x <= lp[u] && x > from) from = x;
|
|
5062
|
+
if (x >= lp[u] && x < to) to = x;
|
|
5063
|
+
}
|
|
5064
|
+
if (to - from < 0.2) return false;
|
|
5065
|
+
const tnt = energy * DEFAULT_BLAST_IMPULSE.tntPerJoule;
|
|
5066
|
+
const centre = objLocal(o, point);
|
|
5067
|
+
const span = to - from, height = p.dims[v];
|
|
5068
|
+
let loaded = 0;
|
|
5069
|
+
for (const [fu, fv] of PANEL_POINTS) {
|
|
5070
|
+
const at = [0, 0, 0];
|
|
5071
|
+
at[u] = (from + to) / 2 + fu * span;
|
|
5072
|
+
at[v] = fv * height;
|
|
5073
|
+
at[p.thin] = Math.sign(lp[p.thin]) * p.dims[p.thin] / 2;
|
|
5074
|
+
const r = vec.distance(toWorld(p.pos, p.q, at), centre);
|
|
5075
|
+
const side = sideOnOverpressure(tnt, r);
|
|
5076
|
+
const reflected = 2 * side + 6 * side * side / (side + 7 * 101325);
|
|
5077
|
+
const impulse = bodyImpulse(tnt, r);
|
|
5078
|
+
if (reflected > PANEL_PRESSURE && impulse > PANEL_IMPULSE && (reflected - PANEL_PRESSURE) * (impulse - PANEL_IMPULSE) >= PANEL_PI) loaded++;
|
|
5079
|
+
}
|
|
5080
|
+
if (loaded < PANEL_POINTS_LOADED) return false;
|
|
5081
|
+
const grid = gridOf(p);
|
|
5082
|
+
const worn = (_a = p.rt).chips ?? (_a.chips = {});
|
|
5083
|
+
const out = [];
|
|
5084
|
+
for (let i = 0; i < grid.length; i++) {
|
|
5085
|
+
const cell = grid[i];
|
|
5086
|
+
if (worn[i] === -1) continue;
|
|
5087
|
+
if (cell.center[u] - cell.size[u] / 2 >= from - 1e-6 && cell.center[u] + cell.size[u] / 2 <= to + 1e-6) out.push(i);
|
|
5088
|
+
}
|
|
5089
|
+
if (out.length === 0) return false;
|
|
5090
|
+
for (const i of out) worn[i] = -1;
|
|
5091
|
+
return knockOutUnits(o, p, point, energy, out, src);
|
|
5092
|
+
};
|
|
5093
|
+
const knockOutUnits = (o, p, wp, energy, out, src) => {
|
|
5094
|
+
var _a, _b;
|
|
5095
|
+
const [u, v] = planeAxes(p.thin);
|
|
5096
|
+
const op = p.st.opening ? toLocal(p.pos, p.q, p.st.opening.center) : null;
|
|
5097
|
+
const grid = gridOf(p);
|
|
5098
|
+
const worn = (_a = p.rt).chips ?? (_a.chips = {});
|
|
5099
|
+
for (const i of out) worn[i] = -1;
|
|
4980
5100
|
const loose = unsupportedCells(grid, p.thin, p.dims, (i) => worn[i] !== -1 && !(op && p.st.opening && Math.hypot(grid[i].center[u] - op[u], grid[i].center[v] - op[v]) <= p.st.opening.radius));
|
|
4981
5101
|
for (const i of loose) worn[i] = -1;
|
|
4982
5102
|
const blown = out.map((i) => grid[i]), fallen = loose.map((i) => grid[i]);
|
|
@@ -5052,6 +5172,27 @@ var GameDestruction = (() => {
|
|
|
5052
5172
|
debris(o, p, p.mat.breakDebris, center, [0, -1, 0], 6, 1, src);
|
|
5053
5173
|
return loose.length;
|
|
5054
5174
|
};
|
|
5175
|
+
const giveAtCut = (o, p, src) => {
|
|
5176
|
+
if (!p.spec.reinforcement || !p.spec.units || !p.st.chipped?.length || sectionLoss(p) <= 0) return false;
|
|
5177
|
+
const opened = [];
|
|
5178
|
+
for (const j of o.byPart.get(p.spec.id) ?? []) {
|
|
5179
|
+
if (j.st.open) continue;
|
|
5180
|
+
const onIt = j.bears === "a_on_b" && j.spec.b === p.spec.id || j.bears === "b_on_a" && j.spec.a === p.spec.id;
|
|
5181
|
+
if (!onIt) continue;
|
|
5182
|
+
j.st.open = true;
|
|
5183
|
+
j.st.openedBy = "break";
|
|
5184
|
+
opened.push(j);
|
|
5185
|
+
}
|
|
5186
|
+
if (opened.length === 0) return false;
|
|
5187
|
+
p.st.hp = Math.max(p.st.hp, p.st.maxHp * GIVE_AT_CUT_HP);
|
|
5188
|
+
markDamaged(p);
|
|
5189
|
+
solveStructure(o);
|
|
5190
|
+
recomputeGroups(o);
|
|
5191
|
+
detachEvents(o, opened, "break", src);
|
|
5192
|
+
exposeRebar(o, p, src);
|
|
5193
|
+
collapseCheck(o, src);
|
|
5194
|
+
return true;
|
|
5195
|
+
};
|
|
5055
5196
|
const stripPart = (o, p, reinforcement, src) => {
|
|
5056
5197
|
const bars = rebarBars(p.dims, p.thin, reinforcement.spacing);
|
|
5057
5198
|
p.st.stripped = true;
|
|
@@ -5109,6 +5250,7 @@ var GameDestruction = (() => {
|
|
|
5109
5250
|
hingeDrop(o, p, src);
|
|
5110
5251
|
return;
|
|
5111
5252
|
}
|
|
5253
|
+
if (!force && giveAtCut(o, p, src)) return;
|
|
5112
5254
|
const ex = p.spec.explodes;
|
|
5113
5255
|
const style = force ?? (p.snap ? "snap" : p.mat.fracture === "none" ? "snap" : p.mat.fracture);
|
|
5114
5256
|
const { c, q } = partXf(o, p);
|
|
@@ -5437,12 +5579,15 @@ var GameDestruction = (() => {
|
|
|
5437
5579
|
return reach;
|
|
5438
5580
|
}
|
|
5439
5581
|
if (Ein > 20 && p.layers.length > 0) spallOuter(o, p, wp, faceOf(o, p, wn), wn, clamp2(0.05 + Math.sqrt(Ein) * 2e-3, 0.05, 0.4), src);
|
|
5440
|
-
const
|
|
5582
|
+
const breachReach = BREACH_BLAST_REACH * Math.cbrt(Math.max(0, energy) / 1e3);
|
|
5583
|
+
const farMember = Boolean(p.spec.reinforcement && p.spec.units) && dist > MEMBER_CUT_REACH * breachReach;
|
|
5584
|
+
const dealt = farMember ? applyDamage(o, p, Math.min(amount, Math.max(0, p.st.hp - p.st.maxHp * FAR_MEMBER_HP_FLOOR)), "explosive", null, null, Ein * 0.3, 40, src) : applyDamage(o, p, amount, "explosive", point, null, Ein * 0.3, 40, src);
|
|
5441
5585
|
res.damage += dealt;
|
|
5442
5586
|
if (p.bend && flexure.enabled) bendByBlast(o, p, point, energy, radius, reach, src);
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5587
|
+
if (p.spec.units && p.spec.units.mortar !== p.spec.material) {
|
|
5588
|
+
blastPanel(o, p, point, energy, src);
|
|
5589
|
+
if (!isLost(p)) blastUnits(o, p, wp, energy, dist, src);
|
|
5590
|
+
} else if (farMember) blastUnits(o, p, wp, energy, dist, src);
|
|
5446
5591
|
else if (dist <= breachReach) addBreach(o, p, wp, breachAreaOf(p, dealt, true), Ein, null, src);
|
|
5447
5592
|
return reach;
|
|
5448
5593
|
};
|
|
@@ -5455,7 +5600,9 @@ var GameDestruction = (() => {
|
|
|
5455
5600
|
const dx = local[0] - p.pos[0];
|
|
5456
5601
|
const dy = local[1] - p.pos[1];
|
|
5457
5602
|
const dz = local[2] - p.pos[2];
|
|
5458
|
-
if (dx * dx + dy * dy + dz * dz
|
|
5603
|
+
if (dx * dx + dy * dy + dz * dz >= reach * reach) continue;
|
|
5604
|
+
if (p.spec.anchored && p.spec.structure?.role === "foundation" && distToPart(o, p, point) > BURIED_BLAST_REACH) continue;
|
|
5605
|
+
near.push(p);
|
|
5459
5606
|
}
|
|
5460
5607
|
blastProbe.partsTested += o.parts.length;
|
|
5461
5608
|
blastProbe.partsNear += near.length;
|