@adatechnology/game-floorplan-structure 2.1.3 → 2.1.5

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.
@@ -2541,6 +2541,26 @@ var GameFloorplanStructure = (() => {
2541
2541
 
2542
2542
  // ../game-contracts/dist/index.js
2543
2543
  var DEFAULT_BLAST_IMPULSE = { bodyArea: 0.7, lift: 0.15, minSpeed: 0.3, tntPerJoule: 0.24 / 2e5 };
2544
+ var NEAREST = 0.3;
2545
+ function sideOnImpulse(tnt, distance2) {
2546
+ if (!(tnt > 0)) return 0;
2547
+ const w3 = Math.cbrt(tnt);
2548
+ const z = Math.max(distance2, NEAREST) / w3;
2549
+ const scaled = 0.067 * Math.sqrt(1 + (z / 0.23) ** 4) / (z * z * Math.cbrt(1 + (z / 1.55) ** 3));
2550
+ return scaled * w3 * 100;
2551
+ }
2552
+ var ATMOSPHERE = 101325;
2553
+ function sideOnOverpressure(tnt, distance2) {
2554
+ if (!(tnt > 0)) return 0;
2555
+ const z = Math.max(distance2, NEAREST) / Math.cbrt(tnt);
2556
+ 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));
2557
+ return ratio * ATMOSPHERE;
2558
+ }
2559
+ function bodyImpulse(tnt, distance2) {
2560
+ if (!(tnt > 0)) return 0;
2561
+ const z = Math.max(distance2, NEAREST) / Math.cbrt(tnt);
2562
+ return sideOnImpulse(tnt, distance2) * (2 + 4 / (1 + z * z));
2563
+ }
2544
2564
  var DEFAULT_COST_ALLOCATION_BYTES = 1024 * 1024;
