@adatechnology/game-floorplan-three 0.9.3 → 0.10.1

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.
@@ -3,6 +3,7 @@ var GameFloorplanThree = (() => {
3
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
6
7
  var __export = (target, all) => {
7
8
  for (var name in all)
8
9
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -16,6 +17,7 @@ var GameFloorplanThree = (() => {
16
17
  return to;
17
18
  };
18
19
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
19
21
 
20
22
  // src/index.ts
21
23
  var index_exports = {};
@@ -70,7 +72,7 @@ var GameFloorplanThree = (() => {
70
72
  shade: () => shade
71
73
  });
72
74
 
73
- // ../game-shapes/dist/chunk-M5N2SHNL.js
75
+ // ../game-shapes/dist/chunk-FZQTELDQ.js
74
76
  function canonicalJson(value) {
75
77
  if (value === null) return "null";
76
78
  if (typeof value === "number") return Object.is(value, -0) ? "-0" : Number.isFinite(value) ? String(value) : JSON.stringify(String(value));
@@ -982,17 +984,19 @@ var GameFloorplanThree = (() => {
982
984
  if (p.prevZ) p.prevZ.nextZ = p.nextZ;
983
985
  if (p.nextZ) p.nextZ.prevZ = p.prevZ;
984
986
  }
985
- function Node(i, x, y) {
986
- this.i = i;
987
- this.x = x;
988
- this.y = y;
989
- this.prev = null;
990
- this.next = null;
991
- this.z = 0;
992
- this.prevZ = null;
993
- this.nextZ = null;
994
- this.steiner = false;
995
- }
987
+ var Node = class {
988
+ constructor(i, x, y) {
989
+ this.i = i;
990
+ this.x = x;
991
+ this.y = y;
992
+ this.prev = null;
993
+ this.next = null;
994
+ this.z = 0;
995
+ this.prevZ = null;
996
+ this.nextZ = null;
997
+ this.steiner = false;
998
+ }
999
+ };
996
1000
  function signedArea(data, start, end, dim) {
997
1001
  let sum = 0;
998
1002
  for (let i = start, j = end - dim; i < end; i += dim) {
@@ -1817,6 +1821,9 @@ var GameFloorplanThree = (() => {
1817
1821
  return mesh2;
1818
1822
  });
1819
1823
  }
1824
+ function isMesh(o) {
1825
+ return !!o && o.isMesh === true;
1826
+ }
1820
1827
 
1821
1828
  // ../game-props3d/dist/index.js
1822
1829
  function rng(seed) {
@@ -3691,6 +3698,20 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
3691
3698
  const WITH_MAP = { roughcast: 1, block: 1 };
3692
3699
  class Accumulator {
3693
3700
  constructor() {
3701
+ __publicField(this, "cv");
3702
+ __publicField(this, "ci");
3703
+ __publicField(this, "p");
3704
+ __publicField(this, "n");
3705
+ __publicField(this, "c");
3706
+ __publicField(this, "u");
3707
+ __publicField(this, "i");
3708
+ __publicField(this, "nv");
3709
+ __publicField(this, "ni");
3710
+ __publicField(this, "lim");
3711
+ __publicField(this, "cur");
3712
+ __publicField(this, "piece0");
3713
+ __publicField(this, "mat", "");
3714
+ __publicField(this, "group", "");
3694
3715
  this.cv = 2048;
3695
3716
  this.ci = 3072;
3696
3717
  this.p = new Float32Array(this.cv * 3);
@@ -3753,7 +3774,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
3753
3774
  g.position.copy(u.pose0.p);
3754
3775
  g.quaternion.copy(u.pose0.q);
3755
3776
  g.scale.copy(u.pose0.s);
3756
- const e = new THREE.Vector3(...a.axis || [0, 1, 0]);
3777
+ const [ax, ay, az] = a.axis || [0, 1, 0];
3778
+ const e = new THREE.Vector3(ax, ay, az);
3757
3779
  if (a.type === "swing" || a.type === "toggle") g.rotateOnAxis(e, (a.angle || 0) * t);
3758
3780
  else if (a.type === "slide") g.translateOnAxis(e, (a.dist || 0) * t);
3759
3781
  else if (a.type === "scale") {
@@ -3848,7 +3870,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
3848
3870
  }
3849
3871
  if (uvr) {
3850
3872
  let gu = G.u[k * 2], gv = G.u[k * 2 + 1];
3851
- for (let r = uvr.rot | 0; r > 0; r--) {
3873
+ for (let r = (uvr.rot ?? 0) | 0; r > 0; r--) {
3852
3874
  const t = gu;
3853
3875
  gu = gv;
3854
3876
  gv = 1 - t;
@@ -3946,14 +3968,13 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
3946
3968
  A.nv += n;
3947
3969
  }
3948
3970
  let bxImpl = bxBase;
3949
- function bx(...a) {
3950
- return bxImpl(...a);
3951
- }
3952
- K.getBx = () => bxImpl;
3953
- K.setBx = (f) => {
3971
+ const bx = (...a) => bxImpl(...a);
3972
+ const getBx = () => bxImpl;
3973
+ const setBx = (f) => {
3954
3974
  bxImpl = f;
3955
3975
  };
3956
- Object.assign(K, { GEO, UV_SCALE, WITH_MAP, Accumulator, cursor, PARTS, MIRRORS, part, applyPart, _Y, _qa, _qb, pieceRecord, structurePiece, accFor, _q, _e, _col, rgbOf, put, boxC, bx, cylBase, polyFace });
3976
+ const defined = { GEO, UV_SCALE, WITH_MAP, Accumulator, cursor, PARTS, MIRRORS, part, applyPart, _Y, _qa, _qb, pieceRecord, structurePiece, accFor, _q, _e, _col, rgbOf, put, boxC, bx, getBx, setBx, cylBase, polyFace };
3977
+ Object.assign(K, defined);
3957
3978
  }
3958
3979
 
3959
3980
  // src/kernel/noise.ts
@@ -3971,17 +3992,20 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
3971
3992
  const n = 0.5 * vnoise(x / 5.5, z / 5.5) + 0.32 * vnoise(x / 1.9 + 31, z / 1.9) + 0.18 * vnoise(x / 0.8 + 7, z / 0.8), m = vnoise(x / 9 + 100, z / 9 + 50) - 0.5, f = 0.76 + 0.5 * n;
3972
3993
  return type === "dirt" ? [f * (1 + 0.14 * m), f * (1 + 0.03 * m), f * (1 - 0.18 * m)] : [f * (1 + 0.1 * m), f * (1 + 0.05 * m), f * (1 - 0.1 * m)];
3973
3994
  }
3974
- Object.assign(K, { _h2, vnoise, macroGround });
3995
+ const defined = { _h2, vnoise, macroGround };
3996
+ Object.assign(K, defined);
3975
3997
  }
3976
3998
 
3977
3999
  // src/materials/textures.ts
3978
4000
  function installMaterialsTextures(K, _shared) {
3979
4001
  const { DOC, THREE, cfg } = K;
3980
4002
  function texture(n, f, rep = true, rx = 1, ry = 1) {
4003
+ if (!DOC) throw new Error("game-floorplan-three: the procedural textures need a document (options.document)");
3981
4004
  const c = DOC.createElement("canvas");
3982
4005
  c.width = n;
3983
4006
  c.height = n;
3984
4007
  const g = c.getContext("2d");
4008
+ if (!g) throw new Error("game-floorplan-three: no 2d canvas context");
3985
4009
  f(g, n);
3986
4010
  const t = new THREE.CanvasTexture(c);
3987
4011
  t.colorSpace = THREE.SRGBColorSpace;
@@ -4100,10 +4124,16 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
4100
4124
  const TEX = {};
4101
4125
  for (const k of Object.keys(TEXGEN)) TEX[k] = cfg.textures && cfg.textures[k] || TEXGEN[k]();
4102
4126
  const ATLAS = {};
4103
- Object.assign(K, { texture, rngTexture, TEXGEN, TEX, ATLAS });
4127
+ const defined = { texture, rngTexture, TEXGEN, TEX, ATLAS };
4128
+ Object.assign(K, defined);
4104
4129
  }
4105
4130
 
4106
4131
  // src/materials/materials.ts
4132
+ function context2d(c) {
4133
+ const g = c.getContext("2d");
4134
+ if (!g) throw new Error("game-floorplan-three: no 2d canvas context");
4135
+ return g;
4136
+ }
4107
4137
  function installMaterialsMaterials(K, _shared) {
4108
4138
  const { THREE, cfg, ATLAS, TEX, rngTexture } = K;
4109
4139
  function createMaterials(props) {
@@ -4115,7 +4145,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
4115
4145
  return t;
4116
4146
  };
4117
4147
  if (!(cfg.atlas && cfg.atlas.floorKind)) {
4118
- const pc = props.atlas("floor").canvas, g = pc.getContext("2d");
4148
+ const pc = props.atlas("floor").canvas, g = context2d(pc);
4119
4149
  for (const c of [0, 1]) {
4120
4150
  g.save();
4121
4151
  g.globalCompositeOperation = "screen";
@@ -4130,7 +4160,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
4130
4160
  g.restore();
4131
4161
  }
4132
4162
  if (!(cfg.atlas && cfg.atlas.floorKind)) {
4133
- const pc = props.atlas("floor").canvas, g = pc.getContext("2d");
4163
+ const pc = props.atlas("floor").canvas, g = context2d(pc);
4134
4164
  g.save();
4135
4165
  g.fillStyle = "rgba(128,92,62,0.52)";
4136
4166
  g.fillRect(256, 256, 256, 256);
@@ -4227,7 +4257,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
4227
4257
  }
4228
4258
  const ORDER = { glass: 3, water: 2, screen: 3, building: 4 };
4229
4259
  const NO_SHADOW = /* @__PURE__ */ new Set(["dirt", "glass", "water", "screen", "building", "picture", "floor", "emissive"]);
4230
- Object.assign(K, { createMaterials, antiTile, dirtMaterial, CLOTHES_UNIFORMS, clothesMaterial, ORDER, NO_SHADOW });
4260
+ const defined = { createMaterials, antiTile, dirtMaterial, CLOTHES_UNIFORMS, clothesMaterial, ORDER, NO_SHADOW };
4261
+ Object.assign(K, defined);
4231
4262
  }
4232
4263
 
4233
4264
  // src/kernel/close-scene.ts
@@ -4238,15 +4269,18 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
4238
4269
  const assembleVegetation = (...a) => K.assembleVegetation(...a);
4239
4270
  function markMesh(m, A) {
4240
4271
  const L = A.lim, fx = [];
4272
+ const pieces = shared.ctx.pieces;
4241
4273
  for (let k = 0; k < L.length; k++) {
4242
- if (!L[k].pc) continue;
4243
- const v1 = k + 1 < L.length ? L[k + 1].v : A.nv, i1 = k + 1 < L.length ? L[k + 1].i : A.ni;
4244
- if (v1 > L[k].v) fx.push({ id: L[k].pc.id, v0: L[k].v, v1, i0: L[k].i, i1 });
4274
+ const lim = L[k], pc = lim.pc;
4275
+ if (!pc) continue;
4276
+ const next = L[k + 1];
4277
+ const v1 = next ? next.v : A.nv, i1 = next ? next.i : A.ni;
4278
+ if (v1 > lim.v) fx.push({ id: pc.id, v0: lim.v, v1, i0: lim.i, i1 });
4245
4279
  }
4246
4280
  if (!fx.length) return;
4247
4281
  const u = m.userData, ids = [...new Set(fx.map((f) => f.id))], weight = {};
4248
4282
  for (const f of fx) {
4249
- const mt = shared.ctx.pieces.get(f.id).mat;
4283
+ const mt = pieces.get(f.id).mat;
4250
4284
  weight[mt] = (weight[mt] || 0) + f.v1 - f.v0;
4251
4285
  }
4252
4286
  u.ranges = fx;
@@ -4254,7 +4288,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
4254
4288
  u.destructionMaterial = Object.keys(weight).sort((a, b) => weight[b] - weight[a])[0];
4255
4289
  if (ids.length === 1) {
4256
4290
  u.destructionPart = ids[0];
4257
- const pc = shared.ctx.pieces.get(ids[0]);
4291
+ const pc = pieces.get(ids[0]);
4258
4292
  if (pc.role) u.structuralRole = pc.role;
4259
4293
  }
4260
4294
  }
@@ -4305,7 +4339,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
4305
4339
  assembleVegetation(groups);
4306
4340
  return groups;
4307
4341
  }
4308
- Object.assign(K, { markMesh, closeScene });
4342
+ const defined = { markMesh, closeScene };
4343
+ Object.assign(K, defined);
4309
4344
  }
4310
4345
 
4311
4346
  // src/house/helpers.ts
@@ -4343,8 +4378,14 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
4343
4378
  for (const h of holes) L = L.flatMap((r) => subRect(r, h));
4344
4379
  return L;
4345
4380
  }
4346
- const wallColor = (c) => c && c.color && colorOf(c.color.wall) != null ? colorOf(c.color.wall) : 14999768;
4347
- const floorColor = (c) => c && c.color && colorOf(c.color.floor) != null ? colorOf(c.color.floor) : 11577496;
4381
+ const wallColor = (c) => {
4382
+ const k = c && c.color ? colorOf(c.color.wall) : null;
4383
+ return k != null ? k : 14999768;
4384
+ };
4385
+ const floorColor = (c) => {
4386
+ const k = c && c.color ? colorOf(c.color.floor) : null;
4387
+ return k != null ? k : 11577496;
4388
+ };
4348
4389
  function stairFootprint(m) {
4349
4390
  const c = Math.cos(m.ry), s = Math.sin(m.ry), pts = [[-m.w / 2, -m.d / 2], [m.w / 2, -m.d / 2], [m.w / 2, m.d / 2], [-m.w / 2, m.d / 2]].map(([a, b]) => [m.x + a * c + b * s, m.z - a * s + b * c]);
4350
4391
  return [Math.min(...pts.map((p) => p[0])), Math.min(...pts.map((p) => p[1])), Math.max(...pts.map((p) => p[0])), Math.max(...pts.map((p) => p[1]))];
@@ -4355,12 +4396,14 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
4355
4396
  const pitch = A[f + 1].y0 - A[f].y0;
4356
4397
  return h > pitch && h - pitch <= 0.45 ? pitch : h;
4357
4398
  }
4358
- Object.assign(K, { OPEN_ROOM_TYPES: OPEN_ROOM_TYPES3, NO_CEILING, OPEN_AREA, EPS: EPS3, Y0, colorOf, rectsOf: rectsOf2, roomAt, subRect, subRects, wallColor, floorColor, stairFootprint, effectiveHeight });
4399
+ const defined = { OPEN_ROOM_TYPES: OPEN_ROOM_TYPES3, NO_CEILING, OPEN_AREA, EPS: EPS3, Y0, colorOf, rectsOf: rectsOf2, roomAt, subRect, subRects, wallColor, floorColor, stairFootprint, effectiveHeight };
4400
+ Object.assign(K, defined);
4359
4401
  }
4360
4402
 
4361
4403
  // src/house/walls.ts
4362
4404
  function installHouseWalls(K, shared) {
4363
4405
  const { EPS: EPS3, Y0, boxC, bx, cursor, effectiveHeight, pieceRecord, structurePiece, wallColor } = K;
4406
+ const sh = (...a) => K.sh(...a);
4364
4407
  function wallBox2(alongU, L, a, b, ya, yb, th, cL, cH, mL, mH) {
4365
4408
  if (yb - ya < 1e-4 || b - a < 1e-4) return;
4366
4409
  if (alongU) {
@@ -4404,8 +4447,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
4404
4447
  function walls() {
4405
4448
  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)));
4406
4449
  const corners = /* @__PURE__ */ new Map();
4450
+ const looks = /* @__PURE__ */ new Map();
4407
4451
  for (const w of R.walls) {
4408
- const f = w.floor;
4452
+ const f = w.floor, colliders = shared.ctx.colliders;
4409
4453
  cursor.group = "f" + f;
4410
4454
  cursor.pc = pieceOf(w);
4411
4455
  const y0 = Y0(f), pitch = f + 1 < nA ? Y0(f + 1) - y0 : Infinity, th = w.thickness || 0.15, alongU = w.axis === "u";
@@ -4413,7 +4457,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
4413
4457
  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);
4414
4458
  const front = alongU && Math.abs(L - vMin) < 0.05;
4415
4459
  const sideL = sideOf(w.sides[0], front), sideH = sideOf(w.sides[1], front);
4416
- 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 });
4460
+ if (w.id) {
4461
+ const face = sideL.exterior ? sideL : sideH;
4462
+ looks.set(w.id, { mat: face.mat, color: face.color, top: y0 + hh });
4463
+ }
4464
+ 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 });
4417
4465
  if (w.type === "railing") {
4418
4466
  const c0 = shared.ctx.colors.metal != null ? shared.ctx.colors.metal : 9080466;
4419
4467
  const sx = alongU ? 1 : 0, sz = alongU ? 0 : 1, len = s1 - s0, n = Math.max(1, Math.round(len / 1.4));
@@ -4459,8 +4507,28 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
4459
4507
  cursor.group = "f" + e.f;
4460
4508
  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);
4461
4509
  }
4510
+ if (shared.ctx.showPillars) wallPillars(looks);
4511
+ }
4512
+ const PILLAR_IN_WALL = /^(wall_column_|column_\d|jamb_column_)/;
4513
+ const wallOf = (pillar, parts) => {
4514
+ const u = (pillar.aabb[0] + pillar.aabb[2]) / 2, v = (pillar.aabb[1] + pillar.aabb[3]) / 2;
4515
+ 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);
4516
+ };
4517
+ function wallPillars(looks) {
4518
+ const parts = Object.values(shared.ctx.structureIndex);
4519
+ for (const e of parts) {
4520
+ if (e.role !== "column" || !PILLAR_IN_WALL.test(e.id)) continue;
4521
+ const host = wallOf(e, parts);
4522
+ const look = host && looks.get(host.id);
4523
+ if (!host || !look) continue;
4524
+ cursor.group = "f" + e.floor;
4525
+ cursor.pc = structurePiece(host.id);
4526
+ 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));
4527
+ }
4528
+ cursor.pc = null;
4462
4529
  }
4463
- Object.assign(K, { wallBox: wallBox2, sideOf, pieceOf, nearestWall, walls });
4530
+ const defined = { wallBox: wallBox2, sideOf, pieceOf, nearestWall, walls, wallPillars };
4531
+ Object.assign(K, defined);
4464
4532
  }
4465
4533
 
4466
4534
  // src/house/doors.ts
@@ -4479,7 +4547,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
4479
4547
  }
4480
4548
  shared.doorState = /* @__PURE__ */ new Map();
4481
4549
  function doors() {
4482
- shared.ctx.leaves = [];
4550
+ const leaves = shared.ctx.leaves = [], doorState = shared.doorState, colliders = shared.ctx.colliders;
4483
4551
  const R = shared.ctx.r, cut = shared.ctx.cut, C = shared.ctx.colors, wood = C.wood != null ? C.wood : 8016948, frame2 = sh(wood, 0.55);
4484
4552
  for (const p of R.doors) {
4485
4553
  const f = p.floor;
@@ -4510,21 +4578,21 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
4510
4578
  }
4511
4579
  const leaf = (u0, u1, y_0, y_1, tt, color2, mat = "wood") => alongU ? bx(mat, u0, y0 + y_0, L - tt / 2, u1, y0 + y_1, L + tt / 2, color2) : bx(mat, L - tt / 2, y0 + y_0, u0, L + tt / 2, y0 + y_1, u1, color2);
4512
4580
  const extC = wood;
4513
- const open0 = p.blocked ? false : shared.doorState.has(p.id) ? shared.doorState.get(p.id) : !!p.open;
4514
- shared.doorState.set(p.id, open0);
4581
+ const open0 = p.blocked ? false : doorState.has(p.id) ? doorState.get(p.id) === true : !!p.open;
4582
+ doorState.set(p.id, open0);
4515
4583
  const dc = alongU ? [p.at, L] : [L, p.at];
4516
- const fo = { id: p.id, floor: f, type: p.model === "steel" ? "steel" : "swing", open: open0, t: open0 ? 1 : 0, blockedFlag: !!p.blocked, locked: R.state === "fortified" && !!p.exterior, handleNames: [], hT: 0, cx: dc[0], cz: dc[1], visH, groups: [], col: null };
4584
+ const fo = { id: p.id, floor: f, type: p.model === "steel" ? "steel" : "swing", open: open0, t: open0 ? 1 : 0, blockedFlag: !!p.blocked, locked: R.state === "fortified" && !!p.exterior, handleNames: [], hT: 0, cx: dc[0], cz: dc[1], visH, groups: [], gs: [], col: null };
4517
4585
  const colR = { type: "door", f, x0: alongU ? a : L - th / 2, z0: alongU ? L - th / 2 : a, x1: alongU ? b : L + th / 2, z1: alongU ? L + th / 2 : b, off: open0 };
4518
- shared.ctx.colliders.push(colR);
4586
+ colliders.push(colR);
4519
4587
  fo.col = colR;
4520
4588
  const leafBox = (hu, d0, d1, y_0, y_1, tt, color2, mat) => alongU ? bx(mat, d0, y_0, -tt / 2, d1, y_1, tt / 2, color2) : bx(mat, -tt / 2, y_0, d0, tt / 2, y_1, d1, color2);
4521
- const newGroup = (k, hu, topY) => {
4589
+ const newGroup2 = (k, hu, topY) => {
4522
4590
  cursor.group = "dyn:" + p.id + ":" + k;
4523
4591
  fo.groups.push({ name: cursor.group, pos: alongU ? [hu, topY != null ? y0 + topY : y0, L] : [L, topY != null ? y0 + topY : y0, hu] });
4524
4592
  };
4525
4593
  if (p.model === "steel") {
4526
- const light = [14276044, 13223615, 14999766], pal = ((R.seed | 0) + p.id.length * 7) % 4, color2 = pal === 3 ? wood : light[pal], mid = (a + b) / 2;
4527
- newGroup(0, mid, visH);
4594
+ const light = [14276044, 13223615, 14999766], pal = ((Number(R.seed) | 0) + p.id.length * 7) % 4, color2 = pal === 3 ? wood : light[pal], mid = (a + b) / 2;
4595
+ newGroup2(0, mid, visH);
4528
4596
  const la = a + jw - mid, lb = b - jw - mid, nSec = Math.max(4, Math.round(visH / 0.5)), hs = visH / nSec, grv = sh(color2, 0.55);
4529
4597
  leafBox(0, la, lb, -visH, 0, 0.05, color2, "metal");
4530
4598
  for (let k = 1; k < nSec; k++) leafBox(0, la, lb, -k * hs - 0.012, -k * hs + 0.012, 0.07, grv, "metal");
@@ -4539,7 +4607,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
4539
4607
  for (let k = 0; k < nf; k++) {
4540
4608
  const u0 = nf === 2 ? k ? (aa + bb) / 2 : aa : aa, u1 = nf === 2 ? k ? bb : (aa + bb) / 2 : bb, color2 = p.exterior ? extC : wood;
4541
4609
  const hingeAt = nf === 2 ? k ? u1 : u0 : p.hinge === "end" ? u1 : u0, dir = hingeAt === u0 ? 1 : -1, len = u1 - u0;
4542
- newGroup(k, hingeAt);
4610
+ newGroup2(k, hingeAt);
4543
4611
  const d0 = dir > 0 ? 5e-3 : -len + 5e-3, d1 = dir > 0 ? len - 5e-3 : -5e-3;
4544
4612
  leafBox(hingeAt, d0, d1, 0, visH, 0.04, color2, PMAT);
4545
4613
  {
@@ -4575,7 +4643,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
4575
4643
  fo.rot = Math.atan2(n[0] * A[1] - n[1] * A[0], A[0] * n[0] + A[1] * n[1]);
4576
4644
  }
4577
4645
  }
4578
- shared.ctx.leaves.push(fo);
4646
+ leaves.push(fo);
4579
4647
  cursor.group = "f" + f;
4580
4648
  if (p.blocked) {
4581
4649
  part("window_boards", [0, 0, 0], () => {
@@ -4603,11 +4671,12 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
4603
4671
  }
4604
4672
  }
4605
4673
  }, { id: "boards:" + p.id }, "f" + f);
4606
- shared.ctx.colliders.push({ type: "board", f, x0: alongU ? a : L - th / 2, z0: alongU ? L - th / 2 : a, x1: alongU ? b : L + th / 2, z1: alongU ? L + th / 2 : b });
4674
+ colliders.push({ type: "board", f, x0: alongU ? a : L - th / 2, z0: alongU ? L - th / 2 : a, x1: alongU ? b : L + th / 2, z1: alongU ? L + th / 2 : b });
4607
4675
  }
4608
4676
  }
4609
4677
  }
4610
- Object.assign(K, { doorLineSides, doors });
4678
+ const defined = { doorLineSides, doors };
4679
+ Object.assign(K, defined);
4611
4680
  }
4612
4681
 
4613
4682
  // src/house/windows.ts
@@ -4667,7 +4736,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
4667
4736
  }
4668
4737
  }
4669
4738
  }
4670
- Object.assign(K, { FRAME_COLORS, SILL_COLORS, windows });
4739
+ const defined = { FRAME_COLORS, SILL_COLORS, windows };
4740
+ Object.assign(K, defined);
4671
4741
  }
4672
4742
 
4673
4743
  // src/house/floors.ts
@@ -4677,7 +4747,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
4677
4747
  if (shared.ctx._flights && shared.ctx._flights.r === shared.ctx.r) return shared.ctx._flights.l;
4678
4748
  const R = shared.ctx.r, mp = /* @__PURE__ */ new Map();
4679
4749
  for (const m of R.items) {
4680
- if (m.type !== "stairs" || !m.link) continue;
4750
+ if (m.type !== "stairs" || !m.link || typeof m.link !== "object") continue;
4681
4751
  const k = [m.link.from, m.link.to, m.x.toFixed(2), m.z.toFixed(2), (m.ry || 0).toFixed(2)].join("|");
4682
4752
  if (!mp.has(k)) mp.set(k, { m, from: m.link.from, to: m.link.to, rect: stairFootprint(m) });
4683
4753
  }
@@ -4696,7 +4766,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
4696
4766
  }
4697
4767
  const panelRects = (p) => p.footprint && p.footprint.length ? p.footprint : [p.aabb];
4698
4768
  function tilesOf(parent, rc) {
4699
- const ps = parent && shared.ctx.panelsOf && shared.ctx.panelsOf.get(parent);
4769
+ const ps = parent ? shared.ctx.panelsOf && shared.ctx.panelsOf.get(parent) : void 0;
4700
4770
  if (!ps) return [[parent ? structurePiece(parent) : null, rc]];
4701
4771
  const out = [], taken = [];
4702
4772
  for (const p of ps) for (const q of panelRects(p)) {
@@ -4760,13 +4830,12 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
4760
4830
  const { rectsOf: rectsOf2, Y0, boxC, bx, cursor, flights, stairFootprint, structurePiece } = K;
4761
4831
  const sh = (...a) => K.sh(...a);
4762
4832
  function buildStairs() {
4763
- const R = shared.ctx.r;
4764
- shared.ctx.stairs = [];
4833
+ const R = shared.ctx.r, stairs = shared.ctx.stairs = [];
4765
4834
  for (const vo of flights()) {
4766
4835
  const m = vo.m, f = vo.from, Dr = m.d - (vo.LP || 0);
4767
4836
  cursor.group = "f" + f;
4768
4837
  cursor.pc = structurePiece("stairs_" + vo.from + "_" + vo.to);
4769
- const y0 = Y0(f), H = Y0(m.link.to) - y0, n = Math.max(8, Math.round(H / 0.18)), c = Math.cos(m.ry), s = Math.sin(m.ry);
4838
+ const y0 = Y0(f), H = Y0(vo.to) - y0, n = Math.max(8, Math.round(H / 0.18)), c = Math.cos(m.ry), s = Math.sin(m.ry);
4770
4839
  const wood = shared.ctx.colors.wood != null ? shared.ctx.colors.wood : 9071176, under = sh(wood, 0.7);
4771
4840
  const P = (lx, lz) => [m.x + lx * c + lz * s, m.z - lx * s + lz * c];
4772
4841
  const boxL = (mat, lx, y, lz, w, h, d, color2, rx = 0) => {
@@ -4789,7 +4858,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
4789
4858
  }
4790
4859
  }
4791
4860
  cursor.pc = null;
4792
- shared.ctx.stairs.push({ m, f, to: vo.to, y0, H, rect: stairFootprint(m), landingPoint: (vo.LP || 0) / m.d });
4861
+ stairs.push({ m, f, to: vo.to, y0, H, rect: stairFootprint(m), landingPoint: (vo.LP || 0) / m.d });
4793
4862
  }
4794
4863
  }
4795
4864
  function elevator() {
@@ -4806,7 +4875,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
4806
4875
  }
4807
4876
  }
4808
4877
  }
4809
- Object.assign(K, { buildStairs, elevator });
4878
+ const defined = { buildStairs, elevator };
4879
+ Object.assign(K, defined);
4810
4880
  }
4811
4881
 
4812
4882
  // src/lights/fixtures.ts
@@ -4846,14 +4916,15 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
4846
4916
  c: (mat, x, y, z, r, h, color2, seg) => api.cylinder(M, mat, x, y0 + y, z, r, h, color2, 0, 0, seg),
4847
4917
  e: (mat, x, y, z, r, h, color2) => api.cylinder(M, mat, x, y0 + y - h / 2, z, r, h, color2, 0, 0, "sphere"),
4848
4918
  t(mat, p, q, r, color2, seg) {
4919
+ const tc = color2 ?? NaN;
4849
4920
  const dx = q[0] - p[0], dy = q[1] - p[1], dz = q[2] - p[2], L = Math.hypot(dx, dy, dz);
4850
4921
  if (L < 1e-5) return;
4851
4922
  const nx = dx / L, ny = dy / L, nz = dz / L;
4852
4923
  if (Math.abs(nx) < 1e-4 && Math.abs(nz) < 1e-4) {
4853
- api.cylinder(M, mat, p[0], y0 + Math.min(p[1], q[1]), p[2], r, L, color2, 0, 0, seg);
4924
+ api.cylinder(M, mat, p[0], y0 + Math.min(p[1], q[1]), p[2], r, L, tc, 0, 0, seg);
4854
4925
  return;
4855
4926
  }
4856
- api.cylinder(M, mat, (p[0] + q[0]) / 2, y0 + (p[1] + q[1]) / 2, (p[2] + q[2]) / 2, r, L, color2, Math.atan2(nz, ny), -Math.asin(nx), seg);
4927
+ api.cylinder(M, mat, (p[0] + q[0]) / 2, y0 + (p[1] + q[1]) / 2, (p[2] + q[2]) / 2, r, L, tc, Math.atan2(nz, ny), -Math.asin(nx), seg);
4857
4928
  },
4858
4929
  polyFace: (mat, pts, color2, hint) => polyFace(mat, pts.map((p) => W(p[0], p[1], p[2])), color2, hint && [hint[0] * c + hint[2] * s, hint[1], -hint[0] * s + hint[2] * c]),
4859
4930
  part(type, lx, ly, lz, fn, meta) {
@@ -5032,7 +5103,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
5032
5103
  K2.part("light_switch", x, 0, 0.011, () => K2.b("plain", x, -0.018, 0.0125, two ? 0.03 : 0.036, 0.034, 8e-3, 16513782, 0, -0.12), { id: "sw_" + M.seed + "_" + i, room: M.room, target: M.target || M.room, opens: { type: "toggle", axis: [1, 0, 0], angle: 0.24 }, on: !!M.on, t0: M.on ? 1 : 0 });
5033
5104
  }
