@adatechnology/game-floorplan-three 0.9.3 → 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.
@@ -778,6 +778,7 @@ function installHouseHelpers(K, shared) {
778
778
  // src/house/walls.ts
779
779
  function installHouseWalls(K, shared) {
780
780
  const { EPS, Y0, boxC, bx, cursor, effectiveHeight, pieceRecord, structurePiece, wallColor } = K;
781
+ const sh = (color, factor) => K.sh(color, factor);
781
782
  function wallBox(alongU, L, a, b, ya, yb, th, cL, cH, mL, mH) {
782
783
  if (yb - ya < 1e-4 || b - a < 1e-4) return;
783
784
  if (alongU) {
@@ -821,6 +822,7 @@ function installHouseWalls(K, shared) {
821
822
  function walls() {
822
823
  const R = shared.ctx.r, cut = shared.ctx.cut, nA = R.floors.length, vMin = Math.min(...R.walls.map((w) => w.axis === "u" ? w.v0 : Math.min(w.v0, w.v1)));
823
824
  const corners = /* @__PURE__ */ new Map();
825
+ const looks = /* @__PURE__ */ new Map();
824
826
  for (const w of R.walls) {
825
827
  const f = w.floor;
826
828
  cursor.group = "f" + f;
@@ -830,6 +832,10 @@ function installHouseWalls(K, shared) {
830
832
  const hh = cut ? Math.min(h, 1.2) : h, L = alongU ? w.v0 : w.u0, s0 = Math.min(alongU ? w.u0 : w.v0, alongU ? w.u1 : w.v1), s1 = Math.max(alongU ? w.u0 : w.v0, alongU ? w.u1 : w.v1);
831
833
  const front = alongU && Math.abs(L - vMin) < 0.05;
832
834
  const sideL = sideOf(w.sides[0], front), sideH = sideOf(w.sides[1], front);
835
+ if (w.id) {
836
+ const face = sideL.exterior ? sideL : sideH;
837
+ looks.set(w.id, { mat: face.mat, color: face.color, top: y0 + hh });
838
+ }
833
839
  shared.ctx.colliders.push({ type: "wall", piece: w.id, f, x0: alongU ? s0 : L - th / 2, z0: alongU ? L - th / 2 : s0, x1: alongU ? s1 : L + th / 2, z1: alongU ? L + th / 2 : s1 });
834
840
  if (w.type === "railing") {
835
841
  const c0 = shared.ctx.colors.metal != null ? shared.ctx.colors.metal : 9080466;
@@ -876,8 +882,27 @@ function installHouseWalls(K, shared) {
876
882
  cursor.group = "f" + e.f;
877
883
  bx(e.color.mat, e.pu - e.th / 2, e.y0, e.pv - e.th / 2, e.pu + e.th / 2, e.y0 + e.hh, e.pv + e.th / 2, e.color.color);
878
884
  }
885
+ if (shared.ctx.showPillars) wallPillars(looks);
879
886
  }
880
- Object.assign(K, { wallBox, sideOf, pieceOf, nearestWall, walls });
887
+ const PILLAR_IN_WALL = /^(wall_column_|column_\d|jamb_column_)/;
888
+ const wallOf = (pillar, parts) => {
889
+ const u = (pillar.aabb[0] + pillar.aabb[2]) / 2, v = (pillar.aabb[1] + pillar.aabb[3]) / 2;
890
+ return parts.find((q) => (q.role === "wall" || q.role === "partition") && q.assembly !== void 0 && q.floor === pillar.floor && u >= q.aabb[0] - 1e-3 && u <= q.aabb[2] + 1e-3 && v >= q.aabb[1] - 1e-3 && v <= q.aabb[3] + 1e-3);
891
+ };
892
+ function wallPillars(looks) {
893
+ const parts = Object.values(shared.ctx.structureIndex);
894
+ for (const e of parts) {
895
+ if (e.role !== "column" || !PILLAR_IN_WALL.test(e.id)) continue;
896
+ const host = wallOf(e, parts);
897
+ const look = host && looks.get(host.id);
898
+ if (!host || !look) continue;
899
+ cursor.group = "f" + e.floor;
900
+ cursor.pc = structurePiece(host.id);
901
+ bx(look.mat, e.aabb[0], e.y0, e.aabb[1], e.aabb[2], Math.min(e.y1, look.top), e.aabb[3], sh(look.color != null ? look.color : 15262420, 0.93));
902
+ }
903
+ cursor.pc = null;
904
+ }
905
+ Object.assign(K, { wallBox, sideOf, pieceOf, nearestWall, walls, wallPillars });
881
906
  }
882
907
 
883
908
  // src/house/doors.ts
@@ -6165,7 +6190,7 @@ function installLightsRoomLight(K, shared) {
6165
6190
 
6166
6191
  // src/destruction/pieces.ts
6167
6192
  import { assemblyPart, getAssembly } from "@adatechnology/game-destruction";
6168
- import { DENSITY, MEMBER_CAGE, toDestructibleSpec } from "@adatechnology/game-floorplan-structure";
6193
+ import { DENSITY, MEMBER_CAGE, cageResidualCapacity, toDestructibleSpec } from "@adatechnology/game-floorplan-structure";
6169
6194
 
6170
6195
  // src/destruction/panels.ts
6171
6196
  import { resolveSlabPanels, slabPanelView } from "@adatechnology/game-floorplan-structure";
@@ -6345,7 +6370,8 @@ function installDestructionPieces(K, shared) {
6345
6370
  if (!pc || pc.unit) return sp;
6346
6371
  if (pc.role !== "wall" && pc.role !== "partition") {
6347
6372
  const def = getAssembly(pc.assembly || (pc.material === "concrete" && ["slab", "beam", "column", "stair"].includes(pc.role) ? "reinforced_concrete" : ""));
6348
- const out = def && def.reinforcement && !sp.reinforcement ? { ...sp, reinforcement: { ...def.reinforcement } } : sp;
6373
+ const cage = def && def.reinforcement ? pc.role === "column" && sp.shape.kind === "box" ? { ...def.reinforcement, residualCapacity: cageResidualCapacity(sp.shape.size, def.reinforcement, sp.structure?.capacity) } : { ...def.reinforcement } : null;
6374
+ const out = cage && !sp.reinforcement ? { ...sp, reinforcement: cage } : sp;
6349
6375
  if (out.reinforcement && !out.units && pc.material === "concrete" && (pc.role === "beam" || pc.role === "column") && sp.shape.kind === "box") {
6350
6376
  return { ...out, units: { size: [MEMBER_CAGE.unit, MEMBER_CAGE.unit, Math.min(...sp.shape.size)], bond: "stack", mortar: "concrete" } };
6351
6377
  }
@@ -6374,6 +6400,7 @@ function collapseRange(mesh, v0, v1) {
6374
6400
  const a = pos.array;
6375
6401
  const u = mesh.userData;
6376
6402
  if (!u._p0) u._p0 = a.slice();
6403
+ if (!u.plainPositions) Object.defineProperty(u, "plainPositions", { value: u._p0, enumerable: false, configurable: true, writable: true });
6377
6404
  const list = u.collapsedRanges ?? (u.collapsedRanges = []);
6378
6405
  if (!list.some((r) => r[0] === v0 && r[1] === v1)) list.push([v0, v1]);
6379
6406
  const saved = a.slice(v0 * 3, v1 * 3);
@@ -6457,10 +6484,11 @@ function installDestructionBreach(K, _shared) {
6457
6484
  const v = house.worldToLocal(tmp.p.set(p[0], p[1], p[2]));
6458
6485
  return [v.x, v.y, v.z];
6459
6486
  };
6460
- function cells(part, units) {
6487
+ function cells(part, units, mask) {
6461
6488
  const [u0, v0, u1, v1] = part.aabb;
6462
- const alongU = u1 - u0 >= v1 - v0;
6489
+ const alongU = mask.alongU;
6463
6490
  const len = alongU ? u1 - u0 : v1 - v0;
6491
+ const offset = (alongU ? u0 : v0) - mask.start;
6464
6492
  const [ul, uh] = units.size;
6465
6493
  const rows = Math.max(1, Math.round((part.y1 - part.y0) / uh));
6466
6494
  const rowH = (part.y1 - part.y0) / rows;
@@ -6471,7 +6499,7 @@ function installDestructionBreach(K, _shared) {
6471
6499
  const shift = units.bond === "running" && r & 1 ? w / 2 : 0;
6472
6500
  for (let i = 0; i < per + (shift ? 1 : 0); i++) {
6473
6501
  const a = Math.max(0, i * w - shift), b = Math.min(len, i * w - shift + w);
6474
- if (b - a > 1e-3) out.push({ a, b, y0: part.y0 + r * rowH, y1: part.y0 + (r + 1) * rowH, r, i });
6502
+ if (b - a > 1e-3) out.push({ a: offset + a, b: offset + b, y0: part.y0 + r * rowH, y1: part.y0 + (r + 1) * rowH, r, i });
6475
6503
  }
6476
6504
  }
6477
6505
  return out;
@@ -6485,8 +6513,21 @@ function installDestructionBreach(K, _shared) {
6485
6513
  for (const q of structure.parts) {
6486
6514
  if (q.id !== partId && same(q.aabb, part.aabb) && Math.abs(q.y1 - part.y0) < 0.01 && structure.meshesOf(q.id).some((it) => it.mesh)) return q.id;
6487
6515
  }
6516
+ return part.role === "column" ? wallAround(part) : null;
6517
+ }
6518
+ function wallAround(part) {
6519
+ const cu = (part.aabb[0] + part.aabb[2]) / 2, cv = (part.aabb[1] + part.aabb[3]) / 2;
6520
+ for (const q of structure.parts) {
6521
+ if (q.id === part.id || q.role !== "wall" && q.role !== "partition" || !structure.meshesOf(q.id).some((it) => it.mesh)) continue;
6522
+ const inside = cu >= q.aabb[0] - 1e-3 && cu <= q.aabb[2] + 1e-3 && cv >= q.aabb[1] - 1e-3 && cv <= q.aabb[3] + 1e-3;
6523
+ if (inside && Math.min(q.y1, part.y1) - Math.max(q.y0, part.y0) > 0.5) return q.id;
6524
+ }
6488
6525
  return null;
6489
6526
  }
6527
+ const pillarSpans = (view) => structure.parts.filter((q) => q.role === "column" && wallAround(q) === view.host).map((q) => {
6528
+ const a = (view.mask.alongU ? q.aabb[0] : q.aabb[1]) - view.mask.start, b = (view.mask.alongU ? q.aabb[2] : q.aabb[3]) - view.mask.start;
6529
+ return { a, b, y0: q.y0, y1: q.y1 };
6530
+ });
6490
6531
  const riders = (hostId) => structure.parts.filter((q) => q.id !== hostId && specOf(q.id)?.units && hostOf(q.id) === hostId && !structure.meshesOf(q.id).some((it) => it.mesh)).map((q) => q.id);
6491
6532
  function shell(it, mask) {
6492
6533
  const g0 = it.mesh.geometry, g = new THREE.BufferGeometry();
@@ -6575,6 +6616,17 @@ function installDestructionBreach(K, _shared) {
6575
6616
  texture.needsUpdate = true;
6576
6617
  return { data, texture, w, h, alongU, start: alongU ? u0 : v0, len, y0, height: y1 - y0, rect: new THREE.Vector4(alongU ? u0 : v0, y0, len, y1 - y0) };
6577
6618
  }
6619
+ function besidePillars(c, pillars) {
6620
+ let { a, b } = c;
6621
+ for (const k of pillars) {
6622
+ if (c.y1 <= k.y0 || c.y0 >= k.y1 || b <= k.a || a >= k.b) continue;
6623
+ const centre = (a + b) / 2;
6624
+ if (centre > k.a && centre < k.b) return [];
6625
+ if (centre <= k.a) b = Math.min(b, k.a);
6626
+ else a = Math.max(a, k.b);
6627
+ }
6628
+ return b - a > 1e-3 ? [{ ...c, a, b }] : [];
6629
+ }
6578
6630
  const boxDistance = (s, y, a, b, c, d) => Math.hypot(Math.max(a - s, 0, s - b), Math.max(c - y, 0, y - d));
6579
6631
  const ellipseDistance = (s, y, cs, cy, rs, ry) => (Math.hypot((s - cs) / Math.max(rs, 1e-4), (y - cy) / Math.max(ry, 1e-4)) - 1) * Math.min(rs, ry);
6580
6632
  function covered(c, boxes) {
@@ -6597,9 +6649,15 @@ function installDestructionBreach(K, _shared) {
6597
6649
  const discs = [];
6598
6650
  const flakes = [];
6599
6651
  const shown = [];
6652
+ const pillars = pillarSpans(view);
6600
6653
  for (const id of view.parts) {
6601
6654
  const part = structure.byId.get(id), units2 = specOf(id).units, chips = view.chips.get(id), opening = view.openings.get(id), spalls = view.spalls.get(id);
6602
- const all = cells(part, units2);
6655
+ const own = cells(part, units2, mask);
6656
+ const all = id === view.host ? own.flatMap((c) => besidePillars(c, pillars)) : own;
6657
+ if (view.fallen.has(id)) {
6658
+ holes.push(...all);
6659
+ continue;
6660
+ }
6603
6661
  const thick = mask.alongU ? part.aabb[3] - part.aabb[1] : part.aabb[2] - part.aabb[0];
6604
6662
  const mid = mask.alongU ? (part.aabb[1] + part.aabb[3]) / 2 : (part.aabb[0] + part.aabb[2]) / 2;
6605
6663
  if (opening) discs.push({ s: along(opening.center), y: opening.center[1], r: opening.radius });
@@ -6695,52 +6753,55 @@ function installDestructionBreach(K, _shared) {
6695
6753
  }
6696
6754
  return { center: local(chip.center), along: a, up: u };
6697
6755
  }
6756
+ function newView(hostId) {
6757
+ const items = structure.meshesOf(hostId).filter((it) => it.mesh);
6758
+ if (!items.length) return null;
6759
+ const parts = [hostId, ...riders(hostId)].filter((id) => structure.byId.get(id) && specOf(id)?.units);
6760
+ const mask = maskOf(hostId, parts), core = maskOf(hostId, parts);
6761
+ return {
6762
+ host: hostId,
6763
+ parts,
6764
+ items,
6765
+ mask,
6766
+ core,
6767
+ unitMaterial: unitsMaterial(core),
6768
+ shells: [],
6769
+ saved: null,
6770
+ units: null,
6771
+ gone: 0,
6772
+ pits: 0,
6773
+ bounds: drawnBounds(items),
6774
+ chips: new Map(parts.map((id) => [id, []])),
6775
+ spalls: new Map(parts.map((id) => [id, []])),
6776
+ openings: /* @__PURE__ */ new Map(),
6777
+ flakes: 0,
6778
+ fallen: /* @__PURE__ */ new Set()
6779
+ };
6780
+ }
6781
+ function cut(view) {
6782
+ if (view.saved) return;
6783
+ view.shells = view.items.map((it) => shell(it, view.mask));
6784
+ for (const m of view.shells) m.userData.behind = () => view.units;
6785
+ view.saved = view.items.map((it) => ({ it, v0: it.v0, data: collapseRange(it.mesh, it.v0, it.v1) }));
6786
+ }
6698
6787
  function draw(partId, change) {
6699
6788
  if (lod !== 0) return false;
6700
6789
  const hostId = hostOf(partId);
6701
6790
  if (!hostId || !specOf(partId)?.units) return false;
6702
- let view = views.get(hostId);
6703
- if (!view) {
6704
- const items = structure.meshesOf(hostId).filter((it) => it.mesh);
6705
- if (!items.length) return false;
6706
- const parts = [hostId, ...riders(hostId)].filter((id) => structure.byId.get(id) && specOf(id)?.units);
6707
- if (!parts.includes(partId)) return false;
6708
- const mask = maskOf(hostId, parts), core = maskOf(hostId, parts);
6709
- view = {
6710
- host: hostId,
6711
- parts,
6712
- items,
6713
- mask,
6714
- core,
6715
- unitMaterial: unitsMaterial(core),
6716
- shells: [],
6717
- saved: null,
6718
- units: null,
6719
- gone: 0,
6720
- pits: 0,
6721
- bounds: drawnBounds(items),
6722
- chips: new Map(parts.map((id) => [id, []])),
6723
- spalls: new Map(parts.map((id) => [id, []])),
6724
- openings: /* @__PURE__ */ new Map(),
6725
- flakes: 0
6726
- };
6727
- }
6728
- if (!view.chips.has(partId)) return false;
6791
+ const view = views.get(hostId) ?? newView(hostId);
6792
+ if (!view || !view.chips.has(partId)) return false;
6729
6793
  if (change.opening) {
6730
6794
  if (view.openings.has(partId)) return false;
6731
6795
  view.openings.set(partId, { center: local(change.opening.center), radius: change.opening.radius });
6732
6796
  }
6733
6797
  if (change.chip) view.chips.get(partId).push(chipBox(change.chip, view.mask.alongU));
6798
+ for (const chip of change.chips ?? []) view.chips.get(partId).push(chipBox(chip, view.mask.alongU));
6734
6799
  if (change.spall) {
6735
6800
  const list = view.spalls.get(partId);
6736
6801
  list.push({ center: local(change.spall.center), radius: change.spall.radius });
6737
6802
  if (list.length > MAX_FLAKES) list.shift();
6738
6803
  }
6739
- if (!view.saved) {
6740
- view.shells = view.items.map((it) => shell(it, view.mask));
6741
- for (const m of view.shells) m.userData.behind = () => view.units;
6742
- view.saved = view.items.map((it) => ({ it, v0: it.v0, data: collapseRange(it.mesh, it.v0, it.v1) }));
6743
- }
6804
+ cut(view);
6744
6805
  views.set(hostId, view);
6745
6806
  if (opts.deferPaint && change.spall) dirty.add(view);
6746
6807
  else {
@@ -6851,14 +6912,32 @@ function installDestructionBreach(K, _shared) {
6851
6912
  */
6852
6913
  chip(event) {
6853
6914
  if (event.torn) return false;
6854
- return draw(event.part, { chip: { center: event.center, size: event.size ?? [0, 0, 0], rotation: event.rotation ?? [0, 0, 0, 1] } });
6915
+ const rotation = event.rotation ?? [0, 0, 0, 1];
6916
+ if (event.units?.length) return draw(event.part, { chips: event.units.map((unit) => ({ center: unit.center, size: unit.size, rotation })) });
6917
+ return draw(event.part, { chip: { center: event.center, size: event.size ?? [0, 0, 0], rotation } });
6855
6918
  },
6856
6919
  /** The part broke or fell: its damage goes (the plain range stays collapsed, the renderer hides the part). */
6857
6920
  drop(partId) {
6858
- const v = views.get(partId);
6859
- if (!v) return;
6921
+ let host = views.get(partId) ?? [...views.values()].find((view) => view.parts.includes(partId));
6922
+ if (!host && lod === 0 && riders(partId).length > 0) {
6923
+ host = newView(partId);
6924
+ if (host) {
6925
+ cut(host);
6926
+ views.set(partId, host);
6927
+ }
6928
+ }
6929
+ if (!host) return;
6930
+ host.fallen.add(partId);
6931
+ if (host.parts.some((id) => !host.fallen.has(id))) {
6932
+ dirty.delete(host);
6933
+ paint(host);
6934
+ invalidate();
6935
+ invalidateShadow();
6936
+ return;
6937
+ }
6938
+ const v = host;
6860
6939
  dispose(v);
6861
- views.delete(partId);
6940
+ views.delete(v.host);
6862
6941
  if (v.saved) dropped.push(v);
6863
6942
  },
6864
6943
  /** LOD 0 draws the cut wall; LOD 1 and 2 give the wall its box back. */
@@ -7338,6 +7417,7 @@ function installBuildBuild(K, shared) {
7338
7417
  shared.ctx.noPiece = false;
7339
7418
  cursor.separate = opts.pieces === "meshes";
7340
7419
  cursor.pc = null;
7420
+ shared.ctx.showPillars = opts.pillars !== "hidden";
7341
7421
  shared.ctx.apartment = r.template === "apartment_modern";
7342
7422
  shared.ctx.pa = Math.max(3, Math.min(20, pr.floors != null ? pr.floors : 5));
7343
7423
  shared.ctx.pf = Math.max(1, Math.min(shared.ctx.pa, pr.floor != null ? pr.floor : 3));
@@ -8133,10 +8213,10 @@ function createHouseScenarios(text, at = SPECIMEN_AT) {
8133
8213
  const list = events(room);
8134
8214
  const columns = groundColumns(parts);
8135
8215
  const laj = slabs(parts);
8136
- const first = columns[0];
8137
- const mate = columns.slice(1).filter((p) => Math.abs(p.position[0] - first.position[0]) < 0.5).sort((a, b) => Math.abs(b.position[2] - first.position[2]) - Math.abs(a.position[2] - first.position[2]))[0] ?? columns[1];
8138
- blunt(room, oid, first.id, centre(first.id), 3e6);
8139
- blunt(room, oid, mate.id, centre(mate.id), 3e6);
8216
+ const first = columns.find((p) => !/^wall_column_/.test(p.id)) ?? columns[0];
8217
+ const mate = columns.filter((p) => p !== first && !/^wall_column_/.test(p.id)).filter((p) => Math.abs(p.position[0] - first.position[0]) < 0.5).sort((a, b) => Math.abs(b.position[2] - first.position[2]) - Math.abs(a.position[2] - first.position[2]))[0] ?? columns[1];
8218
+ const along = columns.filter((p) => /^wall_column_/.test(p.id) && Math.abs(p.position[0] - first.position[0]) < 0.5);
8219
+ for (const p of [first, mate, ...along]) blunt(room, oid, p.id, centre(p.id), 3e6);
8140
8220
  step(90);
8141
8221
  const fell = collapsed(list);
8142
8222
  expect(laj.some((id) => fell.includes(id)), fill(T.two_slab_fell, { n: fell.length }));
@@ -8879,4 +8959,4 @@ export {
8879
8959
  FLOORPLAN_AREA_ID,
8880
8960
  createFloorplanTestModule
8881
8961
  };
8882
- //# sourceMappingURL=chunk-QUVGWVEC.js.map
8962
+ //# sourceMappingURL=chunk-7XVCZF6H.js.map