2545
2565
  var DEFAULT_IGNITION_RULES = {
2546
2566
  fillCurve: [
@@ -5545,9 +5565,15 @@ var GameFloorplanStructure = (() => {
5545
5565
  var BREACH_AREA = 0.25;
5546
5566
  var BREACH_BLAST_REACH = 0.12;
5547
5567
  var MEMBER_CUT_REACH = 0.5;
5568
+ var FAR_MEMBER_HP_FLOOR = 0.25;
5548
5569
  var BLAST_REFERENCE_RESISTANCE = 200 * 1900 * 0.13;
5549
5570
  var BLAST_EDGE_RAGGED = 0.3;
5550
5571
  var BLAST_EDGE_LOOSE = 1.35;
5572
+ var PANEL_PRESSURE = 7e4;
5573
+ var PANEL_IMPULSE = 150;
5574
+ var PANEL_PI = 6e6;
5575
+ var PANEL_POINTS = [[0, 0], [-0.3, 0], [0.3, 0], [0, -0.3], [0, 0.3]];
5576
+ var PANEL_POINTS_LOADED = 4;
5551
5577
  var BLAST_EDGE_WEAR = 0.6;
5552
5578
  var RESIDUAL_CAPACITY = 0.15;
5553
5579
  var SEVER_BAND = 0.1;
@@ -5572,23 +5598,42 @@ var GameFloorplanStructure = (() => {
5572
5598
  const v = table[mat.penetrationClass ?? mat.id] ?? table[mat.id];
5573
5599
  return v === void 0 ? void 0 : v * (mat.penetrationScale ?? 1);
5574
5600
  };
5575
- var capacityScale = (p) => {
5601
+ var BURIED_BLAST_REACH = 0.5;
5602
+ var GIVE_AT_CUT_HP = 0.5;
5603
+ var capacityScale = (p, borne, through) => {
5576
5604
  const residual = p.spec.reinforcement?.residualCapacity ?? RESIDUAL_CAPACITY;
5577
5605
  if (p.st.stripped) return residual;
5578
- const kept = Math.min(p.rt.removed ? 1 - p.rt.removed / p.mass0 : 1, 1 - sectionLoss(p));
5606
+ const kept = Math.min(p.rt.removed ? 1 - p.rt.removed / p.mass0 : 1, sectionKept(p, residual, borne, through));
5579
5607
  return Math.max(p.spec.reinforcement ? residual : 0.05, kept);
5580
5608
  };
5581
- var sectionLoss = (p) => {
5609
+ var sectionKept = (p, residual, borne, through) => {
5610
+ let kept = 1;
5611
+ forEachSection(p, borne, (c, along, gone) => {
5612
+ const share = through?.(c.center, along) ?? 1;
5613
+ if (share <= 0) return;
5614
+ kept = Math.min(kept, Math.max(p.spec.reinforcement ? residual : 0, 1 - gone) / share);
5615
+ });
5616
+ return kept;
5617
+ };
5618
+ var sectionLoss = (p, borne) => {
5619
+ let worst = 0;
5620
+ forEachSection(p, borne, (_c, _along, gone) => {
5621
+ worst = Math.max(worst, gone);
5622
+ });
5623
+ return Math.min(1, worst);
5624
+ };
5625
+ var forEachSection = (p, borne, visit) => {
5582
5626
  const cells = p.st.chipped;
5583
- if (!cells?.length) return 0;
5627
+ if (!cells?.length) return;
5584
5628
  const [u, v] = planeAxes(p.thin);
5585
5629
  const role = p.spec.structure?.role;
5586
5630
  const up = rotateVec(p.q, [0, 0, 0].map((_, k) => k === v ? 1 : 0));
5587
5631
  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;
5588
5632
  const along = (role === "wall" || role === "partition" || role === "column") && upright >= 0 ? upright : p.dims[u] >= p.dims[v] ? u : v;
5589
5633
  const across = along === u ? v : u;
5590
- let worst = 0;
5634
+ const lying = along !== upright || upright < 0;
5591
5635
  for (const c of cells) {
5636
+ if (lying && borne?.(c.center, along)) continue;
5592
5637
  const spans = [];
5593
5638
  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]);
5594
5639
  spans.sort((a, b) => a[0] - b[0]);
@@ -5598,9 +5643,8 @@ var GameFloorplanStructure = (() => {
5598
5643
  gone += b - Math.max(a, end);
5599
5644
  end = b;
5600
5645
  }
5601
- worst = Math.max(worst, gone / Math.max(p.dims[across], 1e-6));
5646
+ visit(c, along, Math.min(1, gone / Math.max(p.dims[across], 1e-6)));
5602
5647
  }
5603
- return Math.min(1, worst);
5604
5648
  };
5605
5649
  var isLost = (p) => p.st.status === "broken" || p.st.status === "melted" || p.st.status === "ashes";
5606
5650
  var clone = (v) => structuredClone(v);
@@ -5911,6 +5955,26 @@ var GameFloorplanStructure = (() => {
5911
5955
  byTouching.set(support, { version: poseVersion, box: box2 });
5912
5956
  return box2;
5913
5957
  };
5958
+ const throughOf = (p) => {
5959
+ const load = p.st.load ?? 0;
5960
+ if (load <= 0) return void 0;
5961
+ const above = Math.max(0, load - p.st.mass);
5962
+ const up = rotateVec(p.q, [0, 1, 0]);
5963
+ return (local, along) => {
5964
+ const axis = [0, 0, 0];
5965
+ axis[along] = 1;
5966
+ const sign = Math.sign(rotateVec(p.q, axis)[1]);
5967
+ if (Math.abs(up[1]) < 0.7 && Math.abs(rotateVec(p.q, axis)[1]) < 0.7) return 1;
5968
+ const half = p.dims[along] / 2;
5969
+ const share = clamp22((half - sign * local[along]) / Math.max(p.dims[along], 1e-6), 0, 1);
5970
+ return (above + p.st.mass * share) / load;
5971
+ };
5972
+ };
5973
+ const borneOn = (p, supports) => {
5974
+ if (supports.length === 0) return void 0;
5975
+ const boxes = supports.map((support) => contactBox(p, support));
5976
+ return (local, along) => boxes.some(([lo, hi]) => local[along] >= lo[along] - SUPPORT_TOLERANCE && local[along] <= hi[along] + SUPPORT_TOLERANCE);
5977
+ };
5914
5978
  const measureContact = (p, support) => {
5915
5979
  const A = aabbOf(p.spec.shape, p.pos, p.q), B = aabbOf(support.spec.shape, support.pos, support.q);
5916
5980
  const lo = [0, 0, 0], hi = [0, 0, 0];
@@ -6118,7 +6182,7 @@ var GameFloorplanStructure = (() => {
6118
6182
  o.unsup[i] = o.anyAnchor && !isLost(p) && !p.orphan;
6119
6183
  continue;
6120
6184
  }
6121
- p.st.stress = p.st.load * structureSafety / (p.cap * capacityScale(p));
6185
+ p.st.stress = p.st.load * structureSafety / (p.cap * capacityScale(p, borneOn(p, (lowerOf.get(i) ?? []).map((k) => parts[k])), throughOf(p)));
6122
6186
  if (p.bend) {
6123
6187
  p.bendLoad = p.st.mass + bending[i];
6124
6188
  const bent = restFlexure(o, p, lowerOf.get(i) ?? []);
@@ -7103,7 +7167,7 @@ var GameFloorplanStructure = (() => {
7103
7167
  return BREACH_BLAST_REACH * Math.cbrt(Math.max(0, energy) / 1e3) * clamp22(Math.cbrt(BLAST_REFERENCE_RESISTANCE / resistance), 0.3, 2);
7104
7168
  };
7105
7169
  const blastUnits = (o, p, wp, energy, dist, src) => {
7106
- var _a, _b;
7170
+ var _a;
7107
7171
  if (!p.staged || p.st.stripped || isLost(p) || chipEnergy <= 0) return false;
7108
7172
  const reach = blastReachOf(p, energy);
7109
7173
  if (dist >= reach) return false;
@@ -7133,6 +7197,62 @@ var GameFloorplanStructure = (() => {
7133
7197
  }
7134
7198
  }
7135
7199
  if (out.length === 0) return false;
7200
+ return knockOutUnits(o, p, wp, energy, out, src);
7201
+ };
7202
+ const blastPanel = (o, p, point, energy, src) => {
7203
+ var _a;
7204
+ if (!p.staged || p.st.stripped || isLost(p) || !p.spec.units || p.spec.units.mortar === p.spec.material || p.spec.reinforcement) return false;
7205
+ const [u, v] = planeAxes(p.thin);
7206
+ const lp = toLocal(p.pos, p.q, objLocal(o, point));
7207
+ const half = p.dims[u] / 2;
7208
+ const posts = [];
7209
+ for (const m of o.parts) {
7210
+ if (m === p || !m.spec.reinforcement || isLost(m) || m.spec.shape.kind !== "box") continue;
7211
+ const mp = toLocal(p.pos, p.q, m.pos);
7212
+ 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;
7213
+ posts.push(mp[u]);
7214
+ }
7215
+ let from = -half, to = half;
7216
+ for (const x of posts) {
7217
+ if (x <= lp[u] && x > from) from = x;
7218
+ if (x >= lp[u] && x < to) to = x;
7219
+ }
7220
+ if (to - from < 0.2) return false;
7221
+ const tnt = energy * DEFAULT_BLAST_IMPULSE.tntPerJoule;
7222
+ const centre2 = objLocal(o, point);
7223
+ const span = to - from, height = p.dims[v];
7224
+ let loaded = 0;
7225
+ for (const [fu, fv] of PANEL_POINTS) {
7226
+ const at2 = [0, 0, 0];
7227
+ at2[u] = (from + to) / 2 + fu * span;
7228
+ at2[v] = fv * height;
7229
+ at2[p.thin] = Math.sign(lp[p.thin]) * p.dims[p.thin] / 2;
7230
+ const r = vec.distance(toWorld(p.pos, p.q, at2), centre2);
7231
+ const side = sideOnOverpressure(tnt, r);
7232
+ const reflected = 2 * side + 6 * side * side / (side + 7 * 101325);
7233
+ const impulse = bodyImpulse(tnt, r);
7234
+ if (reflected > PANEL_PRESSURE && impulse > PANEL_IMPULSE && (reflected - PANEL_PRESSURE) * (impulse - PANEL_IMPULSE) >= PANEL_PI) loaded++;
7235
+ }
7236
+ if (loaded < PANEL_POINTS_LOADED) return false;
7237
+ const grid = gridOf(p);
7238
+ const worn = (_a = p.rt).chips ?? (_a.chips = {});
7239
+ const out = [];
7240
+ for (let i = 0; i < grid.length; i++) {
7241
+ const cell = grid[i];
7242
+ if (worn[i] === -1) continue;
7243
+ if (cell.center[u] - cell.size[u] / 2 >= from - 1e-6 && cell.center[u] + cell.size[u] / 2 <= to + 1e-6) out.push(i);
7244
+ }
7245
+ if (out.length === 0) return false;
7246
+ for (const i of out) worn[i] = -1;
7247
+ return knockOutUnits(o, p, point, energy, out, src);
7248
+ };
7249
+ const knockOutUnits = (o, p, wp, energy, out, src) => {
7250
+ var _a, _b;
7251
+ const [u, v] = planeAxes(p.thin);
7252
+ const op = p.st.opening ? toLocal(p.pos, p.q, p.st.opening.center) : null;
7253
+ const grid = gridOf(p);
7254
+ const worn = (_a = p.rt).chips ?? (_a.chips = {});
7255
+ for (const i of out) worn[i] = -1;
7136
7256
  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));
7137
7257
  for (const i of loose) worn[i] = -1;
7138
7258
  const blown = out.map((i) => grid[i]), fallen = loose.map((i) => grid[i]);
@@ -7208,6 +7328,27 @@ var GameFloorplanStructure = (() => {
7208
7328
  debris(o, p, p.mat.breakDebris, center, [0, -1, 0], 6, 1, src);
7209
7329
  return loose.length;
7210
7330
  };
7331
+ const giveAtCut = (o, p, src) => {
7332
+ if (!p.spec.reinforcement || !p.spec.units || !p.st.chipped?.length || sectionLoss(p) <= 0) return false;
7333
+ const opened = [];
7334
+ for (const j of o.byPart.get(p.spec.id) ?? []) {
7335
+ if (j.st.open) continue;
7336
+ const onIt = j.bears === "a_on_b" && j.spec.b === p.spec.id || j.bears === "b_on_a" && j.spec.a === p.spec.id;
7337
+ if (!onIt) continue;
7338
+ j.st.open = true;
7339
+ j.st.openedBy = "break";
7340
+ opened.push(j);
7341
+ }
7342
+ if (opened.length === 0) return false;
7343
+ p.st.hp = Math.max(p.st.hp, p.st.maxHp * GIVE_AT_CUT_HP);
7344
+ markDamaged(p);
7345
+ solveStructure(o);
7346
+ recomputeGroups(o);
7347
+ detachEvents(o, opened, "break", src);
7348
+ exposeRebar(o, p, src);
7349
+ collapseCheck(o, src);
7350
+ return true;
7351
+ };
7211
7352
  const stripPart = (o, p, reinforcement, src) => {
7212
7353
  const bars = rebarBars(p.dims, p.thin, reinforcement.spacing);
7213
7354
  p.st.stripped = true;
@@ -7265,6 +7406,7 @@ var GameFloorplanStructure = (() => {
7265
7406
  hingeDrop(o, p, src);
7266
7407
  return;
7267
7408
  }
7409
+ if (!force && giveAtCut(o, p, src)) return;
7268
7410
  const ex = p.spec.explodes;
7269
7411
  const style = force ?? (p.snap ? "snap" : p.mat.fracture === "none" ? "snap" : p.mat.fracture);
7270
7412
  const { c, q } = partXf(o, p);
@@ -7593,12 +7735,15 @@ var GameFloorplanStructure = (() => {
7593
7735
  return reach;
7594
7736
  }
7595
7737
  if (Ein > 20 && p.layers.length > 0) spallOuter(o, p, wp, faceOf(o, p, wn), wn, clamp22(0.05 + Math.sqrt(Ein) * 2e-3, 0.05, 0.4), src);
7596
- const dealt = applyDamage(o, p, amount, "explosive", point, null, Ein * 0.3, 40, src);
7738
+ const breachReach = BREACH_BLAST_REACH * Math.cbrt(Math.max(0, energy) / 1e3);
7739
+ const farMember = Boolean(p.spec.reinforcement && p.spec.units) && dist > MEMBER_CUT_REACH * breachReach;
7740
+ 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);
7597
7741
  res.damage += dealt;
7598
7742
  if (p.bend && flexure.enabled) bendByBlast(o, p, point, energy, radius, reach, src);
7599
- const breachReach = BREACH_BLAST_REACH * Math.cbrt(Math.max(0, energy) / 1e3);
7600
- if (p.spec.units && p.spec.units.mortar !== p.spec.material) blastUnits(o, p, wp, energy, dist, src);
7601
- else if (p.spec.reinforcement && p.spec.units && dist > MEMBER_CUT_REACH * breachReach) blastUnits(o, p, wp, energy, dist, src);
7743
+ if (p.spec.units && p.spec.units.mortar !== p.spec.material) {
7744
+ blastPanel(o, p, point, energy, src);
7745
+ if (!isLost(p)) blastUnits(o, p, wp, energy, dist, src);
7746
+ } else if (farMember) blastUnits(o, p, wp, energy, dist, src);
7602
7747
  else if (dist <= breachReach) addBreach(o, p, wp, breachAreaOf(p, dealt, true), Ein, null, src);
7603
7748
  return reach;
7604
7749
  };
@@ -7611,7 +7756,9 @@ var GameFloorplanStructure = (() => {
7611
7756
  const dx = local[0] - p.pos[0];
7612
7757
  const dy = local[1] - p.pos[1];
7613
7758
  const dz = local[2] - p.pos[2];
7614
- if (dx * dx + dy * dy + dz * dz < reach * reach) near.push(p);
7759
+ if (dx * dx + dy * dy + dz * dz >= reach * reach) continue;
7760
+ if (p.spec.anchored && p.spec.structure?.role === "foundation" && distToPart(o, p, point) > BURIED_BLAST_REACH) continue;
7761
+ near.push(p);
7615
7762
  }
7616
7763
  blastProbe.partsTested += o.parts.length;
7617
7764
  blastProbe.partsNear += near.length;