5034
5105
  }
5035
- Object.assign(K, { hashString, rngString, SEED, STYLE, kit2, arcPoints, TEMP_WARM, TEMP_NEUTRAL, TEMP_COOL, colorTemperature, lightStyle, ceilingLamp, tableLamp, floorLamp, sconce, outdoorLamp, lightSwitch });
5106
+ const defined = { hashString, rngString, SEED, STYLE, kit2, arcPoints, TEMP_WARM, TEMP_NEUTRAL, TEMP_COOL, colorTemperature, lightStyle, ceilingLamp, tableLamp, floorLamp, sconce, outdoorLamp, lightSwitch };
5107
+ Object.assign(K, defined);
5036
5108
  }
5037
5109
 
5038
5110
  // src/lights/decor.ts
@@ -5099,7 +5171,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
5099
5171
  void sd;
5100
5172
  }
5101
5173
  }
5102
- Object.assign(K, { decorLights });
5174
+ const defined = { decorLights };
5175
+ Object.assign(K, defined);
5103
5176
  }
5104
5177
 
5105
5178
  // src/lot/terrain.ts
@@ -5152,7 +5225,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
5152
5225
  if (x < b) out.push([x, b]);
5153
5226
  return out.filter((q) => q[1] - q[0] > 0.3);
5154
5227
  }
5155
- Object.assign(K, { terrain, freeSpans });
5228
+ const defined = { terrain, freeSpans };
5229
+ Object.assign(K, defined);
5156
5230
  }
5157
5231
 
5158
5232
  // src/lot/perimeter.ts
@@ -5172,7 +5246,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
5172
5246
  for (const s of lt.perimeter) {
5173
5247
  const alongU = Math.abs(s.v1 - s.v0) < EPS3, L = alongU ? s.v0 : s.u0, a = Math.min(alongU ? s.u0 : s.v0, alongU ? s.u1 : s.v1), b = Math.max(alongU ? s.u0 : s.v0, alongU ? s.u1 : s.v1);
5174
5248
  cursor.pc = pieceRecord("lot_wall:" + s.id, s.type === "wall" ? "brick" : s.type === "fence" ? "wood_soft" : "steel", null, { tags: ["wall"] });
5175
- const th = s.thickness || 0.15, h = s.height, color2 = colorOf(s.color) != null ? colorOf(s.color) : C.wall || 12104874, len = b - a;
5249
+ const th = s.thickness || 0.15, h = s.height, color2 = colorOf(s.color) ?? (C.wall || 12104874), len = b - a;
5176
5250
  const B = (mat, u0, y0, u1, y1, t, c, off = 0) => alongU ? bx(mat, u0, y0, L + off - t / 2, u1, y1, L + off + t / 2, c) : bx(mat, L + off - t / 2, y0, u0, L + off + t / 2, y1, u1, c);
5177
5251
  const col = { type: "lot_wall", f: -1, x0: alongU ? a : L - th / 2, z0: alongU ? L - th / 2 : a, x1: alongU ? b : L + th / 2, z1: alongU ? L + th / 2 : b };
5178
5252
  shared.ctx.colliders.push(col);
@@ -5246,7 +5320,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
5246
5320
  }
5247
5321
  }
5248
5322
  }
5249
- Object.assign(K, { perimeter, paths });
5323
+ const defined = { perimeter, paths };
5324
+ Object.assign(K, defined);
5250
5325
  }
5251
5326
 
5252
5327
  // src/lot/gates.ts
@@ -5262,7 +5337,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
5262
5337
  cursor.group = "lot";
5263
5338
  const structure = R.style, auto = structure !== "budget", wall = colorOf("wall") || 12104874, cu = (lt.rect[0] + lt.rect[2]) / 2, cz = (lt.rect[1] + lt.rect[3]) / 2;
5264
5339
  for (const g of lt.gates) {
5265
- const gc = colorOf(g.color) != null ? colorOf(g.color) : C.gate != null ? C.gate : 2763310, a = g.u - g.width / 2, b = g.u + g.width / 2, v = g.v, h = g.height, t = 0.05, ped = g.model === "pedestrian", tilt = g.model === "tilting";
5340
+ const gc = colorOf(g.color) ?? (C.gate != null ? C.gate : 2763310), a = g.u - g.width / 2, b = g.u + g.width / 2, v = g.v, h = g.height, t = 0.05, ped = g.model === "pedestrian", tilt = g.model === "tilting";
5266
5341
  const sd = cz - v >= 0 ? 1 : -1;
5267
5342
  const pil = (x0, x1, high) => {
5268
5343
  bx("plaster", x0, 0, v - 0.13, x1, high, v + 0.13, wall);
@@ -5334,7 +5409,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
5334
5409
  if (!g.open || ped) shared.ctx.colliders.push({ type: "gate", f: -1, x0: a, z0: v - 0.1, x1: b, z1: v + 0.1 });
5335
5410
  }
5336
5411
  }
5337
- Object.assign(K, { slideDirection, gates });
5412
+ const defined = { slideDirection, gates };
5413
+ Object.assign(K, defined);
5338
5414
  }
5339
5415
 
5340
5416
  // src/roof/roofs.ts
@@ -5348,13 +5424,13 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
5348
5424
  function roofs() {
5349
5425
  const R = shared.ctx.r, F = R.facade, C = shared.ctx.colors, nA = R.floors.length, type = F.roof;
5350
5426
  cursor.group = "roof";
5351
- shared.ctx.roofRects = [];
5352
- shared.ctx.gutter = gutterConfig();
5353
- shared.ctx.gutter.ncond = 0;
5354
- shared.ctx.gutter.nbuz = 0;
5355
- shared.ctx.gutter.bz = /* @__PURE__ */ new Set();
5427
+ const roofRects = shared.ctx.roofRects = [];
5428
+ const G = shared.ctx.gutter = gutterConfig();
5429
+ G.ncond = 0;
5430
+ G.nbuz = 0;
5431
+ G.bz = /* @__PURE__ */ new Set();
5356
5432
  shared.ctx.gutterInfo = { gutters: [], downpipes: [], drains: [] };
5357
- const roofColor = C.roof != null ? C.roof : 11031610, gable = (R.seed | 0) % 2 === 0;
5433
+ const roofColor = C.roof != null ? C.roof : 11031610, gable = (Number(R.seed) | 0) % 2 === 0;
5358
5434
  for (let f = 0; f < nA; f++) {
5359
5435
  const rs = R.rooms.filter((c) => c.floor === f && c.type !== "void" && !c.outbuilding && !OPEN_AREA(c)).flatMap(rectsOf2), up = R.rooms.filter((c) => c.floor === f + 1 && c.type !== "void").flatMap(rectsOf2);
5360
5436
  if (!rs.length) continue;
@@ -5376,7 +5452,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
5376
5452
  return Y0(f) + Math.max(...cs.map((c) => c.ceiling)) + 0.2;
5377
5453
  };
5378
5454
  if (type === "slab") {
5379
- for (let j = 0; j < nz; j++) for (let i = 0; i < nx; i++) if (O(j, i)) shared.ctx.roofRects.push({ f, r: [xs[i], zs[j], xs[i + 1], zs[j + 1]], slab: true, yb: pe(xs[i], zs[j], xs[i + 1], zs[j + 1]) + 0.03 });
5455
+ for (let j = 0; j < nz; j++) for (let i = 0; i < nx; i++) if (O(j, i)) roofRects.push({ f, r: [xs[i], zs[j], xs[i + 1], zs[j + 1]], slab: true, yb: pe(xs[i], zs[j], xs[i + 1], zs[j + 1]) + 0.03 });
5380
5456
  slabs(f, xs, zs, O, U, nx, nz, full, pe, C);
5381
5457
  cursor.pc = null;
5382
5458
  continue;
@@ -5399,16 +5475,16 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
5399
5475
  if (U(q.j0 - 1, i)) touchesU[1] = 1;
5400
5476
  if (U(q.j1 + 1, i)) touchesU[3] = 1;
5401
5477
  }
5402
- shared.ctx.roofRects.push({ f, r: [x0, z0, x1, z1], touchesU });
5478
+ roofRects.push({ f, r: [x0, z0, x1, z1], touchesU });
5403
5479
  const ybq = pe(x0, z0, x1, z1);
5404
5480
  roofRect(type, x0, z0, x1, z1, ybq, roofColor, gable && single, F, touchesU);
5405
5481
  cursor.pc = null;
5406
- if (shared.ctx.gutter.on) gutter(type, f, x0, z0, x1, z1, ybq, touchesU);
5482
+ if (G.on) gutter(type, f, x0, z0, x1, z1, ybq, touchesU);
5407
5483
  }
5408
5484
  }
5409
5485
  for (const e of shared.ctx.outbuildingList || []) {
5410
5486
  const [x0, z0, x1, z1] = e.aabb, yb = Y0(0) + (e.ceiling || 2.6) + 0.2;
5411
- shared.ctx.roofRects.push({ f: 0, r: [x0, z0, x1, z1], outbuilding: e.id });
5487
+ roofRects.push({ f: 0, r: [x0, z0, x1, z1], outbuilding: e.id });
5412
5488
  if (type === "slab") {
5413
5489
  const c = C.facade != null ? C.facade : 14209216;
5414
5490
  bx("plaster", x0 - 0.08, yb, z0 - 0.08, x1 + 0.08, yb + 0.03, z1 + 0.08, sh(C.ceiling != null ? C.ceiling : 12104876, 0.85));
@@ -5419,13 +5495,15 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
5419
5495
  roofRect(type, x0, z0, x1, z1, yb, roofColor, true, F, [0, 0, 0, 0]);
5420
5496
  cursor.pc = null;
5421
5497
  shared.ctx.noPiece = false;
5422
- if (shared.ctx.gutter.on) gutter(type, 0, x0, z0, x1, z1, yb, [0, 0, 0, 0]);
5498
+ if (G.on) gutter(type, 0, x0, z0, x1, z1, yb, [0, 0, 0, 0]);
5423
5499
  }
5424
5500
  }
5425
- Object.assign(K, { roofs });
5501
+ const defined = { roofs };
5502
+ Object.assign(K, defined);
5426
5503
  }
5427
5504
 
5428
5505
  // src/roof/gutters.ts
5506
+ var paletteMeta = (colors) => colors.meta;
5429
5507
  function installRoofGutters(K, shared) {
5430
5508
  const { boxC, bx, cursor, roomAt } = K;
5431
5509
  const sh = (...a) => K.sh(...a);
@@ -5442,7 +5520,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
5442
5520
  const draw2 = rnd() < p, light = rnd() < 0.6;
5443
5521
  void draw2;
5444
5522
  const on = true;
5445
- return { on, rnd, color: high ? 3816770 : light ? 15527921 : 10857392, bad: (shared.ctx.colors.meta && shared.ctx.colors.meta.state) === "abandoned" || R.state === "abandoned", fallback: false };
5523
+ return { on, rnd, color: high ? 3816770 : light ? 15527921 : 10857392, bad: paletteMeta(shared.ctx.colors)?.state === "abandoned" || R.state === "abandoned", fallback: false };
5446
5524
  }
5447
5525
  function gutter(type, f, X0, Z0, X1, Z1, yb, touchesU) {
5448
5526
  const G = shared.ctx.gutter, o = type === "fiber_cement" ? 0.3 : 0.45, x0 = X0 - (touchesU[0] ? 0 : o), x1 = X1 + (touchesU[2] ? 0 : o), z0 = Z0 - (touchesU[1] ? 0 : o), z1 = Z1 + (touchesU[3] ? 0 : o), c = G.color, y = yb - 0.05;
@@ -5484,7 +5562,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
5484
5562
  const px = cx + sx * 0.06, pz = cz + sz * 0.06;
5485
5563
  if (!freeAt(px, pz, sx, sz)) return false;
5486
5564
  const floats = G.bad && G.ncond === 1, yt = yb - 0.1, ybx = floats ? 0.7 : 0.04;
5487
- G.ncond++;
5565
+ G.ncond = (G.ncond ?? 0) + 1;
5488
5566
  if (run2 >= 2) boxC("plain", (sides[run2][4] + px) / 2, yt, pz, Math.abs(sides[run2][4] - px), 0.07, 0.07, c);
5489
5567
  else boxC("plain", px, yt, (sides[run2][4] + pz) / 2, 0.07, 0.07, Math.abs(sides[run2][4] - pz), c);
5490
5568
  boxC("plain", px, (ybx + yt) / 2, pz, 0.08, yt - ybx, 0.08, c);
@@ -5518,7 +5596,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
5518
5596
  function drain(x, z, px, pz, c) {
5519
5597
  const g0 = cursor.group;
5520
5598
  cursor.group = "f0";
5521
- if (px != null) boxC("plain", (px + x) / 2, 0.075, (pz + z) / 2, Math.abs(x - px) + 0.05, 0.05, Math.abs(z - pz) + 0.05, sh(c, 0.85));
5599
+ if (px != null && pz != null && c != null) boxC("plain", (px + x) / 2, 0.075, (pz + z) / 2, Math.abs(x - px) + 0.05, 0.05, Math.abs(z - pz) + 0.05, sh(c, 0.85));
5522
5600
  const t = 0.045;
5523
5601
  bx("plain", x - 0.12, 0, z - 0.12, x + 0.12, t - 6e-3, z + 0.12, 12170668);
5524
5602
  bx("metal", x - 0.11, t, z - 0.11, x + 0.11, t + 0.012, z + 0.11, 5922145);
@@ -5529,7 +5607,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
5529
5607
  bx("metal", x - 0.085, t + 0.012, z - 6e-3, x + 0.085, t + 0.02, z + 6e-3, 9080466);
5530
5608
  cursor.group = g0;
5531
5609
  }
5532
- Object.assign(K, { gutterConfig, gutter, drain });
5610
+ const defined = { gutterConfig, gutter, drain };
5611
+ Object.assign(K, defined);
5533
5612
  }
5534
5613
 
5535
5614
  // src/roof/slabs.ts
@@ -5563,15 +5642,16 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
5563
5642
  side(free(j, i + 1), x1 + o - t, z0 - ez0, x1 + o, z1 + ez1);
5564
5643
  side(free(j - 1, i), x0 - ex0, z0 - o, x1 + ex1, z0 - o + t);
5565
5644
  side(free(j + 1, i), x0 - ex0, z1 + o - t, x1 + ex1, z1 + o);
5566
- if (shared.ctx.gutter && shared.ctx.gutter.on) for (const [ok, dx, dz] of [[free(j - 1, i), 0, -1], [free(j + 1, i), 0, 1], [free(j, i - 1), -1, 0], [free(j, i + 1), 1, 0]]) {
5645
+ const G = shared.ctx.gutter;
5646
+ if (G && G.on && G.bz) for (const [ok, dx, dz] of [[free(j - 1, i), 0, -1], [free(j + 1, i), 0, 1], [free(j, i - 1), -1, 0], [free(j, i + 1), 1, 0]]) {
5567
5647
  const kd = f + ":" + dx + ":" + dz;
5568
- if (!ok || shared.ctx.gutter.bz.has(kd)) continue;
5569
- shared.ctx.gutter.bz.add(kd);
5570
- shared.ctx.gutter.nbuz++;
5571
- const mx = (x0 + x1) / 2, mz = (z0 + z1) / 2, ex = dx ? dx < 0 ? x0 - o : x1 + o : mx, ez = dz ? dz < 0 ? z0 - o : z1 + o : mz, cc = shared.ctx.gutter.color;
5648
+ if (!ok || G.bz.has(kd)) continue;
5649
+ G.bz.add(kd);
5650
+ G.nbuz = (G.nbuz ?? 0) + 1;
5651
+ const mx = (x0 + x1) / 2, mz = (z0 + z1) / 2, ex = dx ? dx < 0 ? x0 - o : x1 + o : mx, ez = dz ? dz < 0 ? z0 - o : z1 + o : mz, cc = G.color;
5572
5652
  boxC("plain", ex + dx * 0.15, yb + 0.1, ez + dz * 0.15, dx ? 0.42 : 0.14, 0.1, dz ? 0.42 : 0.14, cc);
5573
5653
  boxC("plain", ex + dx * 0.36, yb + 0.1, ez + dz * 0.36, dx ? 0.04 : 0.18, 0.14, dz ? 0.04 : 0.18, sh(cc, 0.8));
5574
- if (shared.ctx.gutter.bad) boxC("plain", ex + dx * 0.05, yb + 0.5 * 0, ez + dz * 0.05, dx ? 0.06 : 0.3, 0.02, dz ? 0.06 : 0.3, 4868674);
5654
+ if (G.bad) boxC("plain", ex + dx * 0.05, yb + 0.5 * 0, ez + dz * 0.05, dx ? 0.06 : 0.3, 0.02, dz ? 0.06 : 0.3, 4868674);
5575
5655
  {
5576
5656
  const tx = ex + dx * 0.38, tz = ez + dz * 0.38;
5577
5657
  let lv = true;
@@ -5591,7 +5671,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
5591
5671
  }
5592
5672
  }
5593
5673
  }
5594
- Object.assign(K, { slabs });
5674
+ const defined = { slabs };
5675
+ Object.assign(K, defined);
5595
5676
  }
5596
5677
 
5597
5678
  // src/roof/tiles.ts
@@ -5724,7 +5805,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
5724
5805
  }
5725
5806
  return m ? structurePiece(m.id) : null;
5726
5807
  }
5727
- Object.assign(K, { R_STYLE, tileRelief, ridge, pieceOfPane });
5808
+ const defined = { R_STYLE, tileRelief, ridge, pieceOfPane };
5809
+ Object.assign(K, defined);
5728
5810
  }
5729
5811
 
5730
5812
  // src/roof/rect.ts
@@ -5770,7 +5852,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
5770
5852
  }
5771
5853
  const rm = alongX ? dz / 2 : dx / 2;
5772
5854
  {
5773
- const rr = shared.ctx.roofRects[shared.ctx.roofRects.length - 1];
5855
+ const roofRects = shared.ctx.roofRects, rr = roofRects[roofRects.length - 1];
5774
5856
  if (rr && !rr.yb) Object.assign(rr, { yb, tp, gable: !!gable, alongX, exterior: [x0, z0, x1, z1], rm });
5775
5857
  }
5776
5858
  if (gable) {
@@ -5807,7 +5889,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
5807
5889
  }
5808
5890
  }
5809
5891
  }
5810
- Object.assign(K, { roofRect });
5892
+ const defined = { roofRect };
5893
+ Object.assign(K, defined);
5811
5894
  }
5812
5895
 
5813
5896
  // src/mirrors.ts
@@ -5818,9 +5901,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
5818
5901
  function mirrorEnv() {
5819
5902
  if (shared.mirrorEnv) return shared.mirrorEnv;
5820
5903
  const mk = (f) => {
5904
+ if (!DOC) throw new Error("game-floorplan-three: the mirrors need a document (options.document)");
5821
5905
  const c = DOC.createElement("canvas");
5822
5906
  c.width = c.height = 64;
5823
5907
  const g = c.getContext("2d");
5908
+ if (!g) throw new Error("game-floorplan-three: no 2d canvas context");
5824
5909
  f(g);
5825
5910
  return c;
5826
5911
  };
@@ -5879,7 +5964,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
5879
5964
  B.N.push(v.x, v.y, v.z);
5880
5965
  B.U.push(ua.getX(i), ua.getY(i));
5881
5966
  }
5882
- for (const k of geo.index.array) B.I.push(B.nv + k);
5967
+ for (const k of geo.index?.array ?? []) B.I.push(B.nv + k);
5883
5968
  B.nv += pa.count;
5884
5969
  geo.dispose();
5885
5970
  }
@@ -5957,7 +6042,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
5957
6042
  m.fake.visible = !on;
5958
6043
  }
5959
6044
  }
5960
- Object.assign(K, { mirrorEnv, newMirrorState, assembleMirrors, floorAtY, chooseMirror });
6045
+ const defined = { mirrorEnv, newMirrorState, assembleMirrors, floorAtY, chooseMirror };
6046
+ Object.assign(K, defined);
5961
6047
  }
5962
6048
 
5963
6049
  // src/cars.ts
@@ -5970,13 +6056,16 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
5970
6056
  high: ["electric_hatch", "hybrid_suv", "midsize_suv", "v8_coupe", "large_suv", "midsize_pickup", "crossover_pickup", "fastback_sedan"]
5971
6057
  }, K.cfg.carKinds);
5972
6058
  const CAR_CACHE = /* @__PURE__ */ new Map();
5973
- function materialSignature(m) {
6059
+ function materialSignature(material) {
6060
+ const m = material;
5974
6061
  const f = (v) => v == null ? "" : typeof v === "number" ? v.toFixed(3) : v.isColor ? v.getHexString() : v.uuid || String(v);
5975
6062
  return [m.type, f(m.color), f(m.roughness), f(m.metalness), f(m.opacity), m.transparent ? 1 : 0, m.side, f(m.map), f(m.emissive), f(m.emissiveIntensity), m.vertexColors ? 1 : 0, m.flatShading ? 1 : 0, f(m.envMapIntensity), f(m.alphaTest)].join("|");
5976
6063
  }
5977
6064
  const BUCKETS = /* @__PURE__ */ new Map();
5978
- function bucketMaterial(m) {
5979
- if (m.type !== "MeshStandardMaterial" || m.map || m.alphaTest || m.vertexColors || !m.color) return null;
6065
+ function bucketMaterial(material) {
6066
+ if (material.type !== "MeshStandardMaterial") return null;
6067
+ const m = material;
6068
+ if (m.map || m.alphaTest || m.vertexColors || !m.color) return null;
5980
6069
  const mt = m.metalness > 0.5 ? 2 : m.metalness > 0.2 ? 1 : 0, rg = m.roughness < 0.3 ? 0 : m.roughness < 0.6 ? 1 : 2, tr = m.transparent || m.opacity < 1, op = tr ? Math.max(0.2, Math.min(0.9, Math.round(m.opacity * 5) / 5)) : 1, sig = "bucket" + mt + rg + "s" + m.side + (tr ? "t" + op : "");
5981
6070
  let b = BUCKETS.get(sig);
5982
6071
  if (!b) {
@@ -5988,7 +6077,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
5988
6077
  }
5989
6078
  function carModel(k, seed) {
5990
6079
  const kc = k + "|" + shared.quality + "|" + (seed || 0);
5991
- if (CAR_CACHE.has(kc)) return CAR_CACHE.get(kc);
6080
+ if (CAR_CACHE.has(kc)) return CAR_CACHE.get(kc) ?? null;
5992
6081
  if (!FLEET) return null;
5993
6082
  let res = null;
5994
6083
  try {
@@ -5996,8 +6085,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
5996
6085
  c.root.updateMatrixWorld(true);
5997
6086
  const inv = new THREE.Matrix4();
5998
6087
  const byMaterial = /* @__PURE__ */ new Map(), tmp = new THREE.Matrix4(), v = new THREE.Vector3(), nm = new THREE.Matrix3(), bb = new THREE.Box3();
5999
- c.g.traverse((o) => {
6000
- if (!o.isMesh || o.isInstancedMesh || o.isSkinnedMesh || !o.geometry || !o.geometry.attributes.position) return;
6088
+ c.g.traverse((node) => {
6089
+ if (!isMesh(node)) return;
6090
+ const o = node;
6091
+ if (o.isInstancedMesh || o.isSkinnedMesh || !o.geometry || !o.geometry.attributes.position) return;
6001
6092
  for (let p = o; p; p = p.parent) if (p.visible === false) return;
6002
6093
  const mats = Array.isArray(o.material) ? o.material : [o.material];
6003
6094
  let geo = o.geometry;
@@ -6011,7 +6102,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
6011
6102
  const pa = geo.attributes.position, na = geo.attributes.normal, ua = geo.attributes.uv, total = pa.count;
6012
6103
  const grs = Array.isArray(o.material) && o.geometry.groups.length ? o.geometry.groups : [{ start: 0, count: total, materialIndex: 0 }];
6013
6104
  for (const gr of grs) {
6014
- const m = mats[gr.materialIndex] || mats[0];
6105
+ const m = (gr.materialIndex != null ? mats[gr.materialIndex] : void 0) || mats[0];
6015
6106
  if (!m) continue;
6016
6107
  const a0 = Math.min(gr.start, total), n0 = Math.min(gr.count, total - a0) - Math.min(gr.count, total - a0) % 3;
6017
6108
  if (n0 < 3) continue;
@@ -6112,7 +6203,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
6112
6203
  return g;
6113
6204
  }
6114
6205
  function assembleCars(groups) {
6115
- if (shared.quality === "game") return assembleMergedCars(groups);
6206
+ if (shared.quality === "game") {
6207
+ assembleMergedCars(groups);
6208
+ return;
6209
+ }
6116
6210
  for (const C of CARS) {
6117
6211
  const g = carGroup(C.k, C.w, C.d, C.seed);
6118
6212
  if (!g) continue;
@@ -6163,7 +6257,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
6163
6257
  (groups[B.parent] || (groups[B.parent] = new THREE.Group())).add(m);
6164
6258
  }
6165
6259
  }
6166
- Object.assign(K, { CARS, CAR_BY_STYLE, CAR_CACHE, materialSignature, BUCKETS, bucketMaterial, carModel, carGroup, assembleCars, assembleMergedCars });
6260
+ const defined = { CARS, CAR_BY_STYLE, CAR_CACHE, materialSignature, BUCKETS, bucketMaterial, carModel, carGroup, assembleCars, assembleMergedCars };
6261
+ Object.assign(K, defined);
6167
6262
  }
6168
6263
 
6169
6264
  // src/house/building.ts
@@ -6219,14 +6314,15 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
6219
6314
  bx("plaster", Math.min(hall[0] - 3, color2[0]), ytop, Math.min(un[1] ? unitBox(un[1])[1] : 0, el[1]), Math.max(color2[2], un0[2]), ytop + 0.2, Math.max(un0[3], un[1] ? unitBox(un[1])[3] : 0), 11841704);
6220
6315
  shared.ctx.limits = { x0: Math.min(hall[0] - 3, color2[0]), z0: Math.min(el[1], un[1] ? unitBox(un[1])[1] : 0), x1: Math.max(color2[2], un0[2]), z1: Math.max(un0[3], un[1] ? unitBox(un[1])[3] : 0) };
6221
6316
  }
6222
- Object.assign(K, { building });
6317
+ const defined = { building };
6318
+ Object.assign(K, defined);
6223
6319
  }
6224
6320
 
6225
6321
  // src/utilities/water.ts
6226
6322
  function installUtilitiesWater(K, shared) {
6227
6323
  const { THREE, _Y, _q, cursor, cylBase, part, pieceRecord, put } = K;
6228
6324
  function createWater(r, state) {
6229
- const H = r.utilities.plumbing, cap = H ? +H.capacityL || 0 : 0, ls = { dry: [], full: [] }, points = {}, itemPoint = {};
6325
+ const H = r.utilities?.plumbing, cap = H ? +H.capacityL || 0 : 0, ls = { dry: [], full: [] }, points = {}, itemPoint = {};
6230
6326
  if (H) for (const p of H.points) {
6231
6327
  points[p.id] = p;
6232
6328
  itemPoint[p.item] = p.id;
@@ -6315,14 +6411,14 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
6315
6411
  }
6316
6412
  const _v3 = new THREE.Vector3();
6317
6413
  function waterPipes() {
6318
- const H = shared.ctx.r.utilities.plumbing;
6414
+ const H = shared.ctx.r.utilities?.plumbing;
6319
6415
  if (!H || !H.visibleRuns) return;
6320
6416
  const nA = shared.ctx.r.floors.length;
6321
6417
  const groupOf = (c) => c && c.floor >= 0 ? "f" + Math.min(c.floor, nA - 1) : "lot", xyz = (p) => [p[0], p[2], p[1]];
6322
6418
  for (const tr of H.visibleRuns) {
6323
6419
  const pts = tr.points.map(xyz), d = tr.diameter || 0.025;
6324
6420
  if (tr.exposed === "wall") {
6325
- const pt = H.points.find((p) => p.run === tr.id), com = shared.ctx.roomById[tr.to], g2 = groupOf(com || pt && { floor: pt.floor });
6421
+ const pt = H.points.find((p) => p.run === tr.id), com = tr.to != null ? shared.ctx.roomById[tr.to] : void 0, g2 = groupOf(com || pt && { floor: pt.floor });
6326
6422
  part("wall_pipe", [0, 0, 0], () => {
6327
6423
  for (let i = 0; i + 1 < pts.length; i++) pipe(pts[i], pts[i + 1], d, PIPE_COLOR);
6328
6424
  for (const p of pts) cylBase("plain", p[0], p[1] - d, p[2], d * 0.7, d * 2, PIPE_COLOR);
@@ -6344,7 +6440,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
6344
6440
  cursor.pc = null;
6345
6441
  }
6346
6442
  }
6347
- Object.assign(K, { createWater, PIPE_COLOR, pipe, _v3, waterPipes });
6443
+ const defined = { createWater, PIPE_COLOR, pipe, _v3, waterPipes };
6444
+ Object.assign(K, defined);
6348
6445
  }
6349
6446
 
6350
6447
  // src/utilities/electrical.ts
@@ -6352,10 +6449,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
6352
6449
  const { _Y, _q, _v3, bx, cursor, cylBase, pieceRecord, put } = K;
6353
6450
  const sh = (...a) => K.sh(...a);
6354
6451
  function electricalStructure() {
6355
- const E = shared.ctx.r.utilities.electrical;
6452
+ const R = shared.ctx.r, E = R.utilities?.electrical;
6356
6453
  shared.ctx.electricalInfo = null;
6357
6454
  if (!E) return;
6358
- const R = shared.ctx.r, nA = R.floors.length, lotGroup = R.lot ? "lot" : "f0", concreteColor = 11973547, iron = 4869200, wireRun = 1842206, info = { pole: null, meter: null, panel: null, conduits: [], outlets: [], switches: [], circuits: E.circuits || [], exposed: !!E.exposed };
6455
+ const nA = R.floors.length, lotGroup = R.lot ? "lot" : "f0", concreteColor = 11973547, iron = 4869200, wireRun = 1842206, info = { pole: null, meter: null, panel: null, conduits: [], outlets: [], switches: [], circuits: E.circuits || [], exposed: !!E.exposed };
6359
6456
  const en3 = E.serviceEntrance, KV = shared.ctx.iv ? shared.ctx.iv.plane : null;
6360
6457
  const wireL = (a, b, r_ = 8e-3) => {
6361
6458
  const L = Math.hypot(b[0] - a[0], b[1] - a[1], b[2] - a[2]);
@@ -6460,7 +6557,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
6460
6557
  _q.setFromUnitVectors(_Y, _v3.set(dx / L, dy / L, dz / L));
6461
6558
  put("plain", "cyl", (a[0] + b[0]) / 2, (a[1] + b[1]) / 2, (a[2] + b[2]) / 2, _q, diameter, L, diameter, color2);
6462
6559
  }
6463
- Object.assign(K, { electricalStructure, pipe2 });
6560
+ const defined = { electricalStructure, pipe2 };
6561
+ Object.assign(K, defined);
6464
6562
  }
6465
6563
 
6466
6564
  // src/house/baseboard.ts
@@ -6498,7 +6596,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
6498
6596
  strip(W, y0, y0 + 0.045, 0, W, "sidewalk", cal);
6499
6597
  }
6500
6598
  }
6501
- Object.assign(K, { baseboardAndSidewalk });
6599
+ const defined = { baseboardAndSidewalk };
6600
+ Object.assign(K, defined);
6502
6601
  }
6503
6602
 
6504
6603
  // src/utilities/solar.ts
@@ -6513,6 +6612,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
6513
6612
  if (u >= a - 0.05 && u <= c + 0.05 && v >= b - 0.05 && v <= d + 0.05) y = Math.max(y == null ? -1e9 : y, rr.yb);
6514
6613
  continue;
6515
6614
  }
6615
+ if (!rr.exterior || rr.tp == null || rr.rm == null) continue;
6516
6616
  const [x0, z0, x1, z1] = rr.exterior;
6517
6617
  if (u < x0 || u > x1 || v < z0 || v > z1) continue;
6518
6618
  const dz = Math.min(v - z0, z1 - v), dx = Math.min(u - x0, x1 - u), dd = rr.gable ? rr.alongX ? dz : dx : Math.min(dx, dz), h = rr.yb + rr.tp * Math.min(dd, rr.rm);
@@ -6521,7 +6621,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
6521
6621
  return y;
6522
6622
  }
6523
6623
  function solar() {
6524
- const R = shared.ctx.r, S = R.utilities.solar;
6624
+ const R = shared.ctx.r, S = R.utilities?.solar;
6525
6625
  shared.ctx.solarInfo = null;
6526
6626
  if (!S || !S.panels || !S.panels.length) return;
6527
6627
  cursor.group = "roof";
@@ -6564,13 +6664,14 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
6564
6664
  bx("plain", iv.u - W * 0.6, y0 + 0.05, iv.v + fa0, iv.u + W * 0.6, y0 + 0.17, iv.v + fa1, 2106924);
6565
6665
  bx("emissive", iv.u - 0.03, y0 - 0.12, iv.v + fa0, iv.u + 0.03, y0 - 0.09, iv.v + fa1, 6479994);
6566
6666
  }
6567
- const top = Math.max(...(shared.ctx.roofRects || []).filter((t2) => t2.yb != null).map((t2) => t2.yb), Y0(fa) + 2.8);
6667
+ const top = Math.max(...(shared.ctx.roofRects || []).flatMap((t2) => t2.yb != null ? [t2.yb] : []), Y0(fa) + 2.8);
6568
6668
  cylBase("plain", iv.u, y0 + h / 2, iv.v, 0.016, Math.max(0.2, top - 0.2 - (y0 + h / 2)), 15262936);
6569
6669
  info.inverter = { id: iv.id, pos: [iv.u, y0, iv.v], side: iv.side, link: iv.link };
6570
6670
  }
6571
6671
  shared.ctx.solarInfo = info;
6572
6672
  }
6573
- Object.assign(K, { roofHeightAt, solar });
6673
+ const defined = { roofHeightAt, solar };
6674
+ Object.assign(K, defined);
6574
6675
  }
6575
6676
 
6576
6677
  // src/house/outbuildings.ts
@@ -6585,7 +6686,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
6585
6686
  counts.set(w.floor, i + 1);
6586
6687
  return w.id ? w : Object.assign({}, w, { id: `wall_${w.floor}_${i}` });
6587
6688
  }) });
6588
- const E = (r.outbuildings || []).filter((e) => e && e.walls && e.walls.length && e.interior && e.interior.length);
6689
+ const E = (r.outbuildings || []).filter((e) => !!(e && e.walls && e.walls.length && e.interior && e.interior.length));
6589
6690
  if (!E.length) return r;
6590
6691
  const rooms = r.rooms.slice(), walls = r.walls.slice(), doors = r.doors.slice(), windows = r.windows.slice(), items = r.items.filter((m) => m.type !== "outbuilding");
6591
6692
  for (const e of E) {
@@ -6599,7 +6700,22 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
6599
6700
  const alongU = p.axis === "u", below = p.pos < (alongU ? cz : cx);
6600
6701
  doors.push({ id: p.id, floor: 0, u: p.u, v: p.v, axis: p.axis, pos: p.pos, at: p.at, width: p.width, a: below ? "outside" : e.id, b: below ? e.id : "outside", opensTo: e.id, exterior: true, side: p.side, leaves: 1, model: "single", hinge: "start", zones: [], blocked: false, outbuilding: e.id });
6601
6702
  }
6602
- for (const j of e.windows || []) windows.push({ id: j.id, floor: 0, u: j.u, v: j.v, axis: j.axis, pos: j.pos, at: j.at, width: j.width, y0: j.y0, y1: j.y1, room: e.id, side: j.side, outbuilding: e.id });
6703
+ for (const j of e.windows || []) windows.push({
6704
+ id: j.id,
6705
+ floor: 0,
6706
+ u: j.u,
6707
+ v: j.v,
6708
+ axis: j.axis,
6709
+ pos: j.pos,
6710
+ at: j.at,
6711
+ width: j.width,
6712
+ y0: j.y0 ?? 1,
6713
+ y1: j.y1 ?? 2,
6714
+ /* the structure always gives a window its sill and head (1.0, 2.0) */
6715
+ room: e.id,
6716
+ side: j.side,
6717
+ outbuilding: e.id
6718
+ });
6603
6719
  (e.items || []).forEach((m, i) => items.push({ id: e.id + "." + m.type + i, type: m.type, category: m.category, x: m.u, z: m.v, ry: m.ry || 0, w: m.w, d: m.d, h: m.h, y: 0.04, frontU: m.frontU, frontV: m.frontV, room: e.id, floor: 0, outdoor: false, aabb: m.aabb, color: "wood" }));
6604
6720
  items.push({ id: e.id + ".ceiling_light", type: "ceiling_light", category: "ceiling_light", x: cx, z: cz, ry: 0, w: 0.35, d: 0.35, h: 0.1, y: pe - 0.1, room: e.id, floor: 0, outdoor: false, assembly: "ceiling", color: "metal", decor: true });
6605
6721
  }
@@ -6620,7 +6736,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
6620
6736
  }
6621
6737
  }
6622
6738
  }
6623
- Object.assign(K, { OUTBUILDING_USE, applyOutbuildings, outbuildings });
6739
+ const defined = { OUTBUILDING_USE, applyOutbuildings, outbuildings };
6740
+ Object.assign(K, defined);
6624
6741
  }
6625
6742
 
6626
6743
  // src/furniture/api.ts
@@ -6685,7 +6802,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
6685
6802
  return { b: (mat, x, y, z, w, h, d, color2, ry, rx, rz) => A.box(M, mat, x, y0 + y, z, w, h, d, color2, ry || 0, rx || 0, rz || 0), c: (mat, x, y, z, r, h, color2, rx, rz, seg) => A.cylinder(M, mat, x, y0 + y, z, r, h, color2, rx || 0, rz || 0, seg), rnd: rngOf(M), wood: paletteColor(M, "wood", 8016948), met: paletteColor(M, "metal", 10133154) };
6686
6803
  }
6687
6804
  const PE = Math.PI / 2;
6688
- Object.assign(K, { MATERIAL_MAP: MATERIAL_MAP2, mm, LIGHTS, makeApi, sh, paletteColor, rngOf, kk, PE });
6805
+ const defined = { MATERIAL_MAP: MATERIAL_MAP2, mm, LIGHTS, makeApi, sh, paletteColor, rngOf, kk, PE };
6806
+ Object.assign(K, defined);
6689
6807
  }
6690
6808
 
6691
6809
  // src/furniture/builders.ts
@@ -6813,7 +6931,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
6813
6931
  for (let s = 0; s < np; s++) lb("fabric", np === 1 ? 0 : (s ? 1 : -1) * w * 0.23, 0.5, -d / 2 + 0.3, w * (np === 1 ? 0.6 : 0.4), 0.12, 0.34, 15789798, (s ? 1 : -1) * 0.08);
6814
6932
  },
6815
6933
  wardrobe(M, A) {
6816
- const { b: lb } = kk(M, A), w = M.w, d = M.d, h = M.h, wood = M.color != null && false ? M.color : paletteColor(M, "wood", 5980208), np = Math.max(2, Math.round(w / 0.7));
6934
+ const { b: lb } = kk(M, A), w = M.w, d = M.d, h = M.h, wood = paletteColor(M, "wood", 5980208), np = Math.max(2, Math.round(w / 0.7));
6817
6935
  lb("wood", 0, 0.06, 0, w, h - 0.06, d, wood);
6818
6936
  lb("wood", 0, h, 0, w + 0.04, 0.04, d + 0.04, sh(wood, 0.8));
6819
6937
  lb("wood", 0, 0, 0, w - 0.04, 0.06, d - 0.04, sh(wood, 0.4));
@@ -7205,7 +7323,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
7205
7323
  lb("metal", 0, h - 0.04, d / 2, w, 0.03, 0.03, 3815994);
7206
7324
  }
7207
7325
  };
7208
- Object.assign(K, { FURNITURE });
7326
+ const defined = { FURNITURE };
7327
+ Object.assign(K, defined);
7209
7328
  }
7210
7329
 
7211
7330
  // src/furniture/type-table.ts
@@ -7215,7 +7334,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
7215
7334
  function typeTable(props) {
7216
7335
  const B = props.builders, T2 = {};
7217
7336
  const mv = (type, id) => {
7218
- if (B[id || type]) T2[type] = { f: B[id || type], source: "props3d" };
7337
+ const f = B[id || type];
7338
+ if (f) T2[type] = { f, source: "props3d" };
7219
7339
  };
7220
7340
  const mine = (type, f, source = "game") => {
7221
7341
  T2[type] = { f, source };
@@ -7354,7 +7474,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
7354
7474
  extraTypes(T2, props);
7355
7475
  return T2;
7356
7476
  }
7357
- Object.assign(K, { typeTable });
7477
+ const defined = { typeTable };
7478
+ Object.assign(K, defined);
7358
7479
  }
7359
7480
 
7360
7481
  // src/furniture/taps.ts
@@ -7418,7 +7539,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
7418
7539
  }, { id: id + "_a", target: id, hidden: true, fluid: "water" });
7419
7540
  return id;
7420
7541
  }
7421
- Object.assign(K, { counterTap, wallTap });
7542
+ const defined = { counterTap, wallTap };
7543
+ Object.assign(K, defined);
7422
7544
  }
7423
7545
 
7424
7546
  // src/furniture/fixtures.ts
@@ -7531,7 +7653,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
7531
7653
  const a = rnd() * 6.283, u = Math.sqrt(rnd()), ph = (rnd() - 0.2) * 1.2, dx = Math.cos(a) * u * R * 0.75, dz = Math.sin(a) * u * R * 0.75, dy = (o.flatten ? 0.35 : 0.55) * R * (Math.sin(ph) * 0.5 + 0.5) * 1.5 - R * 0.15;
7532
7654
  const r = R * (0.34 + rnd() * 0.22) * (1 - u * 0.25), alt = r * 2 * (o.flatten ? 0.75 : 0.95), f = 0.72 + Math.min(1, (dy + R * 0.2) / (R * 1.2)) * 0.32 + rnd() * 0.12;
7533
7655
  K2.e("plant", cx + dx, cy + dy + r * 0.5, cz + dz, r, alt, sh(green2, f));
7534
- if (o.flower && rnd() < o.flower) {
7656
+ if (o.flower && o.flowers && rnd() < o.flower) {
7535
7657
  const fa = rnd() * 6.283;
7536
7658
  K2.e("plain", cx + dx + Math.cos(fa) * r * 0.78, cy + dy + r * 0.5 + rnd() * r * 0.5, cz + dz + Math.sin(fa) * r * 0.78, 0.028 + rnd() * 0.012, 0.05, o.flowers[rnd() * o.flowers.length | 0]);
7537
7659
  }
@@ -7644,7 +7766,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
7644
7766
  else blobs(K2, x, bh - 0.05, z, 0.24 + rnd() * 0.08, 6, green2, rnd, { flatten: true, flower: tp === "flower" ? 0.9 : 0.25, flowers: FLOWERS.slice(i % 3, i % 3 + 3) });
7645
7767
  }
7646
7768
  }
7647
- Object.assign(K, { GREENS: GREENS2, FLOWERS, blobs, potModel, arcLeaf, tuft, palm_tree, treeModel, shrubModel, potPlant, tablePot, planterModel });
7769
+ const defined = { GREENS: GREENS2, FLOWERS, blobs, potModel, arcLeaf, tuft, palm_tree, treeModel, shrubModel, potPlant, tablePot, planterModel };
7770
+ Object.assign(K, defined);
7648
7771
  }
7649
7772
 
7650
7773
  // src/furniture/yard.ts
@@ -7800,7 +7923,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
7800
7923
  };
7801
7924
  FURNITURE.mailbox = function(M, A) {
7802
7925
  const K2 = kit2(M, A, 0), w = M.w || 0.3, d = M.d || 0.22, h = M.h || 0.3, y = M.y != null && M.y > 0 ? M.y : 1, structure = STYLE(), pc = structure === "high" ? 2763824 : structure === "mid" ? 13676640 : 13126202, rnd = rngString(SEED() + "|" + seedKey(M.seed) + "|correio");
7803
- if (M.pole !== false && !(M.y > 0)) {
7926
+ if (M.pole !== false && !(M.y != null && M.y > 0)) {
7804
7927
  K2.b("plain", 0, 0, -d / 2 - 0.1, 0.12, y, 0.12, 12104874);
7805
7928
  }
7806
7929
  K2.b("metal", 0, y, 0, w, h * 0.78, d, pc);
@@ -7820,7 +7943,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
7820
7943
  );
7821
7944
  K2.b("plain", 0, y + h * 0.62, d / 2 + 4e-3, 0.08, 0.05, 4e-3, 15921386);
7822
7945
  };
7823
- Object.assign(K, { CLOTHES_COLORS, strip, garment });
7946
+ const defined = { CLOTHES_COLORS, strip, garment };
7947
+ Object.assign(K, defined);
7824
7948
  }
7825
7949
 
7826
7950
  // src/furniture/extra-types.ts
@@ -7880,7 +8004,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
7880
8004
  tuft(K2, 0, 0, 0, Math.max(0.2, (M.w || 0.4) / 2), 16, 5212738, rnd);
7881
8005
  });
7882
8006
  }
7883
- Object.assign(K, { extraTypes });
8007
+ const defined = { extraTypes };
8008
+ Object.assign(K, defined);
7884
8009
  }
7885
8010
 
7886
8011
  // src/furniture/place-items.ts
@@ -7902,8 +8027,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
7902
8027
  if (m.type === "stairs") continue;
7903
8028
  const exterior = m.outdoor || m.floor < 0, f = exterior ? -1 : Math.min(m.floor, R.floors.length - 1);
7904
8029
  cursor.group = exterior ? "lot" : "f" + f;
7905
- const com = shared.ctx.roomById[m.room], base = exterior ? 0 : Y0(f);
7906
- const M = { x: m.x, z: m.z, ry: m.ry || 0, w: m.w, d: m.d, h: m.h, y: m.y || 0, base, floor: exterior ? -1 : f, palette: shared.ctx.colors, color: m.category === "ground_cover" ? void 0 : colorOf(m.color) != null ? colorOf(m.color) : void 0, seed: m.id, room: m.room, typeWith: com ? com.type : null, ceiling: com ? com.ceiling : 2.9, on: shared.ctx.night, light: shared.ctx.night, lit: shared.ctx.night, open: !!m.doorOpen, overhead: m.overhead, assembly: m.assembly, hose: m.hose };
8030
+ const com = m.room != null ? shared.ctx.roomById[m.room] : void 0, base = exterior ? 0 : Y0(f);
8031
+ const M = { x: m.x, z: m.z, ry: m.ry || 0, w: m.w, d: m.d, h: m.h, y: m.y || 0, base, floor: exterior ? -1 : f, palette: shared.ctx.colors, color: m.category === "ground_cover" ? void 0 : colorOf(m.color) ?? void 0, seed: m.id, room: m.room, typeWith: com ? com.type : null, ceiling: com ? com.ceiling : 2.9, on: shared.ctx.night, light: shared.ctx.night, lit: shared.ctx.night, open: !!m.doorOpen, overhead: m.overhead, assembly: m.assembly, hose: m.hose };
7907
8032
  if (m.category === "ground_cover" && m.polygon) {
7908
8033
  const xs = m.polygon.map((p) => p[0]), zs = m.polygon.map((p) => p[1]);
7909
8034
  M.x = (Math.min(...xs) + Math.max(...xs)) / 2;
@@ -7980,7 +8105,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
7980
8105
  boxC("plain", m.x, M.base + (m.y || 0) + h / 2, m.z, m.w, h, m.d, color2, 0, m.ry || 0, 0);
7981
8106
  shared.ctx.labels.push({ pos: new THREE.Vector3(m.x, M.base + (m.y || 0) + h + 0.15, m.z), text: m.type, group, type: "no_model" });
7982
8107
  }
7983
- Object.assign(K, { houseItems, waterTankOfHouse, hashColor, labeledBox });
8108
+ const defined = { houseItems, waterTankOfHouse, hashColor, labeledBox };
8109
+ Object.assign(K, defined);
7984
8110
  }
7985
8111
 
7986
8112
  // src/vegetation/bridge.ts
@@ -8015,7 +8141,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8015
8141
  const ids = VEG_MAP[m.type], can = VEG_PLANTER[m.type];
8016
8142
  if (!ids && !can) return false;
8017
8143
  const rnd = rngString(SEED() + "|" + seedKey(m.id) + "|veg"), pick2 = (l) => l[Math.min(l.length - 1, rnd() * l.length | 0)], ok = (id) => {
8018
- if (V.ok.has(id)) return V.ok.get(id);
8144
+ if (V.ok.has(id)) return V.ok.get(id) === true;
8019
8145
  let r = true;
8020
8146
  try {
8021
8147
  V.S.toSpec(id);
@@ -8061,7 +8187,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8061
8187
  obj.position.y = G.y;
8062
8188
  obj.name = "vegetation";
8063
8189
  obj.traverse((o) => {
8064
- if (o.isMesh) {
8190
+ if (isMesh(o)) {
8065
8191
  o.castShadow = true;
8066
8192
  o.receiveShadow = true;
8067
8193
  o.name = "veg/" + o.name;
@@ -8076,7 +8202,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8076
8202
  }
8077
8203
  shared.ctx.vegInfo = { plants: pl, triangles: tri, colliders: col, lodCounts: lod, materials: mats };
8078
8204
  }
8079
- Object.assign(K, { VEG_POT, VEG_CACTUS, VEG_MAP, VEG_PLANTER, vegItem, assembleVegetation });
8205
+ const defined = { VEG_POT, VEG_CACTUS, VEG_MAP, VEG_PLANTER, vegItem, assembleVegetation };
8206
+ Object.assign(K, defined);
8080
8207
  }
8081
8208
 
8082
8209
  // src/interaction/state.ts
@@ -8144,12 +8271,14 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8144
8271
  shared.inter.msg = { text, until: performance.now() + MESSAGE_MS };
8145
8272
  if (cfg.onMessage) cfg.onMessage(text);
8146
8273
  };
8147
- function definePiece(name, def) {
8274
+ const I = () => shared.inter;
8275
+ function definePiece(name, definition) {
8276
+ const def = definition;
8148
8277
  def.name = name;
8149
8278
  def.meshes = [];
8150
8279
  def.t = def.target = shared.partState.has(name) ? shared.partState.get(name) : def.t0 || 0;
8151
- shared.inter.defs.set(name, def);
8152
- shared.inter.pieces.push(def);
8280
+ I().defs.set(name, def);
8281
+ I().pieces.push(def);
8153
8282
  return def;
8154
8283
  }
8155
8284
  const localOf = (M, lx, lz) => {
@@ -8161,17 +8290,18 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8161
8290
  return true;
8162
8291
  }
8163
8292
  function assembleInteractive(gs) {
8164
- for (const [name, def] of shared.inter.defs) {
8293
+ const inter = I();
8294
+ for (const [name, def] of inter.defs) {
8165
8295
  const g = gs[name];
8166
8296
  if (!g) continue;
8167
8297
  delete gs[name];
8168
8298
  const outer = new THREE.Group();
8169
- outer.position.set(...def.pivot);
8299
+ outer.position.fromArray(def.pivot);
8170
8300
  outer.rotation.y = def.ry || 0;
8171
8301
  outer.updateMatrix();
8172
- const inv = new THREE.Matrix4().compose(new THREE.Vector3(...def.pivot), new THREE.Quaternion().setFromAxisAngle(_EY, def.ry || 0), _UM3).invert();
8302
+ const inv = new THREE.Matrix4().compose(new THREE.Vector3().fromArray(def.pivot), new THREE.Quaternion().setFromAxisAngle(_EY, def.ry || 0), _UM3).invert();
8173
8303
  g.traverse((o) => {
8174
- if (o.isMesh) {
8304
+ if (isMesh(o)) {
8175
8305
  o.geometry.applyMatrix4(inv);
8176
8306
  o.userData.piece = def;
8177
8307
  def.meshes.push(o);
@@ -8183,13 +8313,14 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8183
8313
  const parent = def.leaf ? def.leaf.gs[def.k] : gs[def.parent] || (gs[def.parent] = new THREE.Group());
8184
8314
  if (parent) parent.add(outer);
8185
8315
  if (def.roomId) {
8186
- if (!shared.inter.lights.has(def.roomId)) shared.inter.lights.set(def.roomId, []);
8187
- shared.inter.lights.get(def.roomId).push(def);
8316
+ let lamps = inter.lights.get(def.roomId);
8317
+ if (!lamps) inter.lights.set(def.roomId, lamps = []);
8318
+ lamps.push(def);
8188
8319
  }
8189
8320
  applyPiece(def);
8190
8321
  }
8191
- for (const roomId of shared.inter.litRooms) applyLight(roomId);
8192
- shared.inter.defs.forEach((d) => {
8322
+ for (const roomId of inter.litRooms) applyLight(roomId);
8323
+ inter.defs.forEach((d) => {
8193
8324
  if (d.col) d.col.off = d.t > 0.5;
8194
8325
  });
8195
8326
  }
@@ -8197,6 +8328,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8197
8328
  const n = p.inner;
8198
8329
  if (!n) return;
8199
8330
  switch (p.kind) {
8331
+ /* each kind always sets its dimensions (sgn, angle, travel, k, axis) */
8200
8332
  case "swing":
8201
8333
  n.rotation.y = p.sgn * p.t * p.angle;
8202
8334
  break;
@@ -8229,8 +8361,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8229
8361
  }
8230
8362
  const DURATION = { swing: 0.55, drawer: 0.4, curtain: 0.9, gate_swing: 0.9, gate_tilt: 1.4, gate_slide: 1.6, lightSwitch: 0.12, part: 0.4 };
8231
8363
  function animateInteractive(dt) {
8232
- if (!shared.inter.anim.size) return;
8233
- for (const p of shared.inter.anim) {
8364
+ const inter = I();
8365
+ if (!inter.anim.size) return;
8366
+ for (const p of inter.anim) {
8234
8367
  const step = dt / (DURATION[p.kind] || 0.5), d = p.target - p.t;
8235
8368
  p.t = Math.abs(d) <= step ? p.target : p.t + Math.sign(d) * step;
8236
8369
  applyPiece(p);
@@ -8238,7 +8371,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8238
8371
  p.col.off = p.target > 0.5;
8239
8372
  colliderChanged(p.col);
8240
8373
  }
8241
- if (p.t === p.target) shared.inter.anim.delete(p);
8374
+ if (p.t === p.target) inter.anim.delete(p);
8242
8375
  }
8243
8376
  invalidateShadow();
8244
8377
  }
@@ -8249,7 +8382,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8249
8382
  p.col.off = p.target > 0.5;
8250
8383
  colliderChanged(p.col);
8251
8384
  }
8252
- shared.inter.anim.add(p);
8385
+ I().anim.add(p);
8253
8386
  }
8254
8387
  const lightBase = (roomId) => roomId == null ? true : shared.partState.has("light:" + roomId) ? !!shared.partState.get("light:" + roomId) : !!shared.ctx.night;
8255
8388
  const lightOn = (roomId) => {
@@ -8257,20 +8390,21 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8257
8390
  return S ? S.ef : lightBase(roomId);
8258
8391
  };
8259
8392
  function applyLight(roomId) {
8260
- const on = lightOn(roomId);
8261
- for (const d of shared.inter.lights.get(roomId) || []) for (const m of d.meshes) if (m.name.endsWith("/emissive")) m.material = on ? shared.mats.emissive : shared.mats.off;
8262
- const sw = shared.inter.defs.get("ix:int:" + roomId);
8393
+ const on = lightOn(roomId), inter = I(), mats = shared.mats;
8394
+ for (const d of inter.lights.get(roomId) || []) for (const m of d.meshes) if (m.name.endsWith("/emissive")) m.material = on ? mats.emissive : mats.off;
8395
+ const sw = inter.defs.get("ix:int:" + roomId);
8263
8396
  if (sw) {
8264
8397
  sw.target = on ? 1 : 0;
8265
- if (sw.t !== sw.target) shared.inter.anim.add(sw);
8398
+ if (sw.t !== sw.target) inter.anim.add(sw);
8266
8399
  }
8267
8400
  invalidate();
8268
8401
  }
8269
8402
  function toggleLight(roomId) {
8270
8403
  const S = shared.ctx.iv && shared.ctx.iv.roomStates.get(roomId);
8271
8404
  if (S) {
8272
- ivRoomLight(shared.ctx.iv, roomId, !S.ef);
8273
- const ef = ivRoomEffective(shared.ctx.iv, S);
8405
+ const IV = shared.ctx.iv;
8406
+ ivRoomLight(IV, roomId, !S.ef);
8407
+ const ef = ivRoomEffective(IV, S);
8274
8408
  if (ef !== S.ef) {
8275
8409
  S.ef = ef;
8276
8410
  applyLight(roomId);
@@ -8282,11 +8416,13 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8282
8416
  applyLight(roomId);
8283
8417
  if (cfg.onLight) cfg.onLight(roomId, lightOn(roomId));
8284
8418
  }
8285
- Object.assign(K, { newInteractiveState, MESSAGE_MS, createInteractiveMaterials, _EY, _UM3, hudMessage, definePiece, localOf, visibleChain, assembleInteractive, applyPiece, DURATION, animateInteractive, togglePiece, lightBase, lightOn, applyLight, toggleLight });
8419
+ const defined = { newInteractiveState, MESSAGE_MS, createInteractiveMaterials, _EY, _UM3, hudMessage, definePiece, localOf, visibleChain, assembleInteractive, applyPiece, DURATION, animateInteractive, togglePiece, lightBase, lightOn, applyLight, toggleLight };
8420
+ Object.assign(K, defined);
8286
8421
  }
8287
8422
 
8288
8423
  // src/interaction/collision.ts
8289
8424
  function installInteractionCollision(K, shared) {
8425
+ const inter = () => shared.inter;
8290
8426
  const NO_COLLIDE = /(^|_)(rug|floor|concrete|dirt|lawn|deck|ceiling_light|pendant|chandelier|spot|picture|curtain|blinds|wall_shelving|plank_shelf|wall_ac|ac_condenser|pool|soaking_tub|pergola|stairs|mirror)/;
8291
8427
  function registerCollision(m, M) {
8292
8428
  const h = m.h || 0, y = M.y || 0;
@@ -8296,7 +8432,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8296
8432
  w = Math.min(w, 0.5);
8297
8433
  d = Math.min(d, 0.5);
8298
8434
  }
8299
- shared.inter.objs.push({ f: M.floor, cx: m.x, cz: m.z, c: Math.cos(M.ry), s: Math.sin(M.ry), hw: w / 2, hd: d / 2, type: m.type });
8435
+ inter().objs.push({ f: M.floor, cx: m.x, cz: m.z, c: Math.cos(M.ry), s: Math.sin(M.ry), hw: w / 2, hd: d / 2, type: m.type });
8300
8436
  }
8301
8437
  function objectDistance(o, x, z) {
8302
8438
  const dx = x - o.cx, dz = z - o.cz, lx = dx * o.c - dz * o.s, lz = dx * o.s + dz * o.c, ex = Math.abs(lx) - o.hw, ez = Math.abs(lz) - o.hd;
@@ -8304,7 +8440,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8304
8440
  }
8305
8441
  function collideObject(x, z, fx, fz, floor) {
8306
8442
  const R = 0.2;
8307
- for (const o of shared.inter.objs) {
8443
+ for (const o of inter().objs) {
8308
8444
  if (o.f !== -1 ? o.f !== floor : floor !== 0) continue;
8309
8445
  const d1 = objectDistance(o, x, z);
8310
8446
  if (d1 < R && d1 < objectDistance(o, fx, fz) - 1e-6) return true;
@@ -8324,7 +8460,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8324
8460
  const de = opt.from || [x + 1e4, z + 1e4];
8325
8461
  return collideObject(x, z, de[0], de[1], floor);
8326
8462
  }
8327
- Object.assign(K, { NO_COLLIDE, registerCollision, objectDistance, collideObject, collideAt });
8463
+ const defined = { NO_COLLIDE, registerCollision, objectDistance, collideObject, collideAt };
8464
+ Object.assign(K, defined);
8328
8465
  }
8329
8466
 
8330
8467
  // src/interaction/items.ts
@@ -8333,6 +8470,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8333
8470
  const DOOR_ITEM = /^(tv_unit|tv_unit_small|tv_unit_large|sideboard|china_cabinet|filing_cabinet|pantry|laundry_cabinet|wardrobe|wardrobe_small|wardrobe_large|closet_wardrobe|kitchen_cabinet|kitchen_cabinet_large|kitchen_cabinet_small|fridge|fridge_large|display_fridge_medium|display_fridge)$/;
8334
8471
  const DRAWER_ITEM = /^(console_table|dresser|nightstand|vanity_table)$/;
8335
8472
  const LIGHT_TYPES = /* @__PURE__ */ new Set(["ceiling_light", "pendant", "chandelier", "track_spot", "hanging_plant"]);
8473
+ const I = () => shared.inter;
8336
8474
  function objectName(type, drawer) {
8337
8475
  return drawer ? "drawer" : /^wardrobe/.test(type) ? "wardrobe" : /fridge|refrig|freezer/.test(type) ? "fridge" : /pantry/.test(type) ? "pantry" : "cabinet";
8338
8476
  }
@@ -8361,8 +8499,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8361
8499
  cursor.group = parent;
8362
8500
  }
8363
8501
  for (let i = n0; i < LIGHTS.length; i++) LIGHTS[i].roomId = m.room;
8364
- if (!shared.inter.defs.has(name)) definePiece(name, { kind: "light", pivot: [0, 0, 0], ry: 0, parent, roomId: m.room });
8365
- shared.inter.litRooms.add(m.room);
8502
+ if (!I().defs.has(name)) definePiece(name, { kind: "light", pivot: [0, 0, 0], ry: 0, parent, roomId: m.room });
8503
+ I().litRooms.add(m.room);
8366
8504
  }
8367
8505
  function curtainItem(m, M, ent, api, parent) {
8368
8506
  M.open = false;
@@ -8390,12 +8528,13 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8390
8528
  for (const c of body) api[c[0]](M, ...c[1]);
8391
8529
  if (!parts.length) return;
8392
8530
  const [ax, az] = localOf(M, 0, 0.2);
8393
- shared.inter.items.push({ type: "curtain", id: m.id, floor: M.floor, anchor: [ax, M.base + (M.y || 0) + (m.h || 2) * 0.5, az], range: 2.4, angle: 1, pieces: parts, text: () => t(parts[0].target > 0.5 ? "prompt.close" : "prompt.open", { what: t("what.curtain") }), action: () => {
8394
- const al = parts[0].target > 0.5 ? 0 : 1;
8531
+ const first = parts[0];
8532
+ I().items.push({ type: "curtain", id: m.id, floor: M.floor, anchor: [ax, M.base + (M.y || 0) + (m.h || 2) * 0.5, az], range: 2.4, angle: 1, pieces: parts, text: () => t(first.target > 0.5 ? "prompt.close" : "prompt.open", { what: t("what.curtain") }), action: () => {
8533
+ const al = first.target > 0.5 ? 0 : 1;
8395
8534
  for (const p of parts) {
8396
8535
  p.target = al;
8397
8536
  shared.partState.set(p.name, al);
8398
- shared.inter.anim.add(p);
8537
+ I().anim.add(p);
8399
8538
  }
8400
8539
  } });
8401
8540
  }
@@ -8416,7 +8555,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8416
8555
  calls.forEach((c, i) => {
8417
8556
  if (owner.has(i)) return;
8418
8557
  const a = c[1];
8419
- let x0, x1, y0, y1, ok = false, grip = 0, wide = false;
8558
+ let x0 = 0, x1 = 0, y0 = 0, y1 = 0, ok = false, grip = 0, wide = false;
8420
8559
  if (c[0] === "box") {
8421
8560
  const [mat, x, y, z, w, h, d, , ry, rx, rz] = a;
8422
8561
  if (ry || rx || rz || d > 0.05 || z + d / 2 < front - 5e-3 || w > M.w * 1.01) return;
@@ -8448,8 +8587,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8448
8587
  return;
8449
8588
  }
8450
8589
  candidate.sort((p, q) => overlap(x0, x1, q.x0, q.x1) - overlap(x0, x1, p.x0, p.x1));
8451
- owner.set(i, candidate[0]);
8452
- if (grip) candidate[0].grip = grip;
8590
+ const best = candidate[0];
8591
+ owner.set(i, best);
8592
+ if (grip) best.grip = grip;
8453
8593
  });
8454
8594
  const name = objectName(m.type, drawer), parts = [];
8455
8595
  panels.forEach((p, k) => {
@@ -8465,8 +8605,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8465
8605
  p.def = def;
8466
8606
  parts.push(def);
8467
8607
  const [ax, az] = localOf(M, cx, front + 0.08);
8468
- shared.inter.items.push({ type: drawer ? "drawer" : "cabinet", id: m.id, floor: M.floor, anchor: [ax, M.base + (M.y || 0) + (p.y0 + p.y1) / 2, az], range: 1.6, angle: 0.4, pieces: [def], text: () => t(def.target > 0.5 ? "prompt.close" : "prompt.open", { what: t("what." + name) }), action: () => togglePiece(def) });
8469
- def.item = shared.inter.items[shared.inter.items.length - 1];
8608
+ I().items.push({ type: drawer ? "drawer" : "cabinet", id: m.id, floor: M.floor, anchor: [ax, M.base + (M.y || 0) + (p.y0 + p.y1) / 2, az], range: 1.6, angle: 0.4, pieces: [def], text: () => t(def.target > 0.5 ? "prompt.close" : "prompt.open", { what: t("what." + name) }), action: () => togglePiece(def) });
8609
+ def.item = I().items[I().items.length - 1];
8470
8610
  });
8471
8611
  calls.forEach((c, i) => {
8472
8612
  const p = owner.get(i);
@@ -8480,14 +8620,17 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8480
8620
  }
8481
8621
  cursor.group = parent;
8482
8622
  }
8483
- Object.assign(K, { DOOR_ITEM, DRAWER_ITEM, LIGHT_TYPES, objectName, recordApi, buildItem, lightItem, curtainItem, furnitureItem });
8623
+ const defined = { DOOR_ITEM, DRAWER_ITEM, LIGHT_TYPES, objectName, recordApi, buildItem, lightItem, curtainItem, furnitureItem };
8624
+ Object.assign(K, defined);
8484
8625
  }
8485
8626
 
8486
8627
  // src/interaction/fittings.ts
8487
8628
  function installInteractionFittings(K, shared) {
8488
8629
  const { t, EPS: EPS3, OPEN_AREA, Y0, applyPiece, bx, cursor, definePiece, gates, lightOn, roomAt, slideDirection, toggleLight, togglePiece } = K;
8630
+ const I = () => shared.inter;
8631
+ const house = () => shared.ctx.r;
8489
8632
  function gatesInteractive() {
8490
- const lt = shared.ctx.r.lot;
8633
+ const lt = house().lot;
8491
8634
  if (!lt) return;
8492
8635
  const cz0 = (lt.rect[1] + lt.rect[3]) / 2;
8493
8636
  const orig = K.getBx(), gs = lt.gates, opening = gs.map((g) => g.open), reg = gs.map((g) => {
@@ -8507,9 +8650,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8507
8650
  return;
8508
8651
  }
8509
8652
  }
8510
- return orig(mat, x0, y0, z0, x1, y1, z1, color2);
8653
+ orig(mat, x0, y0, z0, x1, y1, z1, color2);
8511
8654
  });
8512
- const n0 = shared.ctx.colliders.length;
8655
+ const colliders = shared.ctx.colliders, n0 = colliders.length;
8513
8656
  try {
8514
8657
  gs.forEach((g) => {
8515
8658
  g.open = false;
@@ -8521,7 +8664,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8521
8664
  g.open = opening[i];
8522
8665
  });
8523
8666
  }
8524
- const cols = shared.ctx.colliders.slice(n0), cz = (lt.rect[1] + lt.rect[3]) / 2, cu = (lt.rect[0] + lt.rect[2]) / 2;
8667
+ const cols = colliders.slice(n0), cz = (lt.rect[1] + lt.rect[3]) / 2, cu = (lt.rect[0] + lt.rect[2]) / 2;
8525
8668
  reg.forEach((r, i) => {
8526
8669
  const g = r.g;
8527
8670
  if (!isFinite(r.x0)) return;
@@ -8530,25 +8673,25 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8530
8673
  const p = ped ? definePiece(name, Object.assign(base, { kind: "gate_swing", pivot: [r.x0, 0, g.v], angle: -sd * 1.65 })) : tilt ? definePiece(name, Object.assign(base, { kind: "gate_tilting", pivot: [(r.x0 + r.x1) / 2, r.y1, g.v], angle: -sd * 1.45 })) : definePiece(name, Object.assign(base, { kind: "gate_sliding", pivot: [r.x0, 0, r.zm], travel: r.dx * g.width }));
8531
8674
  if (p.col) p.col.off = p.target > 0.5;
8532
8675
  const text = () => t(p.target > 0.5 ? "prompt.close" : "prompt.open", { what: t(ped ? "what.pedestrianGate" : "what.garageGate") });
8533
- shared.inter.items.push({ type: "gate", id: g.id, floor: -1, anchor: [g.u, 1, g.v + sd * 0.4], range: 2.8, angle: 0.9, pieces: [p], text, action: () => togglePiece(p), noWall: true });
8676
+ I().items.push({ type: "gate", id: g.id, floor: -1, anchor: [g.u, 1, g.v + sd * 0.4], range: 2.8, angle: 0.9, pieces: [p], text, action: () => togglePiece(p), noWall: true });
8534
8677
  });
8535
8678
  }
8536
8679
  function mailboxInteractive() {
8537
- for (const m of shared.ctx.r.items) {
8680
+ for (const m of house().items) {
8538
8681
  if (m.type !== "mailbox") continue;
8539
8682
  const g = shared.ctx.parts && shared.ctx.parts["mailbox_" + m.id];
8540
8683
  if (!g) continue;
8541
8684
  const name = "ix:correio:" + m.id, def = definePiece(name, { kind: "part", pivot: [0, 0, 0], ry: 0, parent: "lot" });
8542
8685
  def.inner = g;
8543
8686
  const fx = Math.sin(m.ry || 0), fz = Math.cos(m.ry || 0), ay = (m.y || 1) + (m.h || 0.3) * 0.4;
8544
- shared.inter.items.push({ type: "mailbox", id: m.id, floor: -1, anchor: [m.x + fx * 0.2, ay, m.z + fz * 0.2], range: 2.2, angle: 0.7, pieces: [def], text: () => t(def.target > 0.5 ? "prompt.close" : "prompt.open", { what: t("what.mailbox") }), action: () => togglePiece(def), noWall: true });
8687
+ I().items.push({ type: "mailbox", id: m.id, floor: -1, anchor: [m.x + fx * 0.2, ay, m.z + fz * 0.2], range: 2.2, angle: 0.7, pieces: [def], text: () => t(def.target > 0.5 ? "prompt.close" : "prompt.open", { what: t("what.mailbox") }), action: () => togglePiece(def), noWall: true });
8545
8688
  applyPiece(def);
8546
8689
  }
8547
8690
  }
8548
8691
  function placeSwitches() {
8549
- const R = shared.ctx.r, used = /* @__PURE__ */ new Set();
8692
+ const R = house(), used = /* @__PURE__ */ new Set();
8550
8693
  for (const c of R.rooms) {
8551
- if (c.type === "void" || OPEN_AREA(c) || !shared.inter.litRooms.has(c.id) || used.has(c.id) || shared.ctx.iv && shared.ctx.iv.switchRooms.has(c.id)) continue;
8694
+ if (c.type === "void" || OPEN_AREA(c) || !I().litRooms.has(c.id) || used.has(c.id) || shared.ctx.iv && shared.ctx.iv.switchRooms.has(c.id)) continue;
8552
8695
  const doors = R.doors.filter((p) => p.floor === c.floor && (p.a === c.id || p.b === c.id));
8553
8696
  doors.sort((p, q) => ((q.exterior ? 1 : 0) - (p.exterior ? 1 : 0)) * (c.type === "living" ? 1 : -1));
8554
8697
  for (const p of doors) if (putSwitch(c, p)) {
@@ -8558,11 +8701,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8558
8701
  }
8559
8702
  }
8560
8703
  function putSwitch(c, p) {
8561
- const R = shared.ctx.r, f = p.floor, alongU = p.axis === "u", L = p.pos, a = p.at - p.width / 2, b = p.at + p.width / 2;
8704
+ const R = house(), f = p.floor, alongU = p.axis === "u", L = p.pos, a = p.at - p.width / 2, b = p.at + p.width / 2;
8562
8705
  const wref = R.walls.find((w) => w.floor === f && w.axis === p.axis && Math.abs((alongU ? w.v0 : w.u0) - L) < EPS3), th = wref ? wref.thickness || 0.15 : 0.15;
8563
8706
  const inside2 = (sd2) => {
8564
8707
  const cc = roomAt(f, alongU ? p.at : L + sd2 * 0.4, alongU ? L + sd2 * 0.4 : p.at);
8565
- return cc && cc.id === c.id;
8708
+ return !!cc && cc.id === c.id;
8566
8709
  };
8567
8710
  const sd = inside2(1) ? 1 : inside2(-1) ? -1 : 0;
8568
8711
  if (!sd) return false;
@@ -8585,19 +8728,22 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8585
8728
  const piece2 = definePiece("ix:int:" + roomId, { kind: "light_switch", pivot: pv, ry: 0, parent: "f" + f, axis: alongU ? "x" : "z", sgn: alongU ? sd : -sd, t0: lightOn(roomId) ? 1 : 0 });
8586
8729
  piece2.t = piece2.target = lightOn(roomId) ? 1 : 0;
8587
8730
  const an = alongU ? [u, yc, face + sd * 0.12] : [face + sd * 0.12, yc, u];
8588
- shared.inter.items.push({ type: "light_switch", id: roomId, floor: f, anchor: an, range: 1.5, angle: 0.45, pieces: [piece2], text: () => t(lightOn(roomId) ? "prompt.lightOff" : "prompt.lightOn"), action: () => toggleLight(roomId) });
8731
+ I().items.push({ type: "light_switch", id: roomId, floor: f, anchor: an, range: 1.5, angle: 0.45, pieces: [piece2], text: () => t(lightOn(roomId) ? "prompt.lightOff" : "prompt.lightOn"), action: () => toggleLight(roomId) });
8589
8732
  return true;
8590
8733
  }
8591
8734
  return false;
8592
8735
  }
8593
- Object.assign(K, { gatesInteractive, mailboxInteractive, placeSwitches, putSwitch });
8736
+ const defined = { gatesInteractive, mailboxInteractive, placeSwitches, putSwitch };
8737
+ Object.assign(K, defined);
8594
8738
  }
8595
8739
 
8596
8740
  // src/interaction/doors.ts
8597
8741
  function installInteractionDoors(K, shared) {
8598
8742
  const { t, colliderChanged, invalidateShadow, applyPiece, hudMessage } = K;
8743
+ const leaves = () => shared.ctx.leaves;
8599
8744
  function linkDoorLeaves() {
8600
- for (const fo of shared.ctx.leaves) fo.handles = (fo.handleNames || []).map((n) => shared.inter.defs.get(n)).filter(Boolean);
8745
+ const I = shared.inter;
8746
+ for (const fo of leaves()) fo.handles = (fo.handleNames || []).map((n) => I.defs.get(n)).filter((d) => !!d);
8601
8747
  }
8602
8748
  function handleApply(fo) {
8603
8749
  for (const mc of fo.handles || []) {
@@ -8607,11 +8753,12 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8607
8753
  }
8608
8754
  function toggleDoor(fo) {
8609
8755
  if (fo.phase === "rattle") return;
8756
+ const col = fo.col;
8610
8757
  if (fo.phase === "handle" || fo.phase === "swing") {
8611
8758
  fo.open = !fo.open;
8612
8759
  shared.doorState.set(fo.id, fo.open);
8613
- fo.col.off = fo.open;
8614
- colliderChanged(fo.col);
8760
+ col.off = fo.open;
8761
+ colliderChanged(col);
8615
8762
  fo.anim = true;
8616
8763
  fo.phase = "swing";
8617
8764
  return;
@@ -8625,8 +8772,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8625
8772
  }
8626
8773
  fo.open = !fo.open;
8627
8774
  shared.doorState.set(fo.id, fo.open);
8628
- fo.col.off = fo.open;
8629
- colliderChanged(fo.col);
8775
+ col.off = fo.open;
8776
+ colliderChanged(col);
8630
8777
  fo.anim = true;
8631
8778
  shared.ctx.animateDoorsOn = true;
8632
8779
  if (fo.handles && fo.handles.length) {
@@ -8637,7 +8784,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8637
8784
  function animateDoors(dt) {
8638
8785
  if (!shared.ctx.animateDoorsOn) return;
8639
8786
  let active = false;
8640
- for (const fo of shared.ctx.leaves) {
8787
+ for (const fo of leaves()) {
8641
8788
  if (!fo.phase) continue;
8642
8789
  active = true;
8643
8790
  if (fo.phase === "handle") {
@@ -8657,7 +8804,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8657
8804
  handleApply(fo);
8658
8805
  if (fo.hT <= 0) fo.phase = null;
8659
8806
  } else if (fo.phase === "rattle") {
8660
- fo.jig -= dt;
8807
+ fo.jig = (fo.jig ?? 0) - dt;
8661
8808
  fo.hT = fo.jig > 0 ? 0.55 * Math.abs(Math.sin(fo.jig * 26)) : 0;
8662
8809
  handleApply(fo);
8663
8810
  if (fo.jig <= 0) {
@@ -8671,9 +8818,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8671
8818
  invalidateShadow();
8672
8819
  }
8673
8820
  function wallBlocks(ex, ez, ax, az, floor) {
8674
- const n = shared.ctx.wallColliders != null ? shared.ctx.wallColliders : 0;
8821
+ const n = shared.ctx.wallColliders != null ? shared.ctx.wallColliders : 0, colliders = shared.ctx.colliders;
8675
8822
  for (let i = 0; i < n; i++) {
8676
- const c = shared.ctx.colliders[i];
8823
+ const c = colliders[i];
8677
8824
  if (c.f !== floor && !(c.f === -1 && floor === 0)) continue;
8678
8825
  let t0 = 0, t1 = 1;
8679
8826
  const dx = ax - ex, dz = az - ez;
@@ -8703,7 +8850,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8703
8850
  g.updateMatrix();
8704
8851
  }
8705
8852
  }
8706
- Object.assign(K, { linkDoorLeaves, handleApply, toggleDoor, animateDoors, wallBlocks, doorText, applyLeaf });
8853
+ const defined = { linkDoorLeaves, handleApply, toggleDoor, animateDoors, wallBlocks, doorText, applyLeaf };
8854
+ Object.assign(K, defined);
8707
8855
  }
8708
8856
 
8709
8857
  // src/interaction/target.ts
@@ -8713,6 +8861,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8713
8861
  function targetOf(o, opt) {
8714
8862
  if (!o) return null;
8715
8863
  opt = opt || {};
8864
+ const I = shared.inter;
8716
8865
  const ex = o.x, ey = o.y, ez = o.z, floor = o.floor, cp = Math.cos(o.pitch || 0), vx = Math.sin(o.yaw) * cp, vy = Math.sin(o.pitch || 0), vz = Math.cos(o.yaw) * cp;
8717
8866
  let best = null;
8718
8867
  const tries = (score, text, action, target) => {
@@ -8726,16 +8875,16 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8726
8875
  if (angle > 1.35 && d > 0.9) continue;
8727
8876
  tries(angle * 1.6 + d * 0.5 + (angle < 0.25 ? 0 : 0.6), doorText(fo), () => toggleDoor(fo), fo);
8728
8877
  }
8729
- const vis = shared.inter.pieces.flatMap((p) => p.meshes).filter(visibleChain), hit = vis.length && opt.strike ? opt.strike(vis) : null;
8878
+ const vis = I.pieces.flatMap((p) => p.meshes).filter(visibleChain), hit = vis.length && opt.strike ? opt.strike(vis) : null;
8730
8879
  const kitA = shared.ctx.iv ? ivTarget(shared.ctx.iv, o, opt && opt.interactives) : null;
8731
8880
  const nearKit = kitA && hit && Math.hypot(kitA.target.pos[0] - ex, kitA.target.pos[1] - ey, kitA.target.pos[2] - ez) < hit.distance + 0.1;
8732
8881
  const pieceHit = hit && !nearKit && hit.object.userData.piece;
8733
- for (const it of shared.inter.items) {
8882
+ for (const it of I.items) {
8734
8883
  if (it.floor !== floor && !(it.floor === -1 && floor === 0)) continue;
8735
8884
  const [ax, ay, az] = it.anchor, dx = ax - ex, dy = ay - ey, dz = az - ez, d = Math.hypot(dx, dy, dz), dh = Math.hypot(dx, dz);
8736
8885
  if (dh > it.range + 0.4) continue;
8737
8886
  const angle = Math.acos(Math.max(-1, Math.min(1, (dx * vx + dy * vy + dz * vz) / (d || 1))));
8738
- const byRay = pieceHit && it.pieces.includes(pieceHit) && hit.distance <= it.range + 0.5;
8887
+ const byRay = !!hit && pieceHit && it.pieces.includes(pieceHit) && hit.distance <= it.range + 0.5;
8739
8888
  if (!byRay && !(dh <= it.range && angle <= it.angle)) continue;
8740
8889
  if (!it.noWall && wallBlocks(ex, ez, ax, az, it.floor < 0 ? 0 : it.floor)) continue;
8741
8890
  tries((byRay ? 0 : angle * 2) + d * 0.3 - (byRay ? 1.5 : 0), it.text(), it.action, it);
@@ -8743,7 +8892,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8743
8892
  if (kitA) tries(kitA.score - (nearKit ? 0.5 : 0), kitA.text, kitA.action, kitA.target);
8744
8893
  return best;
8745
8894
  }
8746
- Object.assign(K, { targetOf });
8895
+ const defined = { targetOf };
8896
+ Object.assign(K, defined);
8747
8897
  }
8748
8898
 
8749
8899
  // src/interactables/bridge.ts
@@ -8800,7 +8950,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8800
8950
  function ivBuild(h, water) {
8801
8951
  const ik = K.IVK, r = shared.ctx.r, A = ik.A, world0 = null;
8802
8952
  void world0;
8803
- const plane2 = A.plan(r, { seed: r.seed });
8953
+ const source = r;
8954
+ const plane2 = A.plan(source, { seed: r.seed });
8804
8955
  const IV = { k: ik, plane: plane2, world: null, water, entries: /* @__PURE__ */ new Map(), roots: [], replace: /* @__PURE__ */ new Set(), roomStates: /* @__PURE__ */ new Map(), lampRoom: /* @__PURE__ */ new Map(), anim: /* @__PURE__ */ new Map(), screens: /* @__PURE__ */ new Map(), efx: null, items: null, itemsDirty: true, itemsT: 0, dirty: true, list: null, listT: 0, t: 0, circuits: [], switchRooms: /* @__PURE__ */ new Set(), warnings: [], tBuild: 0 };
8805
8956
  h.IV = IV;
8806
8957
  const t0 = performance.now();
@@ -8819,9 +8970,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8819
8970
  const wants = (roomId) => lightBase(roomId);
8820
8971
  const entById = new Map(plane2.items.map((e) => [e.id, e]));
8821
8972
  const entByItem = new Map(plane2.items.map((e) => [e.item, e]));
8822
- IV.mats = { plain: shared.mats.plain, metal: shared.mats.metal, wood: shared.mats.wood, glass: shared.mats.glass, fabric: shared.mats.fabric, plant: shared.mats.plant, water: shared.mats.water, emissive: shared.mats.emissive, atlas_picture: shared.mats.picture, atlas_floor: shared.mats.floor };
8973
+ const mats = shared.mats;
8974
+ IV.mats = { plain: mats.plain, metal: shared.mats.metal, wood: shared.mats.wood, glass: shared.mats.glass, fabric: shared.mats.fabric, plant: shared.mats.plant, water: shared.mats.water, emissive: shared.mats.emissive, atlas_picture: shared.mats.picture, atlas_floor: shared.mats.floor };
8823
8975
  const ev = (e) => ivEvent(h, e);
8824
- const world = IV.world = A.createWorld(r, { water, seed: r.seed, onEvent: ev });
8976
+ const world = IV.world = A.createWorld(source, { water, seed: r.seed, onEvent: ev });
8825
8977
  const add0 = world.add;
8826
8978
  world.add = (id, spec, tr) => {
8827
8979
  const e = entById.get(id);
@@ -8838,7 +8990,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8838
8990
  }
8839
8991
  return add0(id, spec, tr);
8840
8992
  };
8841
- const added = A.add(world, plane2, { isBlocked: A.blockedOf(r), build: (type, item) => ivDraw(IV, entByItem.get(item), type, item) });
8993
+ const added = A.add(world, plane2, { isBlocked: A.blockedOf(source), build: (type, item) => ivDraw(IV, entByItem.get(item), type, item) });
8842
8994
  world.add = add0;
8843
8995
  IV.warnings = added.warnings;
8844
8996
  IV.added = added;
@@ -8885,14 +9037,15 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8885
9037
  }
8886
9038
  function ivDraw(IV, ent, type, item) {
8887
9039
  const ik = IV.k, loc = Object.assign({}, item, { x: 0, z: 0, ry: 0 });
9040
+ const ix = item.x, iz = item.z, iry = item.ry;
8888
9041
  const mesh2 = ik.gfx.createMeshApi({ quality: "game", materials: IV.mats, shadows: true, mergeParts: true, swayAmplitude: 0.03 });
8889
9042
  const spec = ik.A.household.build(type, loc, mesh2.api);
8890
9043
  const out = mesh2.finish(spec);
8891
9044
  for (const l of out.lights) l.removeFromParent();
8892
9045
  const group = ivGroup(ent), g0 = cursor.group, root = new THREE.Group();
8893
9046
  root.name = "iv:" + ent.id;
8894
- root.position.set(item.x, ent.floorY, item.z);
8895
- root.rotation.y = item.ry;
9047
+ root.position.set(ix, ent.floorY, iz);
9048
+ root.rotation.y = iry;
8896
9049
  root.userData.iv = ent.id;
8897
9050
  const screen = spec.devices && spec.devices.some((d) => d.kind === "screen");
8898
9051
  let body = null;
@@ -8901,7 +9054,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8901
9054
  if (type === "gas_bottle") {
8902
9055
  cursor.pc = pieceRecord("gas_cylinder:" + ent.id, "steel", null, { floor: ent.floor, mass: 11, form: "cyl", thickness: 3e-3, fill: 0.03, contains: { fluid: "gas", liters: 13, pressurized: true }, explodes: { energy: 12e4, radius: 5, atC: 300 }, tags: ["gas_bottle"] });
8903
9056
  for (const m of out.group.children) if (!m.name.startsWith("static:")) m.traverse((o) => {
8904
- if (o.isMesh) {
9057
+ if (isMesh(o)) {
8905
9058
  o.userData.destructionPart = cursor.pc.id;
8906
9059
  o.userData.destructionMaterial = "steel";
8907
9060
  }
@@ -8909,13 +9062,13 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8909
9062
  }
8910
9063
  try {
8911
9064
  for (const m of out.group.children.slice()) {
8912
- if (m.isMesh && m.name.startsWith("static:")) {
9065
+ if (isMesh(m) && m.name.startsWith("static:")) {
8913
9066
  if (screen && m.userData.material === "emissive") {
8914
9067
  body = m;
8915
9068
  root.add(m);
8916
9069
  continue;
8917
9070
  }
8918
- ivAccumulate(IV_MATERIAL[m.userData.material] || "plain", m.geometry, item.x, ent.floorY, item.z, item.ry, null);
9071
+ ivAccumulate(IV_MATERIAL[m.userData.material] || "plain", m.geometry, ix, ent.floorY, iz, iry, null);
8919
9072
  m.geometry.dispose();
8920
9073
  } else root.add(m);
8921
9074
  }
@@ -8925,7 +9078,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8925
9078
  }
8926
9079
  const base = new Set(Object.values(IV.mats));
8927
9080
  root.traverse((o) => {
8928
- if (!o.isMesh) return;
9081
+ if (!isMesh(o)) return;
8929
9082
  o.userData.iv = ent.id;
8930
9083
  if (o.castShadow) {
8931
9084
  if (!o.geometry.boundingSphere) o.geometry.computeBoundingSphere();
@@ -8936,7 +9089,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8936
9089
  roomLightMask(m);
8937
9090
  }
8938
9091
  });
8939
- const E = { id: ent.id, type, entry: ent, floor: ent.floor, group, root, parts: out.parts, spec, screen: body, pos: [item.x, ent.floorY, item.z], ry: item.ry, origin: ent.origin };
9092
+ const E = { id: ent.id, type, entry: ent, floor: ent.floor, group, root, parts: out.parts, spec, screen: body, pos: [ix, ent.floorY, iz], ry: iry, origin: ent.origin };
8940
9093
  IV.entries.set(ent.id, E);
8941
9094
  if (root.children.length) IV.roots.push(E);
8942
9095
  return spec;
@@ -8964,7 +9117,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8964
9117
  const wire = (spec.wires || []).find((x) => x.id === d.id + "_cord"), pts = (wire ? wire.points : [d.plug]).map((p) => wl(o, p));
8965
9118
  const sid = w.power.pluggedInto(E.id, d.id);
8966
9119
  if (sid) {
8967
- const cut = sid.indexOf(":"), so = w.get(sid.slice(0, cut)), sk = so && so.spec.sockets.find((s) => s.id === sid.slice(cut + 1));
9120
+ const cut = sid.indexOf(":"), so = w.get(sid.slice(0, cut)), sk = so && (so.spec.sockets || []).find((s) => s.id === sid.slice(cut + 1));
8968
9121
  if (so && sk) {
8969
9122
  const sp = wl(so, sk.pos), dir = [Math.sin(so.transform.ry), Math.cos(so.transform.ry)], last = pts[pts.length - 1];
8970
9123
  if (last[1] < 0.3 + E.pos[1]) {
@@ -8999,9 +9152,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
8999
9152
  roomLightMask(sc.material);
9000
9153
  IV.screens.set(E.id, sc);
9001
9154
  }
9002
- E.screen.material = sc.material;
9003
- E.screen.castShadow = false;
9004
- E.screen.receiveShadow = false;
9155
+ const mesh2 = E.screen;
9156
+ mesh2.material = sc.material;
9157
+ mesh2.castShadow = false;
9158
+ mesh2.receiveShadow = false;
9005
9159
  }
9006
9160
  function ivEvent(h, e) {
9007
9161
  const IV = h.IV;
@@ -9037,16 +9191,17 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
9037
9191
  IV.itemsDirty = true;
9038
9192
  break;
9039
9193
  }
9040
- if (K.IVK.onEvent) {
9194
+ const ik = K.IVK;
9195
+ if (ik.onEvent) {
9041
9196
  try {
9042
- K.IVK.onEvent(e, h.IV.pub);
9197
+ ik.onEvent(e, IV.pub);
9043
9198
  } catch (err) {
9044
9199
  console.warn("interactables.onEvent", err);
9045
9200
  }
9046
9201
  }
9047
- if (e.type === "seat" && K.IVK.onSeat && e.actor === "player") {
9202
+ if (e.type === "seat" && ik.onSeat && e.actor === "player") {
9048
9203
  try {
9049
- K.IVK.onSeat(e.seated ? ivSeat(IV) : null, e);
9204
+ ik.onSeat(e.seated ? ivSeat(IV) : null, e);
9050
9205
  } catch (err) {
9051
9206
  console.warn("interactables.onSeat", err);
9052
9207
  }
@@ -9055,7 +9210,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
9055
9210
  function ivSeat(IV) {
9056
9211
  const w = IV.world, s = w.seatOf("player");
9057
9212
  if (!s) return null;
9058
- const o = w.get(s.object), seat = o.spec.seats.find((q) => q.id === s.seat), L = IV.k.GI.localToWorld, tr = o.transform, E = IV.entries.get(s.object);
9213
+ const o = w.get(s.object), seat = (o.spec.seats || []).find((q) => q.id === s.seat), L = IV.k.GI.localToWorld, tr = o.transform, E = IV.entries.get(s.object);
9059
9214
  const eye = L(tr, [seat.pos[0], seat.pos[1] + (seat.eye === void 0 ? seat.pose === "lie" ? 0.18 : 0.62 : seat.eye), seat.pos[2]]);
9060
9215
  return { object: s.object, seatInfo: s.seat, pose: seat.pose, eye, yaw: tr.ry + seat.ry, pitch: seat.pose === "lie" ? 0.55 : -0.08, exitPoint: L(tr, seat.exit), floor: E ? E.floor : 0, type: E ? E.type : null };
9061
9216
  }
@@ -9097,18 +9252,19 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
9097
9252
  }
9098
9253
  }
9099
9254
  const em = w.emitters();
9100
- if (em.length && !IV.efx && K.IVK.effects) {
9255
+ const ivk = K.IVK;
9256
+ if (em.length && !IV.efx && ivk.effects) {
9101
9257
  const g = h.houseGroup;
9102
- IV.efx = ik.gfx.createEffects(g, { maxParticles: 1400, seed: 11, document: DOC, ...K.IVK.effectSink ? { effects: K.IVK.effectSink } : {} });
9258
+ IV.efx = ik.gfx.createEffects(g, { maxParticles: 1400, seed: 11, document: DOC, ...ivk.effectSink ? { effects: ivk.effectSink } : {} });
9103
9259
  }
9104
9260
  if (IV.efx) {
9105
9261
  const alive = IV.efx.stats().alive;
9106
9262
  if (em.length || alive) {
9107
- IV.efx.update(dt, em, K.IVK.camera ? typeof K.IVK.camera === "function" ? K.IVK.camera() : K.IVK.camera : void 0);
9263
+ IV.efx.update(dt, em, ivk.camera ? typeof ivk.camera === "function" ? ivk.camera() : ivk.camera : void 0);
9108
9264
  invalidate();
9109
9265
  }
9110
9266
  }
9111
- if (K.IVK.items && IV.itemsDirty && IV.t - IV.itemsT > 0.1 && !IV.anim.size) ivItems(IV, h);
9267
+ if (ivk.items && IV.itemsDirty && IV.t - IV.itemsT > 0.1 && !IV.anim.size) ivItems(IV, h);
9112
9268
  }
9113
9269
  const IV_SHAPE = {
9114
9270
  can: [["cyl", 1, 1, 1, 0, 1]],
@@ -9143,7 +9299,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
9143
9299
  if (c.access && w.partT(E.id, c.access) < 0.02) continue;
9144
9300
  const st = w.contents(E.id, c.id);
9145
9301
  for (let i = 0; i < st.length; i++) {
9146
- if (st[i] && items[st[i].type]) list2.push([E, c.id, i, items[st[i].type]]);
9302
+ const stack = st[i];
9303
+ if (stack && items[stack.type]) list2.push([E, c.id, i, items[stack.type]]);
9147
9304
  }
9148
9305
  }
9149
9306
  }
@@ -9172,12 +9329,13 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
9172
9329
  gm.setIndex(new THREE.BufferAttribute(A.i.slice(0, A.ni), 1));
9173
9330
  gm.computeBoundingSphere();
9174
9331
  if (!IV.items) {
9175
- IV.items = new THREE.Mesh(gm, shared.mats.plain);
9176
- IV.items.name = "iv-items";
9177
- IV.items.castShadow = false;
9178
- IV.items.receiveShadow = true;
9179
- IV.items.frustumCulled = false;
9180
- h.houseGroup.add(IV.items);
9332
+ const mesh2 = new THREE.Mesh(gm, shared.mats.plain);
9333
+ IV.items = mesh2;
9334
+ mesh2.name = "iv-items";
9335
+ mesh2.castShadow = false;
9336
+ mesh2.receiveShadow = true;
9337
+ mesh2.frustumCulled = false;
9338
+ h.houseGroup.add(mesh2);
9181
9339
  } else {
9182
9340
  IV.items.geometry.dispose();
9183
9341
  IV.items.geometry = gm;
@@ -9311,7 +9469,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
9311
9469
  };
9312
9470
  return IV.pub;
9313
9471
  }
9314
- Object.assign(K, { IV_MATERIAL, IV_WIRE, IV_DOOR, ivGroup, ivAccumulate, ivBuild, ivRoomEffective, ivDraw, ivCords, ivAssemble, ivScreen, ivEvent, ivSeat, ivUpdate, IV_SHAPE, ivItems, ivTarget, ivRoomLight, ivParts, ivDispose, ivPublish });
9472
+ const defined = { IV_MATERIAL, IV_WIRE, IV_DOOR, ivGroup, ivAccumulate, ivBuild, ivRoomEffective, ivDraw, ivCords, ivAssemble, ivScreen, ivEvent, ivSeat, ivUpdate, IV_SHAPE, ivItems, ivTarget, ivRoomLight, ivParts, ivDispose, ivPublish };
9473
+ Object.assign(K, defined);
9315
9474
  }
9316
9475
 
9317
9476
  // src/animation/clotheslines.ts
@@ -9336,7 +9495,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
9336
9495
  }
9337
9496
  invalidate();
9338
9497
  }
9339
- Object.assign(K, { WIND, animateClotheslines });
9498
+ const defined = { WIND, animateClotheslines };
9499
+ Object.assign(K, defined);
9340
9500
  }
9341
9501
 
9342
9502
  // src/build/instance.ts
@@ -9361,7 +9521,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
9361
9521
  shared.quality = h.quality;
9362
9522
  }
9363
9523
  const tagDamage = (type) => ({ type, hp: HP[type], hpMax: HP[type] });
9364
- Object.assign(K, { props, TYPES, newState, enter, tagDamage });
9524
+ const defined = { props, TYPES, newState, enter, tagDamage };
9525
+ Object.assign(K, defined);
9365
9526
  }
9366
9527
 
9367
9528
  // ../game-floorplan-interactables/dist/index.js
@@ -10748,17 +10909,20 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
10748
10909
  for (const k of ["wood", "foliage", "flowers", "fruit", "spines"]) roomLightMask(m[k]);
10749
10910
  return m;
10750
10911
  })());
10751
- Object.assign(K, { IVK, VEG, vegMaterials });
10912
+ const defined = { IVK, VEG, vegMaterials };
10913
+ Object.assign(K, defined);
10752
10914
  }
10753
10915
 
10754
10916
  // src/lights/room-light.ts
10917
+ var newGroup = (k, house, exterior) => ({ k, house, ls: [], exterior, pos: [0, 0, 0], color: 0, n: 0, range: 0, rects: [], y: [0, 0], exclusion: null, lumens: 0, dist: 0, decay: 0, floor: 0, roomId: null, dm: 0, d: 0 });
10755
10918
  function installLightsRoomLight(K, shared) {
10756
10919
  const { THREE, rectsOf: rectsOf2, cfg } = K;
10757
10920
  const MAX_LIGHTS = 12, EMPTY = [1e9, 1e9, -1e9, -1e9];
10758
10921
  const LZU = { uRB0: { value: new Float32Array(MAX_LIGHTS * 4) }, uRB1: { value: new Float32Array(MAX_LIGHTS * 4) }, uRB2: { value: new Float32Array(MAX_LIGHTS * 4) }, uRBY: { value: new Float32Array(MAX_LIGHTS * 2) }, uREx: { value: new Float32Array(MAX_LIGHTS * 4) }, uIndK: { value: 0.06 } };
10759
10922
  for (let i = 0; i < MAX_LIGHTS; i++) for (const k of ["uRB0", "uRB1", "uRB2", "uREx"]) LZU[k].value.set(EMPTY, i * 4);
10760
- function roomLightMask(m) {
10761
- if (!m || m.userData && m.userData.roomLightMask || !(m.isMeshLambertMaterial || m.isMeshPhongMaterial || m.isMeshStandardMaterial || m.isMeshPhysicalMaterial)) return m;
10923
+ function roomLightMask(material) {
10924
+ const m = material;
10925
+ if (!m || m.userData && m.userData.roomLightMask || !(m.isMeshLambertMaterial || m.isMeshPhongMaterial || m.isMeshStandardMaterial || m.isMeshPhysicalMaterial)) return material;
10762
10926
  m.userData = m.userData || {};
10763
10927
  m.userData.roomLightMask = true;
10764
10928
  const prev = m.onBeforeCompile, pk = m.customProgramCacheKey;
@@ -10786,12 +10950,13 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
10786
10950
  return (pk ? pk.call(this) : "") + "|rm1";
10787
10951
  };
10788
10952
  m.needsUpdate = true;
10789
- return m;
10953
+ return material;
10790
10954
  }
10791
- function lighting(o) {
10792
- o = o || {};
10955
+ function lighting(options) {
10956
+ const o = options || {};
10793
10957
  const scene = o.scene, N = Math.max(1, Math.min(MAX_LIGHTS, o.n || 6)), NS = Math.max(0, Math.min(N, o.shadows != null ? o.shadows : 2)), size = o.shadowSize || 512, MAX_EXTERNAL = o.external != null ? o.external : 2;
10794
- for (const k of Object.keys(shared.mats)) roomLightMask(shared.mats[k]);
10958
+ const mats = shared.mats;
10959
+ for (const k of Object.keys(mats)) roomLightMask(mats[k]);
10795
10960
  const budget = o.budget, lease = budget ? budget.lease({ owner: "floorplan.rooms", point: N, shadows: NS }) : null;
10796
10961
  if (budget && !lease) throw new Error("lighting: the LightBudget has no " + N + " free point slots (" + NS + " with shadow) for the rooms");
10797
10962
  if (budget) for (const [key, width] of [["uRB0", 4], ["uRB1", 4], ["uRB2", 4], ["uRBY", 2], ["uREx", 4]]) {
@@ -10842,9 +11007,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
10842
11007
  }
10843
11008
  for (let i = 0; i < NS; i++) PL[i].shadow.needsUpdate = true;
10844
11009
  };
10845
- A.update = function(houses2, focus, op) {
10846
- op = op || {};
10847
- houses2 = (Array.isArray(houses2) ? houses2 : [houses2]).filter(Boolean);
11010
+ A.update = function(houseOrList, focus, options2) {
11011
+ const op = options2 || {}, houses2 = (Array.isArray(houseOrList) ? houseOrList : [houseOrList]).filter((h) => !!h);
10848
11012
  _f.set(focus.x, focus.y, focus.z);
10849
11013
  const rooms = [], exterior = [];
10850
11014
  for (const house of houses2) {
@@ -10856,7 +11020,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
10856
11020
  if (!house.lightOn(l.roomId)) continue;
10857
11021
  const k = l.type === "outdoor" ? "ext:" + (l.id || exterior.length) : l.roomId != null ? l.roomId : l.room;
10858
11022
  let g = by.get(k);
10859
- if (!g) by.set(k, g = { k, house, ls: [], exterior: l.type === "outdoor" });
11023
+ if (!g) by.set(k, g = newGroup(k, house, l.type === "outdoor"));
10860
11024
  g.ls.push(l);
10861
11025
  }
10862
11026
  for (const g of by.values()) {
@@ -10891,9 +11055,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
10891
11055
  }
10892
11056
  }
10893
11057
  g.exclusion = [x0 + 0.03, z0 + 0.03, x1 - 0.03, z1 - 0.03];
10894
- g.lumens = ls[0].lumens != null ? ls[0].lumens : 3.2;
10895
- g.dist = ls[0].dist != null ? ls[0].dist : 5.5;
10896
- g.decay = ls[0].decay != null ? ls[0].decay : 2;
11058
+ const l0 = ls[0];
11059
+ g.lumens = l0.lumens != null ? l0.lumens : 3.2;
11060
+ g.dist = l0.dist != null ? l0.dist : 5.5;
11061
+ g.decay = l0.decay != null ? l0.decay : 2;
10897
11062
  } else {
10898
11063
  const c = house.r.rooms.find((q) => q.id === g.k);
10899
11064
  if (!c) continue;
@@ -10994,8 +11159,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
10994
11159
  }
10995
11160
  const effectsByBudget = /* @__PURE__ */ new WeakMap();
10996
11161
  const OPENING_REACH = 2.5, OPENING_SIDE = 0.6;
10997
- function effectLights(o) {
10998
- o = o || {};
11162
+ function effectLights(options) {
11163
+ const o = options || {};
10999
11164
  const budget = o.budget;
11000
11165
  if (!budget) throw new Error("effectLights: a LightBudget is required");
11001
11166
  const known = effectsByBudget.get(budget);
@@ -11073,7 +11238,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
11073
11238
  const A = {
11074
11239
  /** Once per frame, after the budget's `present`: points the mask of every lit slot that is not the house's at the rooms its light reaches. */
11075
11240
  update(houses2) {
11076
- const indoors = cfg.effectLightsIndoors !== false, list2 = (Array.isArray(houses2) ? houses2 : [houses2]).filter(Boolean), cache2 = /* @__PURE__ */ new Map();
11241
+ const indoors = cfg.effectLightsIndoors !== false, list2 = (Array.isArray(houses2) ? houses2 : [houses2]).filter((h) => !!h), cache2 = /* @__PURE__ */ new Map();
11077
11242
  for (let at2 = 0; at2 < size; at2++) {
11078
11243
  if (ours.has(at2)) continue;
11079
11244
  const light = budget.group.children[at2];
@@ -11101,7 +11266,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
11101
11266
  if (!o.ours) effectsByBudget.set(budget, A);
11102
11267
  return A;
11103
11268
  }
11104
- Object.assign(K, { MAX_LIGHTS, EMPTY, LZU, roomLightMask, lighting, effectLights });
11269
+ const defined = { MAX_LIGHTS, EMPTY, LZU, roomLightMask, lighting, effectLights };
11270
+ Object.assign(K, defined);
11105
11271
  }
11106
11272
 
11107
11273
  // ../game-destruction/dist/index.js
@@ -11433,6 +11599,22 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
11433
11599
  return angle === 0 ? q : quat.mul(q, quat.fromAxisAngle([0, 0, 1], angle));
11434
11600
  };
11435
11601
  var clamp = (v, a, b) => Math.max(a, Math.min(b, v));
11602
+ var splitAlong = (box3, count, axis, rnd) => {
11603
+ const shares = Array.from({ length: count }, () => 0.25 + 1.5 * rnd());
11604
+ const total = shares.reduce((sum, share) => sum + share, 0);
11605
+ const out = [];
11606
+ let from = box3.c[axis] - box3.s[axis] / 2;
11607
+ for (const share of shares) {
11608
+ const length = box3.s[axis] * share / total;
11609
+ const c = [box3.c[0], box3.c[1], box3.c[2]];
11610
+ const size = [box3.s[0], box3.s[1], box3.s[2]];
11611
+ c[axis] = from + length / 2;
11612
+ size[axis] = length;
11613
+ out.push({ c, s: size });
11614
+ from += length;
11615
+ }
11616
+ return out;
11617
+ };
11436
11618
  var splinterBox = (inp, box3, limit = inp.maxFragments) => {
11437
11619
  const { rnd } = inp;
11438
11620
  const dims = box3.s;
@@ -11442,35 +11624,81 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
11442
11624
  const w = Math.max(fmin * 1.5, 0.01);
11443
11625
  const c = clamp(Math.round(dims[a] * dims[b] / (w * w)), 3, Math.max(3, Math.floor(limit / 2)));
11444
11626
  const maxLen = fmax * 4;
11445
- const cells = bsp(box3, c, [a, b], rnd);
11627
+ const cells = [];
11628
+ for (const cell of bsp(box3, c, [a, b], rnd)) {
11629
+ const wide = cell.s[a] >= cell.s[b] ? a : b;
11630
+ if (rnd() < 0.4 && cell.s[wide] > fmin * 0.5) for (const part of splitAlong(cell, 2, wide, rnd)) cells.push(part);
11631
+ else cells.push(cell);
11632
+ }
11446
11633
  const pieces = [];
11447
11634
  for (const cell of cells) {
11448
- const extra = rnd() < 0.4 ? 1 : 0;
11635
+ const extra = Math.floor(rnd() * 3);
11449
11636
  const k = Math.max(1, Math.ceil(dims[g] / maxLen)) + extra;
11450
- for (const p of bsp(cell, k, [g], rnd)) pieces.push(p);
11637
+ for (const p of splitAlong(cell, k, g, rnd)) pieces.push(p);
11451
11638
  }
11452
11639
  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] }));
11453
11640
  };
11641
+ var PLY = 3e-3;
11642
+ var PLY_GAP = 0.6;
11643
+ var PLY_SHEET = 0.012;
11644
+ var plyBreak = (inp, root, limit) => {
11645
+ const { rnd } = inp;
11646
+ const dims = root.s;
11647
+ const sorted = [0, 1, 2].sort((x, y) => dims[x] - dims[y]);
11648
+ const t = sorted[0];
11649
+ const [u, v] = [sorted[1], sorted[2]].sort((x, y) => x - y);
11650
+ const along = dims[u] >= dims[v] ? u : v;
11651
+ const across = along === u ? v : u;
11652
+ const layers = clamp(Math.round(dims[t] / PLY / 2), 2, 3);
11653
+ const pitch = dims[t] / layers;
11654
+ const th = Math.min(pitch, PLY_SHEET);
11655
+ const per = Math.max(2, Math.floor(limit / layers));
11656
+ const out = [];
11657
+ for (let k = 0; k < layers; k++) {
11658
+ const layer = { c: [root.c[0], root.c[1], root.c[2]], s: [dims[0], dims[1], dims[2]] };
11659
+ layer.c[t] = root.c[t] + (-dims[t] / 2 + (k + 0.5) * pitch) * (pitch > PLY_SHEET ? 1 : 1 + PLY_GAP);
11660
+ layer.s[t] = th;
11661
+ for (const cell of bsp(layer, per, [u, v], rnd)) {
11662
+ if (out.length >= limit) return out;
11663
+ if (rnd() < 0.35) {
11664
+ const strand = cell.s[across] * (0.15 + 0.2 * rnd());
11665
+ out.push({ center: cell.c, size: [cell.s[along], strand, th * 0.7], q: frame(along, across, t), shape: "splinter", weight: cell.s[along] * strand * th });
11666
+ } else {
11667
+ out.push({ center: cell.c, size: [cell.s[u], cell.s[v], th], q: frame(u, v, t, (rnd() - 0.5) * 0.5), shape: "scrap", weight: cell.s[u] * cell.s[v] * th });
11668
+ }
11669
+ }
11670
+ }
11671
+ return out;
11672
+ };
11673
+ var granules = (inp, box3, count) => bsp(box3, count, [0, 1, 2], inp.rnd).map((b) => ({ center: b.c, size: b.s, q: quat.identity(), shape: "chunk", weight: b.s[0] * b.s[1] * b.s[2] }));
11674
+ var CRUMBS = 6;
11454
11675
  var severAt = (inp, root, at2, gap) => {
11455
11676
  const dims = root.s;
11456
11677
  const L = [0, 1, 2].sort((x, y) => dims[y] - dims[x])[0];
11457
11678
  const half = dims[L] / 2;
11458
11679
  const lo = clamp(at2 - gap / 2, -half, half), hi = clamp(at2 + gap / 2, -half, half);
11459
11680
  const out = [];
11460
- const stub = (from, to) => {
11681
+ const fibrous = inp.form === "slivers" || inp.form === "plies";
11682
+ const [a, b] = [0, 1, 2].filter((i) => i !== L);
11683
+ const stub = (from, to, breakAtEnd) => {
11461
11684
  if (to - from < 0.02) return;
11462
11685
  const c = [root.c[0], root.c[1], root.c[2]], sz = [dims[0], dims[1], dims[2]];
11463
11686
  c[L] = (from + to) / 2;
11464
11687
  sz[L] = to - from;
11465
- out.push({ center: c, size: sz, q: quat.identity(), shape: "slab", weight: sz[0] * sz[1] * sz[2] });
11688
+ const weight = sz[0] * sz[1] * sz[2];
11689
+ if (fibrous) out.push({ center: c, size: [sz[L], sz[a], sz[b]], q: frame(L, a, b, breakAtEnd ? 0 : Math.PI), shape: "slab", weight });
11690
+ else out.push({ center: c, size: sz, q: quat.identity(), shape: "slab", weight });
11466
11691
  };
11467
- stub(-half, lo);
11468
- stub(hi, half);
11692
+ stub(-half, lo, true);
11693
+ stub(hi, half, false);
11469
11694
  const band = { c: [root.c[0], root.c[1], root.c[2]], s: [dims[0], dims[1], dims[2]] };
11470
11695
  band.c[L] = (lo + hi) / 2;
11471
11696
  band.s[L] = Math.max(hi - lo, 0.01);
11472
- if (inp.style === "snap") {
11473
- for (const b of bsp(band, Math.max(3, Math.min(6, inp.maxFragments - out.length)), [0, 1, 2], inp.rnd)) out.push({ center: b.c, size: b.s, q: quat.identity(), shape: "chunk", weight: b.s[0] * b.s[1] * b.s[2] });
11697
+ const room = inp.maxFragments - out.length;
11698
+ if (inp.style === "snap" && inp.form === "plies") for (const p of plyBreak(inp, band, Math.max(3, room))) out.push(p);
11699
+ else if (inp.style === "snap" && inp.form === "granules") for (const p of granules(inp, band, clamp(room, 3, 12))) out.push(p);
11700
+ else if (inp.style === "snap") {
11701
+ for (const b2 of bsp(band, Math.max(3, Math.min(6, room)), [0, 1, 2], inp.rnd)) out.push({ center: b2.c, size: b2.s, q: quat.identity(), shape: "chunk", weight: b2.s[0] * b2.s[1] * b2.s[2] });
11474
11702
  } else for (const p of splinterBox(inp, band, Math.max(3, inp.maxFragments - out.length))) out.push(p);
11475
11703
  return out;
11476
11704
  };
@@ -11503,8 +11731,18 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
11503
11731
  const [u, v] = [sorted[1], sorted[2]].sort((a, b) => a - b);
11504
11732
  if (style === "snap") {
11505
11733
  if (inp.cut) return severAt(inp, root, inp.cut.at, inp.cut.gap);
11734
+ if (inp.form === "plies") return plyBreak(inp, root, maxFragments);
11506
11735
  const n2 = Math.min(maxFragments, 2 + Math.floor(rnd() * 3));
11507
11736
  for (const b of bsp(root, n2, [0, 1, 2], rnd)) out.push({ center: b.c, size: b.s, q: idq, shape: "slab", weight: b.s[0] * b.s[1] * b.s[2] });
11737
+ if (inp.form !== "granules") return out;
11738
+ const crumbs = Math.min(CRUMBS, maxFragments - out.length);
11739
+ if (crumbs <= 0) return out;
11740
+ const band = { c: [0, 0, 0], s: [0, 0, 0] };
11741
+ for (let i = 0; i < 3; i++) {
11742
+ band.s[i] = Math.min(dims[i], Math.max(fmin, dims[i] * 0.2));
11743
+ band.c[i] = clamp(inp.hit[i], -dims[i] / 2 + band.s[i] / 2, dims[i] / 2 - band.s[i] / 2);
11744
+ }
11745
+ for (const p of granules(inp, band, crumbs)) out.push({ ...p, weight: p.weight * 0.5 });
11508
11746
  return out;
11509
11747
  }
11510
11748
  if (style === "crumble" || style === "none") {
@@ -11611,6 +11849,88 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
11611
11849
  }
11612
11850
  return marks;
11613
11851
  };
11852
+ var JOINT_GAP = 1e-4;
11853
+ var MIN_BEARING_SHARE = 0.25;
11854
+ var unsupportedCells = (cells, thin, dims, standing) => {
11855
+ if (thin === 1) return [];
11856
+ const [u, v] = planeAxes(thin);
11857
+ const bottom = -dims[v] / 2;
11858
+ const rowOf = (y) => Math.round((y - bottom) * 1e4);
11859
+ const rows = /* @__PURE__ */ new Map();
11860
+ for (let i = 0; i < cells.length; i++) {
11861
+ if (!standing(i)) continue;
11862
+ const cell = cells[i];
11863
+ const key = rowOf(cell.center[v] - cell.size[v] / 2);
11864
+ const row = rows.get(key);
11865
+ if (row) row.push(i);
11866
+ else rows.set(key, [i]);
11867
+ }
11868
+ const held = /* @__PURE__ */ new Set();
11869
+ const queue = [];
11870
+ for (const i of rows.get(0) ?? []) {
11871
+ held.add(i);
11872
+ queue.push(i);
11873
+ }
11874
+ const overlap = (a, b) => Math.min(a.center[u] + a.size[u] / 2, b.center[u] + b.size[u] / 2) - Math.max(a.center[u] - a.size[u] / 2, b.center[u] - b.size[u] / 2);
11875
+ const climb = () => {
11876
+ while (queue.length > 0) {
11877
+ const cell = cells[queue.pop()];
11878
+ for (const j of rows.get(rowOf(cell.center[v] + cell.size[v] / 2)) ?? []) {
11879
+ const other = cells[j];
11880
+ if (held.has(j) || overlap(cell, other) <= Math.max(JOINT_GAP, MIN_BEARING_SHARE * Math.min(cell.size[u], other.size[u]))) continue;
11881
+ held.add(j);
11882
+ queue.push(j);
11883
+ }
11884
+ }
11885
+ };
11886
+ for (const row of rows.values()) row.sort((a, b) => cells[a].center[u] - cells[b].center[u]);
11887
+ const touching = (a, b) => Math.abs(a.center[u] + a.size[u] / 2 - (b.center[u] - b.size[u] / 2)) <= 1e-3;
11888
+ for (let wedged = true; wedged; ) {
11889
+ climb();
11890
+ wedged = false;
11891
+ for (const row of rows.values()) {
11892
+ for (let k = 1; k < row.length - 1; k++) {
11893
+ const i = row[k];
11894
+ if (held.has(i) || !held.has(row[k - 1]) || !held.has(row[k + 1])) continue;
11895
+ if (!touching(cells[row[k - 1]], cells[i]) || !touching(cells[i], cells[row[k + 1]])) continue;
11896
+ held.add(i);
11897
+ queue.push(i);
11898
+ wedged = true;
11899
+ }
11900
+ }
11901
+ }
11902
+ const loose = [];
11903
+ for (const row of rows.values()) for (const i of row) if (!held.has(i)) loose.push(i);
11904
+ return loose;
11905
+ };
11906
+ var clumpCells = (cells, thin, cap) => {
11907
+ if (cells.length <= cap) return [...cells];
11908
+ const [u, v] = planeAxes(thin);
11909
+ let lo0 = Infinity, lo1 = Infinity, hi0 = -Infinity, hi1 = -Infinity;
11910
+ for (const cell of cells) {
11911
+ lo0 = Math.min(lo0, cell.center[u] - cell.size[u] / 2);
11912
+ hi0 = Math.max(hi0, cell.center[u] + cell.size[u] / 2);
11913
+ lo1 = Math.min(lo1, cell.center[v] - cell.size[v] / 2);
11914
+ hi1 = Math.max(hi1, cell.center[v] + cell.size[v] / 2);
11915
+ }
11916
+ let side = Math.sqrt((hi0 - lo0) * (hi1 - lo1) / cap);
11917
+ for (; ; ) {
11918
+ const bins = /* @__PURE__ */ new Map();
11919
+ for (const cell of cells) {
11920
+ const key = `${Math.floor((cell.center[u] - lo0) / side)}:${Math.floor((cell.center[v] - lo1) / side)}`;
11921
+ const bin = bins.get(key) ?? { lo: [Infinity, Infinity, Infinity], hi: [-Infinity, -Infinity, -Infinity] };
11922
+ for (let k = 0; k < 3; k++) {
11923
+ bin.lo[k] = Math.min(bin.lo[k], cell.center[k] - cell.size[k] / 2);
11924
+ bin.hi[k] = Math.max(bin.hi[k], cell.center[k] + cell.size[k] / 2);
11925
+ }
11926
+ bins.set(key, bin);
11927
+ }
11928
+ if (bins.size <= cap) {
11929
+ return [...bins.values()].map(({ lo, hi }) => ({ center: [(lo[0] + hi[0]) / 2, (lo[1] + hi[1]) / 2, (lo[2] + hi[2]) / 2], size: [hi[0] - lo[0], hi[1] - lo[1], hi[2] - lo[2]] }));
11930
+ }
11931
+ side *= 1.25;
11932
+ }
11933
+ };
11614
11934
  var MAX_SPALLS = 64;
11615
11935
  var thinAxisOf = (dims) => dims[0] <= dims[1] && dims[0] <= dims[2] ? 0 : dims[1] <= dims[2] ? 1 : 2;
11616
11936
  var resolveLayer = (def, index, mat) => ({ index, def, mat, energy: mat.penetration * def.thickness * 1e3 });
@@ -12590,6 +12910,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
12590
12910
  },
12591
12911
  {
12592
12912
  id: "wood_soft",
12913
+ splitForm: "slivers",
12593
12914
  density: 500,
12594
12915
  hpPerKg: 90,
12595
12916
  penetration: 9,
@@ -12635,6 +12956,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
12635
12956
  },
12636
12957
  {
12637
12958
  id: "wood_hard",
12959
+ splitForm: "slivers",
12638
12960
  density: 750,
12639
12961
  hpPerKg: 140,
12640
12962
  penetration: 14,
@@ -12679,6 +13001,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
12679
13001
  },
12680
13002
  {
12681
13003
  id: "plywood",
13004
+ splitForm: "plies",
12682
13005
  density: 600,
12683
13006
  hpPerKg: 70,
12684
13007
  penetration: 7,
@@ -12726,6 +13049,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
12726
13049
  },
12727
13050
  {
12728
13051
  id: "mdf",
13052
+ splitForm: "granules",
12729
13053
  density: 750,
12730
13054
  hpPerKg: 45,
12731
13055
  penetration: 6,
@@ -12892,6 +13216,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
12892
13216
  floor: 60
12893
13217
  }
12894
13218
  },
13219
+ ricochetDeg: 12,
12895
13220
  hitDebris: "chips",
12896
13221
  breakDebris: "dust",
12897
13222
  visual: { innerColor: 10197652 },
@@ -12923,6 +13248,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
12923
13248
  0.04,
12924
13249
  0.12
12925
13250
  ],
13251
+ ricochetDeg: 10,
12926
13252
  hitDebris: "chips",
12927
13253
  breakDebris: "dust",
12928
13254
  visual: { innerColor: 12085567 },
@@ -12980,6 +13306,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
12980
13306
  0.06,
12981
13307
  0.3
12982
13308
  ],
13309
+ ricochetDeg: 15,
12983
13310
  hitDebris: "chips",
12984
13311
  breakDebris: "dust",
12985
13312
  visual: { innerColor: 9407364 },
@@ -13237,6 +13564,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
13237
13564
  },
13238
13565
  {
13239
13566
  id: "green_wood",
13567
+ splitForm: "slivers",
13240
13568
  density: 650,
13241
13569
  hpPerKg: 5,
13242
13570
  penetration: 10,
@@ -13407,6 +13735,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
13407
13735
  0.03,
13408
13736
  0.15
13409
13737
  ],
13738
+ ricochetDeg: 8,
13410
13739
  hitDebris: "chips",
13411
13740
  breakDebris: "dust",
13412
13741
  thermal: {
@@ -13506,6 +13835,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
13506
13835
  0.02,
13507
13836
  0.08
13508
13837
  ],
13838
+ ricochetDeg: 8,
13509
13839
  hitDebris: "chips",
13510
13840
  breakDebris: "dust",
13511
13841
  thermal: {
@@ -13517,6 +13847,127 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
13517
13847
  "brittle"
13518
13848
  ],
13519
13849
  compressive: 1e5
13850
+ },
13851
+ {
13852
+ id: "mud",
13853
+ density: 1700,
13854
+ hpPerKg: 1,
13855
+ penetration: 5,
13856
+ ductility: 0.8,
13857
+ fracture: "crumble",
13858
+ fragmentSize: [
13859
+ 0.02,
13860
+ 0.08
13861
+ ],
13862
+ hitDebris: "dust",
13863
+ breakDebris: "dust",
13864
+ thermal: {
13865
+ conductivity: 0.5,
13866
+ heatCapacity: 3.2
13867
+ },
13868
+ tags: [
13869
+ "soil",
13870
+ "wet"
13871
+ ],
13872
+ compressive: 5e3
13873
+ },
13874
+ {
13875
+ id: "clay",
13876
+ density: 1800,
13877
+ hpPerKg: 5,
13878
+ penetration: 7,
13879
+ ductility: 0.05,
13880
+ fracture: "crumble",
13881
+ fragmentSize: [
13882
+ 0.02,
13883
+ 0.1
13884
+ ],
13885
+ hitDebris: "chips",
13886
+ breakDebris: "dust",
13887
+ thermal: {
13888
+ conductivity: 0.15,
13889
+ heatCapacity: 2.2
13890
+ },
13891
+ tags: [
13892
+ "soil",
13893
+ "brittle"
13894
+ ],
13895
+ compressive: 6e4
13896
+ },
13897
+ {
13898
+ id: "snow",
13899
+ density: 300,
13900
+ hpPerKg: 1,
13901
+ penetration: 3,
13902
+ ductility: 0.6,
13903
+ fracture: "crumble",
13904
+ fragmentSize: [
13905
+ 0.01,
13906
+ 0.05
13907
+ ],
13908
+ hitDebris: "dust",
13909
+ breakDebris: "dust",
13910
+ thermal: {
13911
+ conductivity: 0.1,
13912
+ heatCapacity: 2.1
13913
+ },
13914
+ tags: [
13915
+ "soil",
13916
+ "frozen"
13917
+ ],
13918
+ compressive: 1e3
13919
+ },
13920
+ {
13921
+ id: "cobble",
13922
+ density: 2650,
13923
+ hpPerKg: 25,
13924
+ penetration: 25,
13925
+ penetrationClass: "concrete",
13926
+ penetrationScale: 0.9,
13927
+ ductility: 0.03,
13928
+ fracture: "crumble",
13929
+ fragmentSize: [
13930
+ 0.05,
13931
+ 0.15
13932
+ ],
13933
+ ricochetDeg: 15,
13934
+ hitDebris: "chips",
13935
+ breakDebris: "chips",
13936
+ thermal: {
13937
+ conductivity: 0.2,
13938
+ heatCapacity: 2.5
13939
+ },
13940
+ tags: [
13941
+ "masonry",
13942
+ "brittle"
13943
+ ],
13944
+ compressive: 3e5
13945
+ },
13946
+ {
13947
+ id: "paver",
13948
+ density: 2300,
13949
+ hpPerKg: 14,
13950
+ penetration: 18,
13951
+ penetrationClass: "concrete",
13952
+ penetrationScale: 1,
13953
+ ductility: 0.03,
13954
+ fracture: "crumble",
13955
+ fragmentSize: [
13956
+ 0.04,
13957
+ 0.12
13958
+ ],
13959
+ ricochetDeg: 12,
13960
+ hitDebris: "chips",
13961
+ breakDebris: "chips",
13962
+ thermal: {
13963
+ conductivity: 0.15,
13964
+ heatCapacity: 2.5
13965
+ },
13966
+ tags: [
13967
+ "masonry",
13968
+ "brittle"
13969
+ ],
13970
+ compressive: 35e4
13520
13971
  }
13521
13972
  ];
13522
13973
  var material_hints_default = {
@@ -13729,6 +14180,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
13729
14180
  var BREACH_BLAST_FACTOR = 6;
13730
14181
  var BREACH_AREA = 0.25;
13731
14182
  var BREACH_BLAST_REACH = 0.12;
14183
+ var MEMBER_CUT_REACH = 0.5;
14184
+ var BLAST_REFERENCE_RESISTANCE = 200 * 1900 * 0.13;
14185
+ var BLAST_EDGE_RAGGED = 0.3;
14186
+ var BLAST_EDGE_LOOSE = 1.35;
14187
+ var BLAST_EDGE_WEAR = 0.6;
13732
14188
  var RESIDUAL_CAPACITY = 0.15;
13733
14189
  var SEVER_BAND = 0.1;
13734
14190
  var SEVER_ASPECT = 3;
@@ -14981,7 +15437,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
14981
15437
  if (j.st.open) continue;
14982
15438
  if (j.spec.kind === "rebar" && (isReinforced(o.parts[j.ia].spec) || isReinforced(o.parts[j.ib].spec))) continue;
14983
15439
  if (near && jointReach(o, j, near) > Math.max(radius, jointReachLimit(o, j))) continue;
14984
- j.st.hp -= span === void 0 ? amount : amount * Math.min(1, span / Math.max(jointLength(o, j), 1e-6));
15440
+ j.st.hp -= span === void 0 ? amount : Math.min(amount, j.st.maxHp) * Math.min(1, span / Math.max(jointLength(o, j), 1e-6));
14985
15441
  if (j.st.hp <= 0) {
14986
15442
  j.st.hp = 0;
14987
15443
  j.st.open = true;
@@ -15109,6 +15565,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
15109
15565
  const worn = (_a = p.rt).chips ?? (_a.chips = {});
15110
15566
  const cut = (_b = p.rt).craters ?? (_b.craters = {});
15111
15567
  const out = [];
15568
+ const wholeBefore = Object.values(worn).filter((w) => w === -1).length;
15112
15569
  const shot = kind === "ballistic";
15113
15570
  for (let i = 0; i < grid.length; i++) {
15114
15571
  const cell = grid[i];
@@ -15167,6 +15624,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
15167
15624
  emit({ ...base(o, src), type: "chip", part: p.spec.id, center, size: [cell.size[0], cell.size[1], cell.size[2]], rotation: q, fragments });
15168
15625
  debris(o, p, p.mat.breakDebris, center, vec.scale(dir ?? [0, 1, 0], -1), 6, 2, src);
15169
15626
  }
15627
+ if (Object.values(worn).filter((w) => w === -1).length > wholeBefore) dropUnsupported(o, p, src);
15170
15628
  if (p.spec.reinforcement) exposeRebar(o, p, src);
15171
15629
  for (const cell of out) addBreach(o, p, toWorld(c, q, cell.center), cell.size[u] * cell.size[v], energy, dir, src);
15172
15630
  if (p.structural && !isLost(p)) {
@@ -15270,10 +15728,122 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
15270
15728
  setStage(o, p, 1, {}, src);
15271
15729
  setStage(o, p, 2, { opening: { center: worldCenter, radius }, fragments }, src);
15272
15730
  debris(o, p, p.mat.breakDebris, worldCenter, vec.scale(dir ?? [0, 1, 0], -1), 14, 3, src);
15731
+ dropUnsupported(o, p, src);
15273
15732
  const reinforcement = p.spec.reinforcement;
15274
15733
  if (reinforcement) exposeRebar(o, p, src);
15275
15734
  if (p.structural) solveStructure(o);
15276
15735
  };
15736
+ const blastReachOf = (p, energy) => {
15737
+ const perKg = p.mat.hits?.blunt?.unitJoules ?? chipEnergy;
15738
+ const resistance = Math.max(1, perKg * p.mat.density * p.thickness);
15739
+ return BREACH_BLAST_REACH * Math.cbrt(Math.max(0, energy) / 1e3) * clamp2(Math.cbrt(BLAST_REFERENCE_RESISTANCE / resistance), 0.3, 2);
15740
+ };
15741
+ const blastUnits = (o, p, wp, energy, dist, src) => {
15742
+ var _a, _b;
15743
+ if (!p.staged || p.st.stripped || isLost(p) || chipEnergy <= 0) return false;
15744
+ const reach = blastReachOf(p, energy);
15745
+ if (dist >= reach) return false;
15746
+ const radius = Math.sqrt(reach * reach - dist * dist);
15747
+ const [u, v] = planeAxes(p.thin);
15748
+ const lp = toLocal(p.pos, p.q, objLocal(o, wp));
15749
+ const op = p.st.opening ? toLocal(p.pos, p.q, p.st.opening.center) : null;
15750
+ const grid = gridOf(p);
15751
+ const worn = (_a = p.rt).chips ?? (_a.chips = {});
15752
+ const perKg = p.mat.hits?.blunt?.unitJoules ?? chipEnergy;
15753
+ const lobes = [rnd() * 2 * Math.PI, rnd() * 2 * Math.PI];
15754
+ const out = [];
15755
+ for (let i = 0; i < grid.length; i++) {
15756
+ const cell = grid[i];
15757
+ if (worn[i] === -1) continue;
15758
+ if (op && p.st.opening && Math.hypot(cell.center[u] - op[u], cell.center[v] - op[v]) <= p.st.opening.radius) continue;
15759
+ const du = cell.center[u] - lp[u], dv = cell.center[v] - lp[v];
15760
+ const angle = Math.atan2(dv, du);
15761
+ const edge = radius * (1 + BLAST_EDGE_RAGGED * (0.5 * Math.sin(3 * angle + lobes[0]) + 0.3 * Math.sin(5 * angle + lobes[1]) + 0.4 * (rnd() - 0.5)));
15762
+ const d = Math.hypot(du, dv);
15763
+ if (d <= edge) {
15764
+ worn[i] = -1;
15765
+ out.push(i);
15766
+ } else if (d <= edge * BLAST_EDGE_LOOSE) {
15767
+ const mass = p.mat.density * cell.size[0] * cell.size[1] * cell.size[2];
15768
+ worn[i] = Math.max(worn[i] ?? 0, perKg * mass * BLAST_EDGE_WEAR);
15769
+ }
15770
+ }
15771
+ if (out.length === 0) return false;
15772
+ 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));
15773
+ for (const i of loose) worn[i] = -1;
15774
+ const blown = out.map((i) => grid[i]), fallen = loose.map((i) => grid[i]);
15775
+ const volume = (cells2) => cells2.reduce((sum, cell) => sum + cell.size[0] * cell.size[1] * cell.size[2], 0);
15776
+ const blownMass = Math.min(p.st.mass, p.mat.density * volume(blown));
15777
+ const fallenMass = Math.min(p.st.mass - blownMass, p.mat.density * volume(fallen));
15778
+ const speed = clamp2(Math.sqrt(2 * energy * 0.02 / Math.max(blownMass, 0.05)), 1, 12);
15779
+ const { c, q } = partXf(o, p);
15780
+ const chipped = (_b = p.st).chipped ?? (_b.chipped = []);
15781
+ const tag = `x${chipped.length}_`;
15782
+ const fragments = [
15783
+ ...cellFragments(o, p, clumpCells(blown, p.thin, maxFragments), wp, null, speed, blownMass, tag, true),
15784
+ ...fallen.length > 0 ? cellFragments(o, p, clumpCells(fallen, p.thin, Math.max(2, Math.round(maxFragments / 4))), wp, null, 0.3, fallenMass, `${tag}f`, true) : []
15785
+ ];
15786
+ const cells = [...blown, ...fallen];
15787
+ const holes = localHolesOf(p);
15788
+ p.st.holes = p.st.holes.filter((_, k) => !cells.some((cell) => marksInBox(cell.center, cell.size, void 0, [holes[k]]).length > 0));
15789
+ const lo = [Infinity, Infinity, Infinity], hi = [-Infinity, -Infinity, -Infinity];
15790
+ for (const cell of cells) {
15791
+ chipped.push({ center: [cell.center[0], cell.center[1], cell.center[2]], size: [cell.size[0], cell.size[1], cell.size[2]] });
15792
+ for (let k = 0; k < 3; k++) {
15793
+ lo[k] = Math.min(lo[k], cell.center[k] - cell.size[k] / 2);
15794
+ hi[k] = Math.max(hi[k], cell.center[k] + cell.size[k] / 2);
15795
+ }
15796
+ }
15797
+ p.st.mass = Math.max(0, p.st.mass - blownMass - fallenMass);
15798
+ p.rt.removed = (p.rt.removed ?? 0) + blownMass + fallenMass;
15799
+ markDamaged(p);
15800
+ setStage(o, p, 1, {}, src);
15801
+ const center = toWorld(c, q, [(lo[0] + hi[0]) / 2, (lo[1] + hi[1]) / 2, (lo[2] + hi[2]) / 2]);
15802
+ const units = cells.map((cell) => ({ center: toWorld(c, q, cell.center), size: [cell.size[0], cell.size[1], cell.size[2]] }));
15803
+ emit({ ...base(o, src), type: "chip", part: p.spec.id, center, size: [hi[0] - lo[0], hi[1] - lo[1], hi[2] - lo[2]], rotation: q, fragments, units });
15804
+ debris(o, p, p.mat.breakDebris, center, vec.normalize(vec.sub(center, wp), [0, 1, 0]), 14, 3, src);
15805
+ if (p.spec.reinforcement) exposeRebar(o, p, src);
15806
+ if (p.structural && !isLost(p)) {
15807
+ solveStructure(o);
15808
+ collapseCheck(o, src);
15809
+ }
15810
+ return true;
15811
+ };
15812
+ const dropUnsupported = (o, p, src) => {
15813
+ var _a, _b;
15814
+ if (!p.spec.units || p.spec.units.mortar === p.spec.material || p.st.stripped || isLost(p)) return 0;
15815
+ const [u, v] = planeAxes(p.thin);
15816
+ const grid = gridOf(p);
15817
+ const worn = (_a = p.rt).chips ?? (_a.chips = {});
15818
+ const op = p.st.opening ? toLocal(p.pos, p.q, p.st.opening.center) : null;
15819
+ const opened = (cell) => !!op && !!p.st.opening && Math.hypot(cell.center[u] - op[u], cell.center[v] - op[v]) <= p.st.opening.radius;
15820
+ const loose = unsupportedCells(grid, p.thin, p.dims, (i) => worn[i] !== -1 && !opened(grid[i]));
15821
+ if (loose.length === 0) return 0;
15822
+ for (const i of loose) worn[i] = -1;
15823
+ const fallen = loose.map((i) => grid[i]);
15824
+ const mass = Math.min(p.st.mass, p.mat.density * fallen.reduce((sum, cell) => sum + cell.size[0] * cell.size[1] * cell.size[2], 0));
15825
+ const { c, q } = partXf(o, p);
15826
+ const chipped = (_b = p.st).chipped ?? (_b.chipped = []);
15827
+ const lo = [Infinity, Infinity, Infinity], hi = [-Infinity, -Infinity, -Infinity];
15828
+ for (const cell of fallen) {
15829
+ for (let k = 0; k < 3; k++) {
15830
+ lo[k] = Math.min(lo[k], cell.center[k] - cell.size[k] / 2);
15831
+ hi[k] = Math.max(hi[k], cell.center[k] + cell.size[k] / 2);
15832
+ }
15833
+ }
15834
+ const center = toWorld(c, q, [(lo[0] + hi[0]) / 2, (lo[1] + hi[1]) / 2, (lo[2] + hi[2]) / 2]);
15835
+ const fragments = cellFragments(o, p, clumpCells(fallen, p.thin, Math.max(2, Math.round(maxFragments / 4))), vec.add(center, [0, 1, 0]), null, 0.3, mass, `u${chipped.length}_`, true);
15836
+ const holes = localHolesOf(p);
15837
+ p.st.holes = p.st.holes.filter((_, k) => !fallen.some((cell) => marksInBox(cell.center, cell.size, void 0, [holes[k]]).length > 0));
15838
+ for (const cell of fallen) chipped.push({ center: [cell.center[0], cell.center[1], cell.center[2]], size: [cell.size[0], cell.size[1], cell.size[2]] });
15839
+ p.st.mass = Math.max(0, p.st.mass - mass);
15840
+ p.rt.removed = (p.rt.removed ?? 0) + mass;
15841
+ markDamaged(p);
15842
+ const units = fallen.map((cell) => ({ center: toWorld(c, q, cell.center), size: [cell.size[0], cell.size[1], cell.size[2]] }));
15843
+ emit({ ...base(o, src), type: "chip", part: p.spec.id, center, size: [hi[0] - lo[0], hi[1] - lo[1], hi[2] - lo[2]], rotation: q, fragments, units });
15844
+ debris(o, p, p.mat.breakDebris, center, [0, -1, 0], 6, 1, src);
15845
+ return loose.length;
15846
+ };
15277
15847
  const stripPart = (o, p, reinforcement, src) => {
15278
15848
  const bars = rebarBars(p.dims, p.thin, reinforcement.spacing);
15279
15849
  p.st.stripped = true;
@@ -15340,6 +15910,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
15340
15910
  const unitPlan = p.spec.units ? unitFragmentPlan(p, maxFragments * UNIT_FRAGMENT_FACTOR) : [];
15341
15911
  const axis = style === "splinter" || style === "snap" ? severAxis(p) : -1;
15342
15912
  const cutAt2 = axis < 0 ? void 0 : p.rt.cutAt ?? (origin ? hitLocal[axis] : void 0);
15913
+ const planRnd = p.mat.splitForm ? splitStream(o.id, p.spec.id, hitLocal) : rnd;
15343
15914
  const plan = unitPlan.length > 0 ? unitPlan : planFragments({
15344
15915
  style,
15345
15916
  dims: p.dims,
@@ -15348,8 +15919,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
15348
15919
  hit: hitLocal,
15349
15920
  fragmentSize: p.mat.fragmentSize ?? [0.04, 0.2],
15350
15921
  maxFragments,
15351
- rnd,
15922
+ rnd: planRnd,
15352
15923
  sheet: p.mat.sheet,
15924
+ ...p.mat.splitForm ? { form: p.mat.splitForm } : {},
15353
15925
  ...cutAt2 !== void 0 ? { cut: { at: cutAt2, gap: SEVER_BAND } } : {}
15354
15926
  });
15355
15927
  const holes = p.st.holes.length > 0 ? localHolesOf(p) : [];
@@ -15488,7 +16060,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
15488
16060
  const fragments = [];
15489
16061
  for (let i = 0; i < n; i++) {
15490
16062
  const back = !exit || i % 3 === 2;
15491
- const len = scale * (fibre ? 1.2 + 1.2 * rnd() : 3 + 4 * rnd()), w = scale * (0.8 + 0.8 * rnd()), t = scale * (fibre ? 0.7 + 0.6 * rnd() : 0.4 + 0.4 * rnd());
16063
+ const plies = p.mat.splitForm === "plies";
16064
+ const len = scale * (fibre ? 1.2 + 1.2 * rnd() : 3 + 4 * rnd()), w = scale * (0.8 + 0.8 * rnd()), t = scale * (fibre ? 0.7 + 0.6 * rnd() : plies ? 0.15 + 0.15 * rnd() : 0.4 + 0.4 * rnd());
15492
16065
  const size = [w, w, w];
15493
16066
  size[along] = len;
15494
16067
  size[(along + 1) % 3] = w;
@@ -15649,7 +16222,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
15649
16222
  checkIgnite(o, p, src);
15650
16223
  }
15651
16224
  }
15652
- wearJoints(o, p, Ein * 0.5, void 0, 0, src);
16225
+ wearJoints(o, p, Ein * 0.5, void 0, 0, src, false, 2 * BREACH_BLAST_REACH * Math.cbrt(Math.max(0, energy) / 1e3));
15653
16226
  const amount = Ein * DMG_PER_J * (mat.damageScale?.explosive ?? 1);
15654
16227
  if (blastFrontSpeed > 0 && dist > 0 && mat.tags?.includes("glass")) {
15655
16228
  frontQueue.push({ o, p, due: time + dist / blastFrontSpeed, amount, fragEnergy: Ein * 0.3, point, ...src !== void 0 ? { src } : {} });
@@ -15659,7 +16232,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
15659
16232
  const dealt = applyDamage(o, p, amount, "explosive", point, null, Ein * 0.3, 40, src);
15660
16233
  res.damage += dealt;
15661
16234
  if (p.bend && flexure.enabled) bendByBlast(o, p, point, energy, radius, reach, src);
15662
- if (dist <= BREACH_BLAST_REACH * Math.cbrt(Math.max(0, energy) / 1e3)) addBreach(o, p, wp, breachAreaOf(p, dealt, true), Ein, null, src);
16235
+ const breachReach = BREACH_BLAST_REACH * Math.cbrt(Math.max(0, energy) / 1e3);
16236
+ if (p.spec.units && p.spec.units.mortar !== p.spec.material) blastUnits(o, p, wp, energy, dist, src);
16237
+ else if (p.spec.reinforcement && p.spec.units && dist > MEMBER_CUT_REACH * breachReach) blastUnits(o, p, wp, energy, dist, src);
16238
+ else if (dist <= breachReach) addBreach(o, p, wp, breachAreaOf(p, dealt, true), Ein, null, src);
15663
16239
  return reach;
15664
16240
  };
15665
16241
  const blastProbe = { partsTested: 0, partsNear: 0 };
@@ -15905,6 +16481,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
15905
16481
  }
15906
16482
  return h | 0;
15907
16483
  };
16484
+ const splitStream = (objectId, partId, hit) => {
16485
+ const codes = Array.from(`${objectId}|${partId}`, (char) => char.charCodeAt(0) / 1e3);
16486
+ const stream = createRng(seedOf2(rng2.state() / 1e3, ...codes, hit[0], hit[1], hit[2]));
16487
+ return () => stream.next();
16488
+ };
15908
16489
  const throwFragment = (frag, objectId, from, g, centre, index, src, shields, results, reports, mult) => {
15909
16490
  const v = frag.velocity;
15910
16491
  const speed = Math.hypot(v[0], v[1], v[2]);
@@ -17118,8 +17699,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
17118
17699
 
17119
17700
  // ../game-floorplan-structure/dist/index.js
17120
17701
  var __defProp2 = Object.defineProperty;
17121
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
17122
- var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
17702
+ var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
17703
+ var __publicField2 = (obj, key, value) => __defNormalProp2(obj, typeof key !== "symbol" ? key + "" : key, value);
17123
17704
  var uniform = (joint) => ({ wall: joint, slab: joint, beam: joint, column: joint, truss: joint });
17124
17705
  var CONSTRUCTION_SYSTEMS = /* @__PURE__ */ new Map([
17125
17706
  ["masonry", { walls: "brick_wall_plastered", joints: { wall: "mortar", slab: "rebar", beam: "mortar", column: "rebar", truss: "bolt" }, slabThickness: 0.12, plateThickness: 0.2, timber: false, wallRole: "rules", wallFill: 1 }],
@@ -17253,9 +17834,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
17253
17834
  var densityOf = (material) => DENSITY[material] ?? DEFAULT_DENSITY;
17254
17835
  var StructureBuilder = class {
17255
17836
  constructor() {
17256
- __publicField(this, "parts", []);
17257
- __publicField(this, "supports", []);
17258
- __publicField(this, "ids", /* @__PURE__ */ new Set());
17837
+ __publicField2(this, "parts", []);
17838
+ __publicField2(this, "supports", []);
17839
+ __publicField2(this, "ids", /* @__PURE__ */ new Set());
17259
17840
  }
17260
17841
  add(draft, options = {}) {
17261
17842
  const { id, role, material, aabb, y0, y1 } = draft;
@@ -17321,12 +17902,15 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
17321
17902
  builder,
17322
17903
  porchRoofOnPostsOnly: options.porchRoofOnPostsOnly,
17323
17904
  rafterRoof: options.rafterRoof,
17905
+ wallPillarSpacing: options.wallPillarSpacing,
17906
+ visorSections: options.visorSections,
17324
17907
  floorCount: floorCount2,
17325
17908
  isUnit,
17326
17909
  elevation,
17327
17910
  isTimber: system.timber,
17328
17911
  walls: [],
17329
17912
  wallParts: /* @__PURE__ */ new Map(),
17913
+ groundBeams: /* @__PURE__ */ new Map(),
17330
17914
  carriers: /* @__PURE__ */ new Map(),
17331
17915
  plates: /* @__PURE__ */ new Map(),
17332
17916
  pillars: /* @__PURE__ */ new Map(),
@@ -17397,7 +17981,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
17397
17981
  builder.rest(deck, base, system.joints.slab);
17398
17982
  continue;
17399
17983
  }
17400
- const pillar = builder.add({ id: `balcony_column_${room.id}`, role: "column", material: construction.frame.pillars, aabb: [box22[0] - 0.08, box22[1] - 0.08, box22[0] + 0.08, box22[1] + 0.08], y0: 0, y1: context.level(floor) - system.slabThickness }, { floor: 0 });
17984
+ const half = context.visorSections ? 0.08 : 0.1;
17985
+ const pillar = builder.add({ id: `balcony_column_${room.id}`, role: "column", material: construction.frame.pillars, aabb: [box22[0] - half, box22[1] - half, box22[0] + half, box22[1] + half], y0: 0, y1: context.level(floor) - system.slabThickness }, { floor: 0 });
17401
17986
  builder.rest(pillar, GROUND, "rebar");
17402
17987
  builder.rest(deck, pillar, "rebar");
17403
17988
  }
@@ -17462,8 +18047,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
17462
18047
  const top = context.level(floor) - thickness * 0.15;
17463
18048
  const out = [];
17464
18049
  for (let k = 0; k < count; k++) {
17465
- const centre = (alongU ? box22[0] : box22[1]) + 0.05 + k * (span - 0.1) / (count - 1);
17466
- const aabb = alongU ? [centre - 0.025, box22[1], centre + 0.025, box22[3]] : [box22[0], centre - 0.025, box22[2], centre + 0.025];
18050
+ const centre2 = (alongU ? box22[0] : box22[1]) + 0.05 + k * (span - 0.1) / (count - 1);
18051
+ const aabb = alongU ? [centre2 - 0.025, box22[1], centre2 + 0.025, box22[3]] : [box22[0], centre2 - 0.025, box22[2], centre2 + 0.025];
17467
18052
  const joist = context.builder.add({ id: `joist_${floor}${suffix}_${k}`, role: "beam", material, aabb, y0: context.level(floor) - thickness, y1: top }, { floor, fill: 1, spacing: JOIST_PITCH });
17468
18053
  out.push(joist);
17469
18054
  const below = carriers.filter((carrier) => carrier.y0 <= top + 0.05);
@@ -17551,8 +18136,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
17551
18136
  const at2 = alongU ? box22[1] + line * (box22[3] - box22[1]) / (rows - 1) : box22[0] + line * (box22[2] - box22[0]) / (columns - 1);
17552
18137
  const aabb = alongU ? [box22[0], at2 - 0.07, box22[2], at2 + 0.07] : [at2 - 0.07, box22[1], at2 + 0.07, box22[3]];
17553
18138
  const beam = builder.add({ id: `base_beam_${line}`, role: "beam", material: construction.frame.timber, aabb, y0: height, y1: elevation - context.system.slabThickness }, { floor: 0 });
17554
- const centre = (q) => alongU ? (q.aabb[1] + q.aabb[3]) / 2 : (q.aabb[0] + q.aabb[2]) / 2;
17555
- for (const pier of piers.filter((q) => Math.abs(centre(q) - at2) < 0.05)) builder.rest(beam, pier, "bolt");
18139
+ const centre2 = (q) => alongU ? (q.aabb[1] + q.aabb[3]) / 2 : (q.aabb[0] + q.aabb[2]) / 2;
18140
+ for (const pier of piers.filter((q) => Math.abs(centre2(q) - at2) < 0.05)) builder.rest(beam, pier, "bolt");
17556
18141
  beams.push(beam);
17557
18142
  }
17558
18143
  return beams;
@@ -17572,17 +18157,110 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
17572
18157
  else if (type === "crawl_space" || type === "piles" || type === "basement") addRaisedFloor(context);
17573
18158
  else if (context.isTimber && type !== "ground") context.base0 = raft(context, Math.max(0.12, context.elevation), "concrete");
17574
18159
  }
18160
+ var GROUND_BEAM_DEPTH = 0.3;
18161
+ function addGroundBeams(context) {
18162
+ const { builder, construction } = context;
18163
+ if (construction.system !== "masonry" || context.isUnit || construction.foundation.type !== "strip_footing") return;
18164
+ const top = context.level(0);
18165
+ for (const wall of context.walls) {
18166
+ if (wall.floor !== 0 || wall.type !== "wall") continue;
18167
+ const beam = builder.add({ id: `ground_beam_${context.groundBeams.size}`, role: "beam", material: construction.frame.beams, aabb: wall.box, y0: top - GROUND_BEAM_DEPTH, y1: top }, { floor: 0, beamKind: "ground_beam" });
18168
+ builder.rest(beam, GROUND, "rebar");
18169
+ context.groundBeams.set(wall.id, beam);
18170
+ }
18171
+ }
18172
+ var WALL_PILLAR_SPACING = 4;
18173
+ var WALL_PILLAR_SIDE = 0.2;
18174
+ var MERGE_DISTANCE = 0.45;
18175
+ var HALF = WALL_PILLAR_SIDE / 2;
18176
+ var TOLERANCE = 0.03;
18177
+ var alongRange = (wall) => wall.axis === "u" ? [Math.min(wall.u0, wall.u1), Math.max(wall.u0, wall.u1)] : [Math.min(wall.v0, wall.v1), Math.max(wall.v0, wall.v1)];
18178
+ var acrossOf = (wall) => wall.axis === "u" ? wall.v0 : wall.u0;
18179
+ function stretchesOf(context, wall) {
18180
+ const [low, high] = alongRange(wall);
18181
+ const windows = (context.source.windows ?? []).filter((w) => w.floor === wall.floor && w.axis === wall.axis && Math.abs(w.pos - acrossOf(wall)) <= TOLERANCE && w.at + w.width / 2 > low && w.at - w.width / 2 < high).map((w) => [w.at - w.width / 2, w.at + w.width / 2]).sort((a, b) => a[0] - b[0]);
18182
+ const out = [];
18183
+ let cursor = low;
18184
+ for (const [from, to] of windows) {
18185
+ if (from - cursor > TOLERANCE) out.push({ wall, from: cursor, to: from });
18186
+ cursor = Math.max(cursor, to);
18187
+ }
18188
+ if (high - cursor > TOLERANCE) out.push({ wall, from: cursor, to: high });
18189
+ return out;
18190
+ }
18191
+ function junctionsOf(context, wall) {
18192
+ const [low, high] = alongRange(wall);
18193
+ const line = acrossOf(wall);
18194
+ const out = [];
18195
+ for (const other of context.walls) {
18196
+ if (other === wall || other.floor !== wall.floor || other.axis === wall.axis || other.type !== "wall") continue;
18197
+ const at2 = acrossOf(other);
18198
+ const [a, b] = alongRange(other);
18199
+ const meets = Math.abs(a - line) <= (wall.thickness ?? 0.15) / 2 + TOLERANCE || Math.abs(b - line) <= (wall.thickness ?? 0.15) / 2 + TOLERANCE;
18200
+ if (meets && at2 > low + HALF && at2 < high - HALF) out.push(at2);
18201
+ }
18202
+ return out;
18203
+ }
18204
+ function pillarStations(from, to, junctions, spacing) {
18205
+ const length = to - from;
18206
+ if (length < WALL_PILLAR_SIDE - 1e-6) return [];
18207
+ if (length < 2 * WALL_PILLAR_SIDE) return [(from + to) / 2];
18208
+ if (length < WALL_PILLAR_SIDE + MERGE_DISTANCE) return [from + HALF, to - HALF];
18209
+ const fixed = [from + HALF, ...junctions.filter((at2) => at2 > from + HALF + MERGE_DISTANCE && at2 < to - HALF - MERGE_DISTANCE), to - HALF].sort((a, b) => a - b);
18210
+ const out = [];
18211
+ fixed.forEach((at2, k) => {
18212
+ out.push(at2);
18213
+ const next = fixed[k + 1];
18214
+ if (next === void 0) return;
18215
+ const spans = Math.ceil((next - at2) / spacing - 1e-6);
18216
+ for (let s = 1; s < spans; s++) out.push(at2 + (next - at2) * s / spans);
18217
+ });
18218
+ return out;
18219
+ }
18220
+ function addWallPillars(context, walls, floor, plates, taken, spacing) {
18221
+ const { builder } = context;
18222
+ const pillars = [];
18223
+ const placed = taken.map((p) => [(p.aabb[0] + p.aabb[2]) / 2, (p.aabb[1] + p.aabb[3]) / 2]);
18224
+ const base = context.baseOf(floor);
18225
+ for (const wall of walls) {
18226
+ if (!wall.exterior || wall.type !== "wall") continue;
18227
+ const top = context.wallParts.get(wall.id).y1;
18228
+ const across = acrossOf(wall);
18229
+ const junctions = junctionsOf(context, wall);
18230
+ for (const stretch of stretchesOf(context, wall)) {
18231
+ const standing = (end) => placed.map(([pu, pv]) => wall.axis === "u" ? [pu, pv] : [pv, pu]).find(([a, c]) => Math.abs(c - across) <= TOLERANCE && Math.abs(a - end) < MERGE_DISTANCE + HALF)?.[0];
18232
+ const first = standing(stretch.from);
18233
+ const last = standing(stretch.to);
18234
+ const from = first === void 0 ? stretch.from : Math.min(stretch.from, first - HALF);
18235
+ const to = last === void 0 ? stretch.to : Math.max(stretch.to, last + HALF);
18236
+ for (const along of pillarStations(from, to, junctions, spacing)) {
18237
+ const [u, v] = wall.axis === "u" ? [along, across] : [across, along];
18238
+ const end = Math.abs(along - stretch.from) < Math.abs(along - stretch.to) ? stretch.from : stretch.to;
18239
+ const endPoint = wall.axis === "u" ? [end, across] : [across, end];
18240
+ const isEnd = Math.abs(along - end) <= HALF + TOLERANCE;
18241
+ if (placed.some(([pu, pv]) => Math.hypot(pu - u, pv - v) < MERGE_DISTANCE && (!isEnd || Math.hypot(pu - endPoint[0], pv - endPoint[1]) < MERGE_DISTANCE))) continue;
18242
+ placed.push([u, v]);
18243
+ const box22 = [u - HALF, v - HALF, u + HALF, v + HALF];
18244
+ const pillar = builder.add({ id: `wall_column_${floor}_${pillars.length}`, role: "column", material: context.construction.frame.pillars, aabb: box22, y0: context.level(floor), y1: top }, { floor });
18245
+ builder.rest(pillar, base === GROUND || !base ? GROUND : base, "rebar");
18246
+ for (const plate of plates.filter((p) => touches(p.aabb, box22, 0.01))) builder.rest(plate, pillar, "rebar");
18247
+ pillars.push(pillar);
18248
+ }
18249
+ }
18250
+ }
18251
+ return pillars;
18252
+ }
17575
18253
  var MAX_GAP_WITHIN_RUN = 3.2;
17576
18254
  var POST_SPACING = 2.4;
17577
18255
  var POST_SIDE = 0.05;
17578
18256
  var alongOf = (wall, pick2) => wall.axis === "u" ? pick2(wall.u0, wall.u1) : pick2(wall.v0, wall.v1);
17579
- var acrossOf = (wall) => wall.axis === "u" ? wall.v0 : wall.u0;
18257
+ var acrossOf2 = (wall) => wall.axis === "u" ? wall.v0 : wall.u0;
17580
18258
  function runsOf(context, walls, floor) {
17581
18259
  const lines = /* @__PURE__ */ new Map();
17582
18260
  for (const wall of walls) {
17583
18261
  if (!wall.exterior || wall.type !== "wall") continue;
17584
18262
  const top = round2(context.wallParts.get(wall.id).y1);
17585
- const key = `${floor}|${wall.axis}|${acrossOf(wall).toFixed(2)}|${top.toFixed(2)}`;
18263
+ const key = `${floor}|${wall.axis}|${acrossOf2(wall).toFixed(2)}|${top.toFixed(2)}`;
17586
18264
  lines.set(key, [...lines.get(key) ?? [], wall]);
17587
18265
  }
17588
18266
  return [...lines.values()].map((line) => {
@@ -17602,7 +18280,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
17602
18280
  }
17603
18281
  function addShackPosts(context, run, plate, floor, index) {
17604
18282
  const first = run.walls[0];
17605
- const across = acrossOf(first);
18283
+ const across = acrossOf2(first);
17606
18284
  const top = context.wallParts.get(first.id).y1;
17607
18285
  const count = Math.max(1, Math.ceil((run.to - run.from) / POST_SPACING));
17608
18286
  const posts = [];
@@ -17628,7 +18306,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
17628
18306
  for (const run of runs) {
17629
18307
  const first = run.walls[0];
17630
18308
  const half = (first.thickness ?? 0.15) / 2;
17631
- const across = acrossOf(first);
18309
+ const across = acrossOf2(first);
17632
18310
  const top = context.wallParts.get(first.id).y1;
17633
18311
  const aabb = first.axis === "u" ? [run.from - half, across - half, run.to + half, across + half] : [across - half, run.from - half, across + half, run.to + half];
17634
18312
  const material = context.isTimber ? construction.frame.timber : construction.frame.beams;
@@ -17667,7 +18345,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
17667
18345
  function addPlatesAndPillars(context, floor) {
17668
18346
  const walls = context.walls.filter((wall) => wall.floor === floor);
17669
18347
  const { plates, pillars } = addPlates(context, walls, floor);
17670
- if (context.construction.system === "masonry") pillars.push(...addCornerPillars(context, walls, floor, plates));
18348
+ if (context.construction.system === "masonry") {
18349
+ const corners = addCornerPillars(context, walls, floor, plates);
18350
+ pillars.push(...corners);
18351
+ if (context.wallPillarSpacing) pillars.push(...addWallPillars(context, walls, floor, plates, corners, context.wallPillarSpacing));
18352
+ }
17671
18353
  context.pillars.set(floor, pillars);
17672
18354
  context.plates.set(floor, plates);
17673
18355
  const innerBearing = walls.filter((wall) => wall.role === "wall" && !wall.exterior).map((wall) => context.wallParts.get(wall.id));
@@ -17942,21 +18624,21 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
17942
18624
  builder.rest(flight, builder.find("basement_slab"), "resting");
17943
18625
  if (context.base0 && context.base0 !== GROUND) builder.rest(flight, context.base0, "nail");
17944
18626
  }
17945
- var TOLERANCE = 0.02;
18627
+ var TOLERANCE2 = 0.02;
17946
18628
  var DOOR_HEIGHT = 2.1;
17947
18629
  var LINTEL_DEPTH = 0.2;
17948
18630
  var LINTEL_BEARING = 0.2;
17949
18631
  var crossOf = (wall) => wall.axis === "u" ? wall.v0 : wall.u0;
17950
- var alongRange = (wall) => wall.axis === "u" ? [Math.min(wall.u0, wall.u1), Math.max(wall.u0, wall.u1)] : [Math.min(wall.v0, wall.v1), Math.max(wall.v0, wall.v1)];
18632
+ var alongRange2 = (wall) => wall.axis === "u" ? [Math.min(wall.u0, wall.u1), Math.max(wall.u0, wall.u1)] : [Math.min(wall.v0, wall.v1), Math.max(wall.v0, wall.v1)];
17951
18633
  function findOpenings(context) {
17952
18634
  const openings = [];
17953
18635
  for (const window of context.source.windows ?? []) {
17954
18636
  const from = window.at - window.width / 2;
17955
18637
  const to = window.at + window.width / 2;
17956
18638
  const wall = context.walls.find((w) => {
17957
- if (w.floor !== window.floor || w.axis !== window.axis || w.type !== "wall" || Math.abs(crossOf(w) - window.pos) > TOLERANCE) return false;
17958
- const [low, high] = alongRange(w);
17959
- return from >= low - TOLERANCE && to <= high + TOLERANCE;
18639
+ if (w.floor !== window.floor || w.axis !== window.axis || w.type !== "wall" || Math.abs(crossOf(w) - window.pos) > TOLERANCE2) return false;
18640
+ const [low, high] = alongRange2(w);
18641
+ return from >= low - TOLERANCE2 && to <= high + TOLERANCE2;
17960
18642
  });
17961
18643
  if (!wall) continue;
17962
18644
  const base = context.level(window.floor);
@@ -17971,18 +18653,39 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
17971
18653
  const cross = door.axis === "u" ? door.v : door.u;
17972
18654
  const from = along - door.width / 2;
17973
18655
  const to = along + door.width / 2;
17974
- const beside = context.walls.filter((w) => w.floor === door.floor && w.axis === door.axis && w.type === "wall" && Math.abs(crossOf(w) - cross) <= TOLERANCE && (Math.abs(alongRange(w)[1] - from) <= 0.05 || Math.abs(alongRange(w)[0] - to) <= 0.05));
18656
+ const beside = context.walls.filter((w) => w.floor === door.floor && w.axis === door.axis && w.type === "wall" && Math.abs(crossOf(w) - cross) <= TOLERANCE2 && (Math.abs(alongRange2(w)[1] - from) <= 0.05 || Math.abs(alongRange2(w)[0] - to) <= 0.05));
17975
18657
  if (!beside.length) continue;
17976
18658
  const half = (beside[0].thickness ?? 0.15) / 2;
17977
18659
  const lo = from - LINTEL_BEARING;
17978
18660
  const hi = to + LINTEL_BEARING;
17979
18661
  const box22 = door.axis === "u" ? [lo, cross - half, hi, cross + half] : [cross - half, lo, cross + half, hi];
17980
18662
  const y0 = context.level(door.floor) + DOOR_HEIGHT;
17981
- const lintel = builder.add({ id: `lintel_${door.id}`, role: "beam", material: system.timber ? construction.frame.timber : "concrete", aabb: box22, y0, y1: y0 + LINTEL_DEPTH }, { floor: door.floor, ...system.timber ? {} : { assembly: "reinforced_concrete" } });
18663
+ const depth = Math.max(LINTEL_DEPTH, (door.width + LINTEL_BEARING) / 10);
18664
+ const lintel = builder.add({ id: `lintel_${door.id}`, role: "beam", material: system.timber ? construction.frame.timber : "concrete", aabb: box22, y0, y1: y0 + depth }, { floor: door.floor, ...system.timber ? {} : { assembly: "reinforced_concrete" } });
17982
18665
  for (const wall of beside) builder.rest(lintel, context.wallParts.get(wall.id), system.joints.beam);
18666
+ const jambs = (context.pillars.get(door.floor) ?? []).filter((pillar) => pillar.material === "concrete" && overlapsInPlan(pillar.aabb, box22) && pillar.y1 >= y0);
18667
+ if (construction.system === "masonry" && hi - lo > WIDE_DOOR) jambs.push(...addJambPillars(context, { door, beside, from, to, cross }, jambs));
18668
+ for (const pillar of jambs) builder.rest(lintel, pillar, "rebar");
17983
18669
  for (const plate of context.plates.get(door.floor) ?? []) if (overlapsInPlan(plate.aabb, box22)) builder.rest(plate, lintel, system.joints.beam);
17984
18670
  }
17985
18671
  }
18672
+ var WIDE_DOOR = 3;
18673
+ var HALF_PILLAR = WALL_PILLAR_SIDE / 2;
18674
+ function addJambPillars(context, gap, standing) {
18675
+ const { builder, construction } = context;
18676
+ const top = Math.max(...gap.beside.map((w) => context.wallParts.get(w.id).y1));
18677
+ const base = context.baseOf(gap.door.floor);
18678
+ const out = [];
18679
+ for (const [k, along] of [[0, gap.from - HALF_PILLAR], [1, gap.to + HALF_PILLAR]]) {
18680
+ const [u, v] = gap.door.axis === "u" ? [along, gap.cross] : [gap.cross, along];
18681
+ const box22 = [u - HALF_PILLAR, v - HALF_PILLAR, u + HALF_PILLAR, v + HALF_PILLAR];
18682
+ if (standing.some((p) => overlapsInPlan(p.aabb, box22))) continue;
18683
+ const pillar = builder.add({ id: `jamb_column_${gap.door.id}_${k}`, role: "column", material: construction.frame.pillars, aabb: box22, y0: context.level(gap.door.floor), y1: top }, { floor: gap.door.floor });
18684
+ builder.rest(pillar, base === GROUND || !base ? GROUND : base, "rebar");
18685
+ out.push(pillar);
18686
+ }
18687
+ return out;
18688
+ }
17986
18689
  var overlapsInPlan = (a, b) => a[0] < b[2] && a[2] > b[0] && a[1] < b[3] && a[3] > b[1];
17987
18690
  var RISER = 0.18;
17988
18691
  var TREAD = 0.28;
@@ -18154,10 +18857,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
18154
18857
  const framed = system.studs !== void 0 && !isRailing;
18155
18858
  const part = builder.add(
18156
18859
  { id: wall.id, role: wall.role, material: wall.material, aabb: wall.box, y0, y1: Math.max(y1, y0 + 0.2) },
18157
- { floor, fill: context.isTimber && wall.role === "wall" ? system.wallFill : 1, ...framed ? { studs: system.studs } : {}, ...isRailing ? {} : { assembly: assemblyOf(context) } }
18860
+ { floor, fill: context.isTimber && wall.role === "wall" ? system.wallFill : 1, ...framed ? { studs: system.studs } : {}, ...isRailing ? {} : { assembly: assemblyOf(context) }, ...wall.exterior && !isRailing ? { exterior: true } : {} }
18158
18861
  );
18159
18862
  context.wallParts.set(wall.id, part);
18160
- const base = isRailing ? railingBase(context, wall, floor) : context.baseOf(floor);
18863
+ const base = isRailing ? railingBase(context, wall, floor) : context.groundBeams.get(wall.id) ?? context.baseOf(floor);
18161
18864
  const joint = isRailing ? "nail" : system.joints.wall;
18162
18865
  if (base === GROUND || floor === 0 && !base) builder.rest(part, GROUND, joint);
18163
18866
  else if (base) builder.rest(part, base, joint);
@@ -18173,6 +18876,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
18173
18876
  addUpperFloor(context, floor);
18174
18877
  }
18175
18878
  }
18879
+ var wallPillarSpacingOf = (options) => {
18880
+ const pillars = options.wallPillars ?? true;
18881
+ if (pillars === false) return 0;
18882
+ return pillars === true ? WALL_PILLAR_SPACING : pillars.spacing ?? WALL_PILLAR_SPACING;
18883
+ };
18176
18884
  function buildStructure(source, options = {}) {
18177
18885
  const refined = options.wallDetail !== "coarse";
18178
18886
  const built = options.construction ? { construction: options.construction, extras: options.extras ?? {} } : resolveConstruction(source);
@@ -18180,8 +18888,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
18180
18888
  const extras = options.extras ?? built.extras ?? {};
18181
18889
  if (construction.system === "canvas") return buildCanvasStructure(construction, extras);
18182
18890
  const tank = options.tank !== void 0 ? options.tank : source.waterTank ?? null;
18183
- const context = createContext(source, construction, extras, tank, { porchRoofOnPostsOnly: options.porchRoofOnPostsOnly ?? refined, rafterRoof: options.rafterRoof ?? refined });
18891
+ const context = createContext(source, construction, extras, tank, { porchRoofOnPostsOnly: options.porchRoofOnPostsOnly ?? refined, rafterRoof: options.rafterRoof ?? refined, wallPillarSpacing: wallPillarSpacingOf(options), visorSections: options.visorSections === true });
18184
18892
  addFoundation(context);
18893
+ if (options.groundBeams !== false) addGroundBeams(context);
18185
18894
  buildFloors(context);
18186
18895
  addBalconies(context);
18187
18896
  addPorchRoofs(context);
@@ -18930,6 +19639,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
18930
19639
  }
18931
19640
  var LINTEL_DEPTH2 = 0.2;
18932
19641
  var LINTEL_BEARING2 = 0.2;
19642
+ var SILL_BEAM_DEPTH = 0.1;
19643
+ var LINTEL_SPAN_RATIO = 10;
18933
19644
  var MIN_PIECE = 0.05;
18934
19645
  var frameOf = (wall) => {
18935
19646
  const alongU = wall.aabb[2] - wall.aabb[0] >= wall.aabb[3] - wall.aabb[1];
@@ -18943,6 +19654,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
18943
19654
  const drafts = [];
18944
19655
  const piers = [];
18945
19656
  const lintels = [];
19657
+ const sillBeams = [];
18946
19658
  const spandrels = [];
18947
19659
  const height = wall.y1 - wall.y0;
18948
19660
  let cursor = frame2.from;
@@ -18953,23 +19665,26 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
18953
19665
  };
18954
19666
  own.forEach((opening2, k) => {
18955
19667
  addPier(Math.max(cursor, opening2.from));
18956
- const lintelTop = Math.min(wall.y1, opening2.y1 + Math.max(LINTEL_DEPTH2, style.lintelDepth?.(opening2.to - opening2.from) ?? 0));
19668
+ const span = opening2.to - opening2.from;
19669
+ const lintelTop = Math.min(wall.y1, opening2.y1 + Math.max(LINTEL_DEPTH2, style.lintelDepth?.(span) ?? 0, (span + LINTEL_BEARING2) / LINTEL_SPAN_RATIO));
18957
19670
  const lintelFrom = Math.max(frame2.from, opening2.from - LINTEL_BEARING2);
18958
19671
  const lintelTo = Math.min(frame2.to, opening2.to + LINTEL_BEARING2);
18959
19672
  const lintelHeight = lintelTop - opening2.y1;
18960
- if (opening2.y0 - wall.y0 > MIN_PIECE) drafts.push({ id: `${wall.id}_sill_${k}`, role: "partition", material: wall.material, from: opening2.from, to: opening2.to, y0: wall.y0, y1: opening2.y0, volume: (opening2.to - opening2.from) * (opening2.y0 - wall.y0) });
19673
+ const sillTop = style.sillBeam && opening2.y0 - wall.y0 > SILL_BEAM_DEPTH + MIN_PIECE ? opening2.y0 - SILL_BEAM_DEPTH : opening2.y0;
19674
+ if (sillTop - wall.y0 > MIN_PIECE) drafts.push({ id: `${wall.id}_sill_${k}`, role: "partition", material: wall.material, from: opening2.from, to: opening2.to, y0: wall.y0, y1: sillTop, volume: (opening2.to - opening2.from) * (sillTop - wall.y0) });
19675
+ if (sillTop < opening2.y0) sillBeams.push({ id: `${wall.id}_sillbeam_${k}`, role: "beam", material: style.lintelMaterial, from: lintelFrom, to: lintelTo, y0: sillTop, y1: opening2.y0, volume: (lintelTo - lintelFrom) * SILL_BEAM_DEPTH });
18961
19676
  lintels.push({ id: `${wall.id}_lintel_${k}`, role: "beam", material: style.lintelMaterial, from: lintelFrom, to: lintelTo, y0: opening2.y1, y1: lintelTop, volume: (lintelTo - lintelFrom) * lintelHeight });
18962
19677
  spandrels.push(wall.y1 - lintelTop > MIN_PIECE ? { id: `${wall.id}_spandrel_${k}`, role: roleOf2, material: wall.material, from: opening2.from, to: opening2.to, y0: lintelTop, y1: wall.y1, volume: (opening2.to - opening2.from) * (wall.y1 - lintelTop) } : void 0);
18963
19678
  cursor = opening2.to;
18964
19679
  });
18965
19680
  addPier(frame2.to);
18966
19681
  for (const pier of piers) {
18967
- for (const lintel of lintels) {
19682
+ for (const lintel of [...lintels, ...sillBeams]) {
18968
19683
  const overlap = Math.max(0, Math.min(pier.to, lintel.to) - Math.max(pier.from, lintel.from));
18969
19684
  pier.volume -= overlap * (lintel.y1 - lintel.y0);
18970
19685
  }
18971
19686
  }
18972
- const all = [...piers, ...drafts, ...lintels, ...spandrels.filter((d) => d !== void 0)];
19687
+ const all = [...piers, ...drafts, ...sillBeams, ...lintels, ...spandrels.filter((d) => d !== void 0)];
18973
19688
  const total = all.reduce((sum, d) => sum + d.volume, 0);
18974
19689
  const pieces = all.map((d) => ({
18975
19690
  id: d.id,
@@ -18984,6 +19699,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
18984
19699
  ...d.role === "beam" ? style.lintelAssembly ? { assembly: style.lintelAssembly } : {} : wall.assembly ? { assembly: wall.assembly } : {}
18985
19700
  }));
18986
19701
  const internal = [];
19702
+ for (const beam of sillBeams) {
19703
+ const sill = drafts.find((d) => d.id === beam.id.replace("_sillbeam_", "_sill_"));
19704
+ if (sill) internal.push({ part: beam.id, on: sill.id, joint: style.lintelJoint });
19705
+ for (const pier of piers) if (Math.min(pier.to, beam.to) - Math.max(pier.from, beam.from) > 0) internal.push({ part: beam.id, on: pier.id, joint: style.lintelJoint });
19706
+ }
18987
19707
  lintels.forEach((lintel, k) => {
18988
19708
  for (const pier of piers) if (Math.min(pier.to, lintel.to) - Math.max(pier.from, lintel.from) > 0) internal.push({ part: lintel.id, on: pier.id, joint: style.lintelJoint });
18989
19709
  const spandrel = spandrels[k];
@@ -19013,7 +19733,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
19013
19733
  ...system.timber ? {} : { lintelAssembly: "reinforced_concrete" },
19014
19734
  lintelJoint: system.joints.beam,
19015
19735
  wallJoint: system.joints.wall,
19016
- ...lintelDepth && !system.timber ? { lintelDepth } : {}
19736
+ ...lintelDepth && !system.timber ? { lintelDepth } : {},
19737
+ ...construction.system === "masonry" ? { sillBeam: true } : {}
19017
19738
  });
19018
19739
  if (split.pieces.length > 1) splits.set(part.id, split);
19019
19740
  }
@@ -19021,7 +19742,18 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
19021
19742
  }
19022
19743
  function applyPanels(input, structure, detail, lintelDepth) {
19023
19744
  if (detail === "coarse") return input;
19024
- return applySplits(input, panelSplits(input, structure, lintelDepth));
19745
+ return onJambPillars(applySplits(input, panelSplits(input, structure, lintelDepth)));
19746
+ }
19747
+ var OPENING_BEAM = /_(lintel|sillbeam)_\d+$/;
19748
+ var overlaps2 = (a, b) => a[0] < b[2] && a[2] > b[0] && a[1] < b[3] && a[3] > b[1];
19749
+ function onJambPillars(split) {
19750
+ const pillars = split.parts.filter((part) => part.role === "column" && part.material === "concrete");
19751
+ const extra = [];
19752
+ for (const beam of split.parts) {
19753
+ if (beam.role !== "beam" || !OPENING_BEAM.test(beam.id)) continue;
19754
+ for (const pillar of pillars) if (pillar.floor === beam.floor && overlaps2(pillar.aabb, beam.aabb) && pillar.y0 <= beam.y0 && pillar.y1 >= beam.y1 - 0.01) extra.push({ part: beam.id, on: pillar.id, joint: "rebar" });
19755
+ }
19756
+ return extra.length ? { parts: split.parts, supports: [...split.supports, ...extra] } : split;
19025
19757
  }
19026
19758
  function applyUnits(input, structure) {
19027
19759
  const splits = /* @__PURE__ */ new Map();
@@ -19083,21 +19815,35 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
19083
19815
  };
19084
19816
  var MEMBER_CAGE = { spacing: 0.1, diameter: 8e-3, unit: 0.1 };
19085
19817
  var CAGED_ROLES = /* @__PURE__ */ new Set(["beam", "column"]);
19818
+ var CAGE_COVER = 0.03;
19819
+ var CAGE_BUCKLING_SHARE = 0.5;
19820
+ var DEFAULT_RESIDUAL = 0.15;
19821
+ function cageResidualCapacity(size, reinforcement, capacity) {
19822
+ const [a = 0, b = 0] = [...size].sort((x, y) => x - y);
19823
+ const perSide = (w) => Math.max(2, Math.floor(Math.max(0, w - 2 * CAGE_COVER) / reinforcement.spacing) + 1);
19824
+ const bars = 2 * perSide(a) + 2 * perSide(b) - 4;
19825
+ const steel = MATERIALS2.find((m) => m.id === reinforcement.material)?.compressive ?? 0;
19826
+ const carries2 = bars * Math.PI * (reinforcement.diameter / 2) ** 2 * steel * CAGE_BUCKLING_SHARE;
19827
+ if (!capacity || capacity <= 0 || carries2 <= 0) return DEFAULT_RESIDUAL;
19828
+ return Math.min(1, Math.max(DEFAULT_RESIDUAL, carries2 / capacity));
19829
+ }
19086
19830
  var caged = (spec, part, rebar) => {
19087
19831
  if (!rebar || part.material !== "concrete" || !CAGED_ROLES.has(part.role) || spec.reinforcement) return spec;
19088
19832
  const size = spec.shape.kind === "box" ? spec.shape.size : null;
19089
19833
  if (!size) return spec;
19090
19834
  const thin = Math.min(...size);
19835
+ const reinforcement = { material: rebar, spacing: MEMBER_CAGE.spacing, diameter: MEMBER_CAGE.diameter };
19091
19836
  return {
19092
19837
  ...spec,
19093
- reinforcement: { material: rebar, spacing: MEMBER_CAGE.spacing, diameter: MEMBER_CAGE.diameter },
19838
+ reinforcement: part.role === "column" ? { ...reinforcement, residualCapacity: cageResidualCapacity(size, reinforcement, spec.structure?.capacity) } : reinforcement,
19094
19839
  units: { size: [MEMBER_CAGE.unit, MEMBER_CAGE.unit, thin], bond: "stack", mortar: "concrete" }
19095
19840
  };
19096
19841
  };
19097
19842
  var partSpecOf = (part, capacity, detailed, rebar) => {
19098
19843
  if (detailed && part.assembly) return assemblySpec(part, capacity);
19099
19844
  const spec = boxSpec(part, capacity);
19100
- return detailed ? caged(spec, part, rebar) : spec;
19845
+ const out = detailed ? caged(spec, part, rebar) : spec;
19846
+ return part.beamKind === "ground_beam" ? { ...out, anchored: true, structure: { role: "foundation" } } : out;
19101
19847
  };
19102
19848
  function toDestructibleSpec(structure, options = {}) {
19103
19849
  const { safety = 0.65, redundancy = 0.97, safetyOthers = 0.45 } = options;
@@ -19113,7 +19859,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
19113
19859
  const walled = applyPanels(slabbed, structure, detail, slabOptions && structure.construction.frame.rebar ? (span) => beamDepthFor(span, slabOptions) : void 0);
19114
19860
  const panels = slabOptions && detail !== "coarse" ? deepenBondBeams(walled, structure, slabOptions) : walled;
19115
19861
  const sizing = { safety, redundancy, safetyOthers };
19116
- const sized = { ...panels, capacities: options.sized === false ? /* @__PURE__ */ new Map() : sizeCapacities(panels.parts, panels.supports, sizing) };
19862
+ const foundation = new Set(panels.parts.filter((part) => part.beamKind === "ground_beam").map((part) => part.id));
19863
+ const onFoundation = foundation.size ? panels.supports.filter((s) => !foundation.has(s.part)).map((s) => foundation.has(s.on) ? { ...s, on: GROUND } : s) : panels.supports;
19864
+ const sized = { ...panels, capacities: options.sized === false ? /* @__PURE__ */ new Map() : sizeCapacities(panels.parts.filter((part) => !foundation.has(part.id)), onFoundation, sizing) };
19117
19865
  const { parts, supports, capacities } = detail === "units" ? applyUnits(sized, structure) : sized;
19118
19866
  const specs = parts.map((part) => partSpecOf(part, capacities.get(part.id), detail !== "coarse", structure.construction.frame.rebar));
19119
19867
  const margin = options.terrainMargin ?? TERRAIN_MARGIN;
@@ -19178,6 +19926,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
19178
19926
  out.parts = out.parts.map((p, i) => i === ground ? raft2 : p);
19179
19927
  return out;
19180
19928
  }
19929
+ var LOW_WINDOW = SILL_BEAM_DEPTH + 0.05;
19181
19930
  var slabId = (g, f) => f === g.floors - 1 && g.roof ? "roof" : `slab_${f}`;
19182
19931
  var storeyBase = (g, f) => f * (g.height + g.slab);
19183
19932
  var slabHalf = (g) => [(g.spanX + g.columnSize + 0.2) / 2, (g.spanZ + g.columnSize + 0.2) / 2];
@@ -19336,7 +20085,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
19336
20085
  parts.push({ id, material: c.material, shape: box2(size), position, structure: { role: "partition" }, tags: ["infill"] });
19337
20086
  for (const o of on) rests(joints, id, o, "mortar");
19338
20087
  };
19339
- const bay = (side, k, along, centre, fixed, length, c1, c2) => {
20088
+ const bay = (side, k, along, centre2, fixed, length, c1, c2) => {
19340
20089
  const clear = length - g.columnSize;
19341
20090
  const win = clear * c.window;
19342
20091
  const pier = (clear - win) / 2;
@@ -19347,11 +20096,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
19347
20096
  wall(`infill_${f}_${side}_${k}_${name}`, size(u, h), [p[0], y, p[2]], on);
19348
20097
  };
19349
20098
  const id = (name) => `infill_${f}_${side}_${k}_${name}`;
19350
- put("sill", clear, sill, yb + sill / 2, centre, [c1, c2]);
19351
- put("pl", pier, head - sill, yb + sill + (head - sill) / 2, centre - clear / 2 + pier / 2, [c1, id("sill")]);
19352
- put("pr", pier, head - sill, yb + sill + (head - sill) / 2, centre + clear / 2 - pier / 2, [c2, id("sill")]);
19353
- put("head", clear, room - head, yb + head + (room - head) / 2, centre, [c1, c2]);
19354
- const gp = at2(centre);
20099
+ put("sill", clear, sill, yb + sill / 2, centre2, [c1, c2]);
20100
+ put("pl", pier, head - sill, yb + sill + (head - sill) / 2, centre2 - clear / 2 + pier / 2, [c1, id("sill")]);
20101
+ put("pr", pier, head - sill, yb + sill + (head - sill) / 2, centre2 + clear / 2 - pier / 2, [c2, id("sill")]);
20102
+ put("head", clear, room - head, yb + head + (room - head) / 2, centre2, [c1, c2]);
20103
+ const gp = at2(centre2);
19355
20104
  const gid = `glass_${f}_${side}_${k}`;
19356
20105
  parts.push({
19357
20106
  id: gid,
@@ -19995,7 +20744,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
19995
20744
  if (!pc || pc.unit) return sp;
19996
20745
  if (pc.role !== "wall" && pc.role !== "partition") {
19997
20746
  const def = getAssembly(pc.assembly || (pc.material === "concrete" && ["slab", "beam", "column", "stair"].includes(pc.role) ? "reinforced_concrete" : ""));
19998
- const out = def && def.reinforcement && !sp.reinforcement ? { ...sp, reinforcement: { ...def.reinforcement } } : sp;
20747
+ 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;
20748
+ const out = cage && !sp.reinforcement ? { ...sp, reinforcement: cage } : sp;
19999
20749
  if (out.reinforcement && !out.units && pc.material === "concrete" && (pc.role === "beam" || pc.role === "column") && sp.shape.kind === "box") {
20000
20750
  return { ...out, units: { size: [MEMBER_CAGE.unit, MEMBER_CAGE.unit, Math.min(...sp.shape.size)], bond: "stack", mortar: "concrete" } };
20001
20751
  }
@@ -20023,6 +20773,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
20023
20773
  const a = pos.array;
20024
20774
  const u = mesh2.userData;
20025
20775
  if (!u._p0) u._p0 = a.slice();
20776
+ if (!u.plainPositions) Object.defineProperty(u, "plainPositions", { value: u._p0, enumerable: false, configurable: true, writable: true });
20026
20777
  const list2 = u.collapsedRanges ?? (u.collapsedRanges = []);
20027
20778
  if (!list2.some((r) => r[0] === v0 && r[1] === v1)) list2.push([v0, v1]);
20028
20779
  const saved = a.slice(v0 * 3, v1 * 3);
@@ -20106,10 +20857,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
20106
20857
  const v = house.worldToLocal(tmp.p.set(p[0], p[1], p[2]));
20107
20858
  return [v.x, v.y, v.z];
20108
20859
  };
20109
- function cells(part, units) {
20860
+ function cells(part, units, mask) {
20110
20861
  const [u0, v0, u1, v1] = part.aabb;
20111
- const alongU = u1 - u0 >= v1 - v0;
20862
+ const alongU = mask.alongU;
20112
20863
  const len = alongU ? u1 - u0 : v1 - v0;
20864
+ const offset = (alongU ? u0 : v0) - mask.start;
20113
20865
  const [ul, uh] = units.size;
20114
20866
  const rows = Math.max(1, Math.round((part.y1 - part.y0) / uh));
20115
20867
  const rowH = (part.y1 - part.y0) / rows;
@@ -20120,7 +20872,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
20120
20872
  const shift = units.bond === "running" && r & 1 ? w / 2 : 0;
20121
20873
  for (let i = 0; i < per + (shift ? 1 : 0); i++) {
20122
20874
  const a = Math.max(0, i * w - shift), b = Math.min(len, i * w - shift + w);
20123
- if (b - a > 1e-3) out.push({ a, b, y0: part.y0 + r * rowH, y1: part.y0 + (r + 1) * rowH, r, i });
20875
+ 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 });
20124
20876
  }
20125
20877
  }
20126
20878
  return out;
@@ -20134,8 +20886,21 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
20134
20886
  for (const q of structure.parts) {
20135
20887
  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;
20136
20888
  }
20889
+ return part.role === "column" ? wallAround(part) : null;
20890
+ }
20891
+ function wallAround(part) {
20892
+ const cu = (part.aabb[0] + part.aabb[2]) / 2, cv = (part.aabb[1] + part.aabb[3]) / 2;
20893
+ for (const q of structure.parts) {
20894
+ if (q.id === part.id || q.role !== "wall" && q.role !== "partition" || !structure.meshesOf(q.id).some((it) => it.mesh)) continue;
20895
+ const inside2 = cu >= q.aabb[0] - 1e-3 && cu <= q.aabb[2] + 1e-3 && cv >= q.aabb[1] - 1e-3 && cv <= q.aabb[3] + 1e-3;
20896
+ if (inside2 && Math.min(q.y1, part.y1) - Math.max(q.y0, part.y0) > 0.5) return q.id;
20897
+ }
20137
20898
  return null;
20138
20899
  }
20900
+ const pillarSpans = (view) => structure.parts.filter((q) => q.role === "column" && wallAround(q) === view.host).map((q) => {
20901
+ 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;
20902
+ return { a, b, y0: q.y0, y1: q.y1 };
20903
+ });
20139
20904
  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);
20140
20905
  function shell(it, mask) {
20141
20906
  const g0 = it.mesh.geometry, g = new THREE.BufferGeometry();
@@ -20224,6 +20989,17 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
20224
20989
  texture.needsUpdate = true;
20225
20990
  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) };
20226
20991
  }
20992
+ function besidePillars(c, pillars) {
20993
+ let { a, b } = c;
20994
+ for (const k of pillars) {
20995
+ if (c.y1 <= k.y0 || c.y0 >= k.y1 || b <= k.a || a >= k.b) continue;
20996
+ const centre = (a + b) / 2;
20997
+ if (centre > k.a && centre < k.b) return [];
20998
+ if (centre <= k.a) b = Math.min(b, k.a);
20999
+ else a = Math.max(a, k.b);
21000
+ }
21001
+ return b - a > 1e-3 ? [{ ...c, a, b }] : [];
21002
+ }
20227
21003
  const boxDistance = (s, y, a, b, c, d) => Math.hypot(Math.max(a - s, 0, s - b), Math.max(c - y, 0, y - d));
20228
21004
  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);
20229
21005
  function covered(c, boxes) {
@@ -20246,9 +21022,15 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
20246
21022
  const discs = [];
20247
21023
  const flakes = [];
20248
21024
  const shown = [];
21025
+ const pillars = pillarSpans(view);
20249
21026
  for (const id of view.parts) {
20250
21027
  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);
20251
- const all = cells(part, units2);
21028
+ const own = cells(part, units2, mask);
21029
+ const all = id === view.host ? own.flatMap((c) => besidePillars(c, pillars)) : own;
21030
+ if (view.fallen.has(id)) {
21031
+ holes.push(...all);
21032
+ continue;
21033
+ }
20252
21034
  const thick = mask.alongU ? part.aabb[3] - part.aabb[1] : part.aabb[2] - part.aabb[0];
20253
21035
  const mid = mask.alongU ? (part.aabb[1] + part.aabb[3]) / 2 : (part.aabb[0] + part.aabb[2]) / 2;
20254
21036
  if (opening) discs.push({ s: along(opening.center), y: opening.center[1], r: opening.radius });
@@ -20344,52 +21126,55 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
20344
21126
  }
20345
21127
  return { center: local(chip.center), along: a, up: u };
20346
21128
  }
21129
+ function newView(hostId) {
21130
+ const items = structure.meshesOf(hostId).filter((it) => it.mesh);
21131
+ if (!items.length) return null;
21132
+ const parts = [hostId, ...riders(hostId)].filter((id) => structure.byId.get(id) && specOf(id)?.units);
21133
+ const mask = maskOf(hostId, parts), core = maskOf(hostId, parts);
21134
+ return {
21135
+ host: hostId,
21136
+ parts,
21137
+ items,
21138
+ mask,
21139
+ core,
21140
+ unitMaterial: unitsMaterial(core),
21141
+ shells: [],
21142
+ saved: null,
21143
+ units: null,
21144
+ gone: 0,
21145
+ pits: 0,
21146
+ bounds: drawnBounds(items),
21147
+ chips: new Map(parts.map((id) => [id, []])),
21148
+ spalls: new Map(parts.map((id) => [id, []])),
21149
+ openings: /* @__PURE__ */ new Map(),
21150
+ flakes: 0,
21151
+ fallen: /* @__PURE__ */ new Set()
21152
+ };
21153
+ }
21154
+ function cut(view) {
21155
+ if (view.saved) return;
21156
+ view.shells = view.items.map((it) => shell(it, view.mask));
21157
+ for (const m of view.shells) m.userData.behind = () => view.units;
21158
+ view.saved = view.items.map((it) => ({ it, v0: it.v0, data: collapseRange(it.mesh, it.v0, it.v1) }));
21159
+ }
20347
21160
  function draw2(partId, change) {
20348
21161
  if (lod !== 0) return false;
20349
21162
  const hostId = hostOf(partId);
20350
21163
  if (!hostId || !specOf(partId)?.units) return false;
20351
- let view = views.get(hostId);
20352
- if (!view) {
20353
- const items = structure.meshesOf(hostId).filter((it) => it.mesh);
20354
- if (!items.length) return false;
20355
- const parts = [hostId, ...riders(hostId)].filter((id) => structure.byId.get(id) && specOf(id)?.units);
20356
- if (!parts.includes(partId)) return false;
20357
- const mask = maskOf(hostId, parts), core = maskOf(hostId, parts);
20358
- view = {
20359
- host: hostId,
20360
- parts,
20361
- items,
20362
- mask,
20363
- core,
20364
- unitMaterial: unitsMaterial(core),
20365
- shells: [],
20366
- saved: null,
20367
- units: null,
20368
- gone: 0,
20369
- pits: 0,
20370
- bounds: drawnBounds(items),
20371
- chips: new Map(parts.map((id) => [id, []])),
20372
- spalls: new Map(parts.map((id) => [id, []])),
20373
- openings: /* @__PURE__ */ new Map(),
20374
- flakes: 0
20375
- };
20376
- }
20377
- if (!view.chips.has(partId)) return false;
21164
+ const view = views.get(hostId) ?? newView(hostId);
21165
+ if (!view || !view.chips.has(partId)) return false;
20378
21166
  if (change.opening) {
20379
21167
  if (view.openings.has(partId)) return false;
20380
21168
  view.openings.set(partId, { center: local(change.opening.center), radius: change.opening.radius });
20381
21169
  }
20382
21170
  if (change.chip) view.chips.get(partId).push(chipBox(change.chip, view.mask.alongU));
21171
+ for (const chip of change.chips ?? []) view.chips.get(partId).push(chipBox(chip, view.mask.alongU));
20383
21172
  if (change.spall) {
20384
21173
  const list2 = view.spalls.get(partId);
20385
21174
  list2.push({ center: local(change.spall.center), radius: change.spall.radius });
20386
21175
  if (list2.length > MAX_FLAKES) list2.shift();
20387
21176
  }
20388
- if (!view.saved) {
20389
- view.shells = view.items.map((it) => shell(it, view.mask));
20390
- for (const m of view.shells) m.userData.behind = () => view.units;
20391
- view.saved = view.items.map((it) => ({ it, v0: it.v0, data: collapseRange(it.mesh, it.v0, it.v1) }));
20392
- }
21177
+ cut(view);
20393
21178
  views.set(hostId, view);
20394
21179
  if (opts.deferPaint && change.spall) dirty.add(view);
20395
21180
  else {
@@ -20500,14 +21285,32 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
20500
21285
  */
20501
21286
  chip(event) {
20502
21287
  if (event.torn) return false;
20503
- return draw2(event.part, { chip: { center: event.center, size: event.size ?? [0, 0, 0], rotation: event.rotation ?? [0, 0, 0, 1] } });
21288
+ const rotation = event.rotation ?? [0, 0, 0, 1];
21289
+ if (event.units?.length) return draw2(event.part, { chips: event.units.map((unit) => ({ center: unit.center, size: unit.size, rotation })) });
21290
+ return draw2(event.part, { chip: { center: event.center, size: event.size ?? [0, 0, 0], rotation } });
20504
21291
  },
20505
21292
  /** The part broke or fell: its damage goes (the plain range stays collapsed, the renderer hides the part). */
20506
21293
  drop(partId) {
20507
- const v = views.get(partId);
20508
- if (!v) return;
21294
+ let host = views.get(partId) ?? [...views.values()].find((view) => view.parts.includes(partId));
21295
+ if (!host && lod === 0 && riders(partId).length > 0) {
21296
+ host = newView(partId);
21297
+ if (host) {
21298
+ cut(host);
21299
+ views.set(partId, host);
21300
+ }
21301
+ }
21302
+ if (!host) return;
21303
+ host.fallen.add(partId);
21304
+ if (host.parts.some((id) => !host.fallen.has(id))) {
21305
+ dirty.delete(host);
21306
+ paint(host);
21307
+ invalidate();
21308
+ invalidateShadow();
21309
+ return;
21310
+ }
21311
+ const v = host;
20509
21312
  dispose(v);
20510
- views.delete(partId);
21313
+ views.delete(v.host);
20511
21314
  if (v.saved) dropped.push(v);
20512
21315
  },
20513
21316
  /** LOD 0 draws the cut wall; LOD 1 and 2 give the wall its box back. */
@@ -20948,8 +21751,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
20948
21751
  // src/build/build.ts
20949
21752
  function installBuildBuild(K, shared) {
20950
21753
  const { THREE, cfg, rectsOf: rectsOf2, invalidateShadow, colliderChanged, VERSION: VERSION2, CARS, EPS: EPS3, LIGHTS, MIRRORS, Y0, animateClotheslines, animateDoors, animateInteractive, applyLeaf, applyOutbuildings, applyPart, applyPiece, assembleInteractive, baseboardAndSidewalk, buildStairs, building, carModel, chooseMirror, closeScene, collideAt, createWater, cursor, doorText, doors, electricalStructure, elevator, floorsAndSlabs, gatesInteractive, houseItems, ivAssemble, ivBuild, ivDispose, ivParts, ivPublish, ivUpdate, lightOn, linkDoorLeaves, mailboxInteractive, newInteractiveState, newMirrorState, outbuildings, paths, perimeter, placeSwitches, roofs, roomAt, solar, targetOf, terrain, toggleDoor, toggleLight, walls, waterPipes, windows, props, TYPES, IVK, VEG, vegMaterials, markGroups, assembleStructure, createHouseDestruction, newState, enter, tagDamage } = K;
20951
- function build(r0, opts) {
20952
- opts = opts || {};
21754
+ function build(r0, options) {
21755
+ const opts = options || {};
20953
21756
  const r = applyOutbuildings(r0);
20954
21757
  const t0 = performance.now();
20955
21758
  const h = { ctx: { mv: props, types: TYPES }, inter: newInteractiveState(), state: opts.state || newState(), mirrors: newMirrorState(), quality: opts.quality === "game" || opts.quality === "high" ? opts.quality : cfg.quality === "game" ? "game" : "high", lodCurrent: 0, lodMin: 0 };
@@ -20970,7 +21773,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
20970
21773
  shared.ctx.pieces = /* @__PURE__ */ new Map();
20971
21774
  shared.ctx.pieceObjects = /* @__PURE__ */ new Map();
20972
21775
  shared.ctx.structureIndex = {};
20973
- for (const e of r0.structure && r0.structure.parts || []) shared.ctx.structureIndex[e.id] = e;
21776
+ for (const e of r0.structure?.parts || []) shared.ctx.structureIndex[e.id] = e;
20974
21777
  {
20975
21778
  const view = shared.ctx.panelView = panelViewOf(r0.structure, !!cfg.destruction);
20976
21779
  shared.ctx.panelsOf = /* @__PURE__ */ new Map();
@@ -20982,17 +21785,18 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
20982
21785
  else shared.ctx.panelsOf.set(e.slab, [e]);
20983
21786
  }
20984
21787
  }
20985
- shared.ctx.panes = r0.roof && r0.roof.panes || [];
21788
+ shared.ctx.panes = r0.roof?.panes || [];
20986
21789
  shared.ctx.noPiece = false;
20987
21790
  cursor.separate = opts.pieces === "meshes";
20988
21791
  cursor.pc = null;
21792
+ shared.ctx.showPillars = opts.pillars !== "hidden";
20989
21793
  shared.ctx.apartment = r.template === "apartment_modern";
20990
21794
  shared.ctx.pa = Math.max(3, Math.min(20, pr.floors != null ? pr.floors : 5));
20991
21795
  shared.ctx.pf = Math.max(1, Math.min(shared.ctx.pa, pr.floor != null ? pr.floor : 3));
20992
21796
  shared.ctx.pu = Math.max(0, pr.unit != null ? pr.unit : 0);
20993
21797
  shared.ctx.showBuilding = shared.ctx.apartment && (pr.showIt == null ? true : !!pr.showIt);
20994
21798
  shared.ctx.limits = { x0: -2, z0: -2, x1: r.w + 2, z1: r.d + 2 };
20995
- shared.ctx.veg = VEG ? { G: VEG.G, S: VEG.S, mats: vegMaterials, groups: /* @__PURE__ */ new Map(), ok: /* @__PURE__ */ new Map(), opts: shared.quality === "game" ? { quality: "game" } : { lod: 0, triangleBudget: 6e4 } } : null;
21799
+ shared.ctx.veg = VEG ? { G: VEG.G, S: VEG.S, mats: vegMaterials, groups: /* @__PURE__ */ new Map(), ok: /* @__PURE__ */ new Map(), opts: shared.quality === "game" ? { quality: "game" } : { quality: "high", lod: 0, triangleBudget: 6e4 } } : null;
20996
21800
  shared.ctx.vegInfo = null;
20997
21801
  terrain();
20998
21802
  if (r.lot) {
@@ -21042,7 +21846,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
21042
21846
  const g = gs[gd.name];
21043
21847
  if (!g) continue;
21044
21848
  delete gs[gd.name];
21045
- g.position.set(...gd.pos);
21849
+ g.position.fromArray(gd.pos);
21046
21850
  parent.add(g);
21047
21851
  fo.gs.push(g);
21048
21852
  }
@@ -21057,7 +21861,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
21057
21861
  root.add(house);
21058
21862
  shared.mirrors.house = house;
21059
21863
  if (shared.ctx.apartment) {
21060
- const u = r.building.units[Math.min(shared.ctx.pu, r.building.units.length - 1)];
21864
+ const units = r.building.units, u = units[Math.min(shared.ctx.pu, units.length - 1)];
21061
21865
  house.position.set(u.u, shared.ctx.showBuilding ? 0 : -r.floors[0].y0, u.v);
21062
21866
  house.rotation.y = u.ry;
21063
21867
  shared.ctx.unitRy = u.ry;
@@ -21173,7 +21977,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
21173
21977
  const wallAt = (f, axis, L) => r.walls.find((w) => w.floor === f && w.axis === axis && Math.abs((axis === "u" ? w.v0 : w.u0) - L) < EPS3);
21174
21978
  for (const j of r.windows) {
21175
21979
  const alongU = j.axis === "u", y0 = Y0(j.floor), a = j.at - j.width / 2, b = j.at + j.width / 2, w = wallAt(j.floor, j.axis, j.pos), th = w ? w.thickness || 0.15 : 0.15, L = j.pos, id = "window:" + j.id;
21176
- parts[id] = { id, type: "window", group: null, virtual: true, floor: j.floor, aabb: alongU ? [a, y0 + j.y0, L - th / 2, b, y0 + j.y1, L + th / 2] : [L - th / 2, y0 + j.y0, a, L + th / 2, y0 + j.y1, b], damageable: tagDamage("glass"), opens: () => {
21980
+ const jy0 = j.y0, jy1 = j.y1;
21981
+ parts[id] = { id, type: "window", group: null, virtual: true, floor: j.floor, aabb: alongU ? [a, y0 + jy0, L - th / 2, b, y0 + jy1, L + th / 2] : [L - th / 2, y0 + jy0, a, L + th / 2, y0 + jy1, b], damageable: tagDamage("glass"), opens: () => {
21177
21982
  }, t: 0 };
21178
21983
  }
21179
21984
  for (const E of mirrorsCopy) {
@@ -21216,7 +22021,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
21216
22021
  const countDraw = () => {
21217
22022
  let n = 0, t = 0;
21218
22023
  root.traverseVisible((o) => {
21219
- if (o.isMesh) {
22024
+ if (isMesh(o)) {
21220
22025
  n++;
21221
22026
  t += (o.geometry.index ? o.geometry.index.count : o.geometry.attributes.position.count) / 3;
21222
22027
  }
@@ -21230,8 +22035,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
21230
22035
  const budget = opts.drawCallBudget != null ? opts.drawCallBudget : cfg.drawCallBudget;
21231
22036
  const stats2 = { quality: h.quality, drawCalls: s0.n, drawCallsLOD1: s1.n, triangles: s0.t, trianglesLod1: s1.t, budget: budget || null, overBudget: !!budget && s0.n > budget, tBuild: performance.now() - t0 };
21232
22037
  shared.ctx.tBuild = stats2.tBuild;
21233
- const applyLod = (n) => {
21234
- n = Math.max(0, Math.min(2, n | 0));
22038
+ const applyLod = (level) => {
22039
+ const n = Math.max(0, Math.min(2, level | 0));
21235
22040
  if (n === h.lodCurrent) return;
21236
22041
  h.lodCurrent = n;
21237
22042
  for (const o of lodObjects) o.visible = n < 1;
@@ -21308,7 +22113,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
21308
22113
  lights: (() => {
21309
22114
  const by = {};
21310
22115
  for (const l of shared.ctx.lights) {
21311
- const k = l.roomId != null ? l.roomId : l.room;
22116
+ const k = String(l.roomId != null ? l.roomId : l.room);
21312
22117
  (by[k] || (by[k] = [])).push(l);
21313
22118
  }
21314
22119
  return { list: shared.ctx.lights, byRoom: by };
@@ -21348,9 +22153,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
21348
22153
  applyLod(Math.max(n, h.lodMin));
21349
22154
  }
21350
22155
  },
21351
- show(o) {
22156
+ show(options2) {
21352
22157
  enter(h);
21353
- o = o || {};
22158
+ const o = options2 || {};
21354
22159
  const rr = shared.ctx.r, nA = rr.floors.length, a = o.floor == null ? -1 : o.floor, sel = a >= nA ? -1 : a, cut = !!o.cut;
21355
22160
  for (const [k, g] of Object.entries(gs)) {
21356
22161
  let v = true;
@@ -21370,7 +22175,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
21370
22175
  enter(h);
21371
22176
  const a = targetOf(o, opt);
21372
22177
  if (!a) return false;
21373
- return actOn(a.target && a.target.pub ? a.target.pub : { id: a.target && a.target.id || a.text, type: a.target && a.target.kit ? a.target.type : "door", label: a.text, ref: a.target, _action: a.action }, a);
22178
+ return actOn(a.target && a.target.pub ? a.target.pub : { id: a.target && a.target.id || a.text, type: a.target && a.target.kit ? a.target.type : "door", label: a.text, ref: a.target, _action: a.action, floor: 0, pos: [], radius: 0 }, a);
21374
22179
  },
21375
22180
  message() {
21376
22181
  return shared.inter.msg && performance.now() < shared.inter.msg.until ? shared.inter.msg.text : null;
@@ -21427,13 +22232,13 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
21427
22232
  }
21428
22233
  return null;
21429
22234
  },
21430
- partAt(p, tol) {
21431
- tol = tol || 0.05;
22235
+ partAt(p, tolerance) {
22236
+ const tol = tolerance || 0.05;
21432
22237
  let m = null;
21433
22238
  for (const k in parts) {
21434
- const a = parts[k].aabb;
22239
+ const part = parts[k], a = part.aabb;
21435
22240
  if (a && p.x >= a[0] - tol && p.x <= a[3] + tol && p.y >= a[1] - tol && p.y <= a[4] + tol && p.z >= a[2] - tol && p.z <= a[5] + tol) {
21436
- m = parts[k];
22241
+ m = part;
21437
22242
  break;
21438
22243
  }
21439
22244
  }
@@ -21444,8 +22249,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
21444
22249
  destruction.deactivate();
21445
22250
  if (h.IV) ivDispose(h);
21446
22251
  root.traverse((o) => {
21447
- if (o.isMesh && o.geometry && !o.geometry.userData.shared) o.geometry.dispose();
21448
- if (o.type === "Reflector" && o.dispose) o.dispose();
22252
+ if (isMesh(o) && o.geometry && !o.geometry.userData.shared) o.geometry.dispose();
22253
+ if (o.type === "Reflector" && "dispose" in o && typeof o.dispose === "function") o.dispose();
21449
22254
  });
21450
22255
  root.removeFromParent();
21451
22256
  },
@@ -21453,7 +22258,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
21453
22258
  };
21454
22259
  return res;
21455
22260
  }
21456
- Object.assign(K, { build });
22261
+ const defined = { build };
22262
+ Object.assign(K, defined);
21457
22263
  }
21458
22264
 
21459
22265
  // src/build/api.ts
@@ -21475,11 +22281,13 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
21475
22281
  lighting,
21476
22282
  effectLights,
21477
22283
  setNight(n) {
21478
- shared.mats.glass.emissive.set(n ? 9067040 : 0);
21479
- shared.mats.glass.opacity = n ? 0.4 : 0.24;
22284
+ const glass = shared.mats.glass;
22285
+ glass.emissive.set(n ? 9067040 : 0);
22286
+ glass.opacity = n ? 0.4 : 0.24;
21480
22287
  },
21481
22288
  /** The chipped-plaster painting of every house of this renderer as a `DeferredQueue` for a frame budget (id `floorplan.breach-paint`); asking for it changes nothing. */
21482
22289
  deferredQueue: () => K.breachPaintQueue,
22290
+ /* the destruction side is typed with the breach */
21483
22291
  cars: { model: carModel, group: carGroup, info: (k, seed) => {
21484
22292
  const m = carModel(k, seed);
21485
22293
  return m && { parts: m.parts.length, tris: m.tris, L: m.L, W: m.W, H: m.H };
@@ -22116,7 +22924,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
22116
22924
  const stopListening = () => {
22117
22925
  for (const off of listening.splice(0)) off?.();
22118
22926
  };
22119
- const collapsed = (list2) => list2.filter((e) => e.type === "collapse").flatMap((e) => e.parts.map((p) => p.id || p));
22927
+ const collapsed = (list2) => list2.flatMap((e) => e.type === "collapse" ? e.parts.map((p) => p.id || p) : []);
22120
22928
  const whole = (room, oid, pid) => {
22121
22929
  const p = room.world.part(oid, pid);
22122
22930
  return !!p && p.status !== "broken" && !p.stripped;
@@ -22153,10 +22961,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
22153
22961
  const list2 = events(room);
22154
22962
  const columns = groundColumns(parts);
22155
22963
  const laj = slabs(parts);
22156
- const first = columns[0];
22157
- 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];
22158
- blunt(room, oid, first.id, centre(first.id), 3e6);
22159
- blunt(room, oid, mate.id, centre(mate.id), 3e6);
22964
+ const first = columns.find((p) => !/^wall_column_/.test(p.id)) ?? columns[0];
22965
+ 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];
22966
+ const along = columns.filter((p) => /^wall_column_/.test(p.id) && Math.abs(p.position[0] - first.position[0]) < 0.5);
22967
+ for (const p of [first, mate, ...along]) blunt(room, oid, p.id, centre(p.id), 3e6);
22160
22968
  step(90);
22161
22969
  const fell = collapsed(list2);
22162
22970
  expect(laj.some((id) => fell.includes(id)), fill(T2.two_slab_fell, { n: fell.length }));
@@ -22476,9 +23284,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
22476
23284
  return best;
22477
23285
  };
22478
23286
  const eyeOf = (p) => {
23287
+ const T2 = THREE;
22479
23288
  const camera = host?.camera;
22480
23289
  const at2 = toLocal2(p, camera.position.x, camera.position.y, camera.position.z);
22481
- const forward = new THREE.Vector3(0, 0, -1).applyQuaternion(camera.quaternion).transformDirection(new THREE.Matrix4().copy(p.house.group.matrixWorld).invert());
23290
+ const forward = new T2.Vector3(0, 0, -1).applyQuaternion(camera.quaternion).transformDirection(new T2.Matrix4().copy(p.house.group.matrixWorld).invert());
22482
23291
  return { x: at2.x, y: at2.y, z: at2.z, floor: floorOf(p, camera.position.y), yaw: Math.atan2(forward.x, forward.z), pitch: Math.asin(Math.max(-1, Math.min(1, forward.y))) };
22483
23292
  };
22484
23293
  const blocked = (x, z, radius) => {
@@ -22494,8 +23303,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
22494
23303
  const standAt = (p, u, v, floor, lookU, lookV) => {
22495
23304
  if (!host?.bench) return;
22496
23305
  const y = p.house.y0(floor) + EYE;
22497
- const eye = p.house.group.localToWorld(new THREE.Vector3(u, y, v));
22498
- const target = p.house.group.localToWorld(new THREE.Vector3(lookU, y - 0.1, lookV));
23306
+ const T2 = THREE;
23307
+ const eye = p.house.group.localToWorld(new T2.Vector3(u, y, v));
23308
+ const target = p.house.group.localToWorld(new T2.Vector3(lookU, y - 0.1, lookV));
22499
23309
  host.bench.look([eye.x, eye.y, eye.z], [target.x, target.y, target.z]);
22500
23310
  floorNow = floor;
22501
23311
  };
@@ -22545,16 +23355,17 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
22545
23355
  host?.log(fill(text.steps.door, { text: String(target.text) }));
22546
23356
  };
22547
23357
  const doorReach = (p, fo) => {
23358
+ const T2 = THREE;
22548
23359
  const g = fo.gs[0];
22549
23360
  const pivot = fo.handles[0].pivot;
22550
23361
  const at2 = (angle) => {
22551
23362
  const c = Math.cos(angle);
22552
23363
  const s = Math.sin(angle);
22553
- const v = new THREE.Vector3(pivot[0] * c + pivot[2] * s, pivot[1], -pivot[0] * s + pivot[2] * c).add(g.position);
23364
+ const v = new T2.Vector3(pivot[0] * c + pivot[2] * s, pivot[1], -pivot[0] * s + pivot[2] * c).add(g.position);
22554
23365
  return g.parent ? g.parent.localToWorld(v) : v;
22555
23366
  };
22556
23367
  const camera = host?.camera;
22557
- const eye = new THREE.Vector3(camera.position.x, 0, camera.position.z);
23368
+ const eye = new T2.Vector3(camera.position.x, 0, camera.position.z);
22558
23369
  const flat = (v) => Math.hypot(v.x - eye.x, v.z - eye.z);
22559
23370
  const closed = at2(0);
22560
23371
  const open = at2(fo.rot);
@@ -22562,9 +23373,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
22562
23373
  return {
22563
23374
  handle: () => {
22564
23375
  g.updateMatrixWorld(true);
22565
- return g.localToWorld(new THREE.Vector3(pivot[0], pivot[1], pivot[2])).toArray();
23376
+ return g.localToWorld(new T2.Vector3(pivot[0], pivot[1], pivot[2])).toArray();
22566
23377
  },
22567
- hinge: () => g.getWorldPosition(new THREE.Vector3()).toArray(),
23378
+ hinge: () => g.getWorldPosition(new T2.Vector3()).toArray(),
22568
23379
  seconds: fo.locked || fo.blockedFlag ? 0.6 : 0.55,
22569
23380
  pull: opening ? flat(open) <= flat(closed) : flat(closed) <= flat(open),
22570
23381
  locked: !!(fo.locked || fo.blockedFlag)
@@ -24329,6 +25140,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
24329
25140
  }
24330
25141
 
24331
25142
  // src/wall.ts
25143
+ var WARM_UP_SHOT = { type: "ballistic", point: [0, 1.2, 0.1], direction: [0, 0, -1], energy: 3500, caliber: 7.62 };
25144
+ var WARM_UP_FRAMES = 5;
25145
+ var isTexture = (value) => typeof value === "object" && value !== null && value.isTexture === true;
24332
25146
  var PLASTER = 15262420;
24333
25147
  var WALL_ID = "wall_0_0";
24334
25148
  function wallPlan(options) {
@@ -24359,9 +25173,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
24359
25173
  waterTank: null,
24360
25174
  outbuildings: []
24361
25175
  };
24362
- plan.construction = masonryConstruction({ ...plan, facade: { ...facade, roof: "colonial" } });
24363
- plan.structure = buildStructure(plan, { wallDetail: "coarse", construction: plan.construction, roof: false });
24364
- return plan;
25176
+ const construction = masonryConstruction({ ...plan, facade: { ...facade, roof: "colonial" } });
25177
+ const structure = buildStructure({ ...plan, construction }, { wallDetail: "coarse", construction, roof: false });
25178
+ return Object.assign(plan, { construction, structure });
24365
25179
  }
24366
25180
  function draw(THREE, plan, options) {
24367
25181
  return create3(THREE, { ...options, fleet: null }).build(plan, { quality: "game", terrain: false, noPerimeter: true, pieces: "meshes" });
@@ -24371,7 +25185,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
24371
25185
  houses.set(house.group, house);
24372
25186
  for (const [id, items] of Object.entries(house.destruction.ranges())) {
24373
25187
  for (const item of items) {
24374
- const object2 = item.isObject3D ? item : item.mesh;
25188
+ const object2 = "isObject3D" in item ? item : item.mesh;
24375
25189
  object2.userData.destructionPart = id;
24376
25190
  }
24377
25191
  }
@@ -24387,7 +25201,39 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
24387
25201
  plan,
24388
25202
  spec,
24389
25203
  build: (T2) => label(draw(T2, plan, houseOptions)),
24390
- follow: (root, world, objectId) => houses.get(root)?.destruction?.follow?.(world, objectId, spec) ?? null
25204
+ follow: (root, world, objectId) => houses.get(root)?.destruction?.follow?.(world, objectId, spec) ?? null,
25205
+ warmUpProbes: (T2) => {
25206
+ const house = draw(T2, plan, houseOptions);
25207
+ const root = label(house);
25208
+ const world = createDestruction({ seed: 1 });
25209
+ const objectId = world.add(spec, { id: "warm_up_wall" });
25210
+ const destruction = house.destruction;
25211
+ const follower = destruction?.follow?.(world, objectId, spec) ?? null;
25212
+ world.hitPart(objectId, WALL_ID, WARM_UP_SHOT);
25213
+ for (let frame2 = 0; frame2 < WARM_UP_FRAMES; frame2++) {
25214
+ world.update(1 / 60);
25215
+ follower?.update?.();
25216
+ }
25217
+ for (let guard = 0; guard < WARM_UP_FRAMES && (destruction?.breach?.flush?.(Infinity) ?? 0) > 0; guard++) ;
25218
+ root.traverse((node) => {
25219
+ node.visible = true;
25220
+ node.frustumCulled = false;
25221
+ });
25222
+ return {
25223
+ group: root,
25224
+ // the materials stay: the warmup keeps them as anchors, and disposing the last material of a program frees the program it was drawn for (so the
25225
+ // follower is not disposed either: it would put the wall back and dispose the breach's materials; its world goes with the probe)
25226
+ dispose: () => {
25227
+ root.traverse((node) => {
25228
+ node.geometry?.dispose();
25229
+ for (const material of Array.isArray(node.material) ? node.material : node.material ? [node.material] : []) {
25230
+ for (const uniform2 of Object.values(material.uniforms ?? {})) if (isTexture(uniform2.value)) uniform2.value.dispose();
25231
+ material.map?.dispose();
25232
+ }
25233
+ });
25234
+ }
25235
+ };
25236
+ }
24391
25237
  };
24392
25238
  }
24393
25239
  return __toCommonJS(index_exports);