@adatechnology/game-floorplan-three 0.9.0 → 0.9.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.
@@ -70,7 +70,7 @@ var GameFloorplanThree = (() => {
70
70
  shade: () => shade
71
71
  });
72
72
 
73
- // ../game-shapes/dist/chunk-IFXQIDSA.js
73
+ // ../game-shapes/dist/chunk-M5N2SHNL.js
74
74
  function canonicalJson(value) {
75
75
  if (value === null) return "null";
76
76
  if (typeof value === "number") return Object.is(value, -0) ? "-0" : Number.isFinite(value) ? String(value) : JSON.stringify(String(value));
@@ -1227,13 +1227,13 @@ var GameFloorplanThree = (() => {
1227
1227
  uvArray.push(x);
1228
1228
  uvArray.push(y);
1229
1229
  };
1230
- const at = (index, axis) => verticesArray[index * 3 + axis];
1230
+ const at2 = (index, axis) => verticesArray[index * 3 + axis];
1231
1231
  const f3 = (a, b, c) => {
1232
1232
  addVertex(a);
1233
1233
  addVertex(b);
1234
1234
  addVertex(c);
1235
1235
  const nextIndex = verticesArray.length / 3;
1236
- for (const index of [nextIndex - 3, nextIndex - 2, nextIndex - 1]) addUV(at(index, 0), at(index, 1));
1236
+ for (const index of [nextIndex - 3, nextIndex - 2, nextIndex - 1]) addUV(at2(index, 0), at2(index, 1));
1237
1237
  };
1238
1238
  const f4 = (a, b, c, d) => {
1239
1239
  addVertex(a);
@@ -1247,8 +1247,8 @@ var GameFloorplanThree = (() => {
1247
1247
  const iB = nextIndex - 3;
1248
1248
  const iC = nextIndex - 2;
1249
1249
  const iD = nextIndex - 1;
1250
- const byX = Math.abs(at(iA, 1) - at(iB, 1)) < Math.abs(at(iA, 0) - at(iB, 0));
1251
- const uv = (index) => [byX ? at(index, 0) : at(index, 1), 1 - at(index, 2)];
1250
+ const byX = Math.abs(at2(iA, 1) - at2(iB, 1)) < Math.abs(at2(iA, 0) - at2(iB, 0));
1251
+ const uv = (index) => [byX ? at2(index, 0) : at2(index, 1), 1 - at2(index, 2)];
1252
1252
  const uvs = [uv(iA), uv(iB), uv(iC), uv(iD)];
1253
1253
  for (const k of [0, 1, 3, 1, 2, 3]) addUV(...uvs[k]);
1254
1254
  };
@@ -1353,8 +1353,8 @@ var GameFloorplanThree = (() => {
1353
1353
  groups: []
1354
1354
  };
1355
1355
  }
1356
- function make(kind, p) {
1357
- switch (kind) {
1356
+ function make(p) {
1357
+ switch (p.kind) {
1358
1358
  case "box": {
1359
1359
  const size = p.size ?? [1, 1, 1];
1360
1360
  const segments = p.segments ?? [1, 1, 1];
@@ -1383,7 +1383,7 @@ var GameFloorplanThree = (() => {
1383
1383
  case "mesh":
1384
1384
  return mesh(p);
1385
1385
  default:
1386
- throw new Error(`game-shapes: unknown primitive kind "${String(kind)}"`);
1386
+ throw new Error(`game-shapes: unknown primitive kind "${String(p.kind)}"`);
1387
1387
  }
1388
1388
  }
1389
1389
  var templates = /* @__PURE__ */ new Map();
@@ -1391,7 +1391,7 @@ var GameFloorplanThree = (() => {
1391
1391
  var stats = { hits: 0, misses: 0 };
1392
1392
  function buildPrimitive(node, options = {}) {
1393
1393
  const parameters = primitiveParameters(node, options);
1394
- if (node.kind === "mesh" || templateLimit === 0) return make(node.kind, parameters);
1394
+ if (node.kind === "mesh" || templateLimit === 0) return make({ ...node, ...parameters });
1395
1395
  const key = `${node.kind}:${canonicalJson(parameters)}`;
1396
1396
  const cached = templates.get(key);
1397
1397
  if (cached) {
@@ -1401,7 +1401,7 @@ var GameFloorplanThree = (() => {
1401
1401
  return cached;
1402
1402
  }
1403
1403
  stats.misses++;
1404
- const built = make(node.kind, parameters);
1404
+ const built = make({ ...node, ...parameters });
1405
1405
  templates.set(key, built);
1406
1406
  if (templates.size > templateLimit) templates.delete(templates.keys().next().value);
1407
1407
  return built;
@@ -1499,7 +1499,7 @@ var GameFloorplanThree = (() => {
1499
1499
  const positions2 = new Float32Array(total * 3);
1500
1500
  const normals = new Float32Array(total * 3);
1501
1501
  const uvs = new Float32Array(total * 2);
1502
- let at = 0;
1502
+ let at2 = 0;
1503
1503
  for (const piece2 of pieces) {
1504
1504
  const e = piece2.matrix;
1505
1505
  const nm = normalMatrix(e);
@@ -1513,15 +1513,15 @@ var GameFloorplanThree = (() => {
1513
1513
  if (piece2.normals) n[k * 3 + c] = piece2.normals[i * 3 + c];
1514
1514
  }
1515
1515
  if (piece2.uvs) {
1516
- uvs[(at + k) * 2] = piece2.uvs[i * 2];
1517
- uvs[(at + k) * 2 + 1] = piece2.uvs[i * 2 + 1];
1516
+ uvs[(at2 + k) * 2] = piece2.uvs[i * 2];
1517
+ uvs[(at2 + k) * 2 + 1] = piece2.uvs[i * 2 + 1];
1518
1518
  }
1519
1519
  }
1520
1520
  for (let k = 0; k < count; k++) {
1521
1521
  const q = transformPoint(e, p[k * 3], p[k * 3 + 1], p[k * 3 + 2]);
1522
- positions2[(at + k) * 3] = q[0];
1523
- positions2[(at + k) * 3 + 1] = q[1];
1524
- positions2[(at + k) * 3 + 2] = q[2];
1522
+ positions2[(at2 + k) * 3] = q[0];
1523
+ positions2[(at2 + k) * 3 + 1] = q[1];
1524
+ positions2[(at2 + k) * 3 + 2] = q[2];
1525
1525
  const x = n[k * 3];
1526
1526
  const y = n[k * 3 + 1];
1527
1527
  const z = n[k * 3 + 2];
@@ -1529,11 +1529,11 @@ var GameFloorplanThree = (() => {
1529
1529
  const vy = nm[1] * x + nm[4] * y + nm[7] * z;
1530
1530
  const vz = nm[2] * x + nm[5] * y + nm[8] * z;
1531
1531
  const s = 1 / (Math.sqrt(vx * vx + vy * vy + vz * vz) || 1);
1532
- normals[(at + k) * 3] = vx * s;
1533
- normals[(at + k) * 3 + 1] = vy * s;
1534
- normals[(at + k) * 3 + 2] = vz * s;
1532
+ normals[(at2 + k) * 3] = vx * s;
1533
+ normals[(at2 + k) * 3 + 1] = vy * s;
1534
+ normals[(at2 + k) * 3 + 2] = vz * s;
1535
1535
  }
1536
- at += count;
1536
+ at2 += count;
1537
1537
  }
1538
1538
  return { positions: positions2, normals, uvs };
1539
1539
  }
@@ -3408,17 +3408,17 @@ var GameFloorplanThree = (() => {
3408
3408
  const index = geometry2.index ? geometry2.index.array : null;
3409
3409
  const count = index ? index.length : position.count;
3410
3410
  for (let item = 0; item < count; item++) {
3411
- const at = index ? index[item] : item;
3412
- vertex.fromBufferAttribute(position, at).applyMatrix4(matrix);
3411
+ const at2 = index ? index[item] : item;
3412
+ vertex.fromBufferAttribute(position, at2).applyMatrix4(matrix);
3413
3413
  positions2.push(vertex.x, vertex.y, vertex.z);
3414
- if (normalAttribute) normal.fromBufferAttribute(normalAttribute, at).applyMatrix3(normalMatrix2).normalize();
3414
+ if (normalAttribute) normal.fromBufferAttribute(normalAttribute, at2).applyMatrix3(normalMatrix2).normalize();
3415
3415
  else normal.set(0, 1, 0);
3416
3416
  normals.push(normal.x, normal.y, normal.z);
3417
3417
  color2.copy(material2.color || color2.set(1, 1, 1)).convertLinearToSRGB();
3418
3418
  if (sample && uv && material2.map) {
3419
3419
  const repeat = material2.map.repeat;
3420
- const u = (uv.getX(at) * repeat.x % 1 + 1) % 1;
3421
- const v = (uv.getY(at) * repeat.y % 1 + 1) % 1;
3420
+ const u = (uv.getX(at2) * repeat.x % 1 + 1) % 1;
3421
+ const v = (uv.getY(at2) * repeat.y % 1 + 1) % 1;
3422
3422
  const pixelX = Math.min(sample.width - 1, u * sample.width | 0);
3423
3423
  const pixelY = Math.min(sample.height - 1, (1 - v) * sample.height | 0);
3424
3424
  const offset = (pixelY * sample.width + pixelX) * 4;
@@ -9871,8 +9871,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
9871
9871
  // was `maquina`
9872
9872
  });
9873
9873
  var OWN_TYPES = Object.freeze({ lamp: true, gas_piped: true });
9874
+ var sourceOf = (result) => result;
9874
9875
  var ADAPTER_VERSION = "0.3.0";
9875
9876
  var r3 = (n) => Math.round(n * 1e3) / 1e3;
9877
+ var at = (it) => ({ x: it.x ?? 0, z: it.z ?? 0, ry: it.ry ?? 0 });
9876
9878
  function mergeRegistry(base, extra) {
9877
9879
  const out = { ...base };
9878
9880
  if (extra) {
@@ -9891,14 +9893,15 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
9891
9893
  });
9892
9894
  function create2(options = {}) {
9893
9895
  const opts = options || {};
9894
- const GI = opts.GameInteractables;
9895
- const HH = opts.GameInteractablesHousehold;
9896
9896
  const locale = opts.locale || "pt-BR";
9897
9897
  const msg = createTranslator2(locale, opts.messages);
9898
- if (!GI || !HH) throw new Error(msg("error.missingKit"));
9898
+ if (!opts.GameInteractables || !opts.GameInteractablesHousehold) throw new Error(msg("error.missingKit"));
9899
+ const GI = opts.GameInteractables;
9900
+ const HH = opts.GameInteractablesHousehold;
9899
9901
  const region = opts.region || "br";
9900
9902
  const hh = HH.createHousehold({ locale, region });
9901
9903
  const householdTypes = new Set(hh.types.map((t) => t.id));
9904
+ const recorderApi = () => HH.createRecorder().api;
9902
9905
  const worlds = /* @__PURE__ */ new WeakMap();
9903
9906
  const typeMap = mergeRegistry(TYPE_MAP, opts.typeMap);
9904
9907
  const contentMap = mergeRegistry(CONTENT_MAP, opts.contentMap);
@@ -9934,7 +9937,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
9934
9937
  function colorOf(res, color2) {
9935
9938
  if (color2 == null) return void 0;
9936
9939
  if (typeof color2 === "number") return color2;
9937
- const v = res.colors && res.colors[color2];
9940
+ const v = res.colors && res.colors[String(color2)];
9938
9941
  return typeof v === "number" ? v : void 0;
9939
9942
  }
9940
9943
  function paletteOf(res) {
@@ -9963,17 +9966,18 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
9963
9966
  drawerItem: (s) => drawerItems.has(s.itemId)
9964
9967
  };
9965
9968
  const itemOfContent = (c) => {
9966
- const id = contentMap[c.itemId] || (hh.items[c.itemId] ? c.itemId : null) || contentByGroup[stackGroup(c)] || contentFallback;
9969
+ const group = stackGroup(c);
9970
+ const id = contentMap[c.itemId] || (hh.items[c.itemId] ? c.itemId : null) || (group != null ? contentByGroup[group] : void 0) || contentFallback;
9967
9971
  return hh.items[id] ? id : contentFallback;
9968
9972
  };
9969
9973
  const volume = (d) => d.size[0] * d.size[1] * d.size[2];
9970
9974
  const candidates = (id, objectType) => {
9971
9975
  const def = hh.items[id];
9972
- const siblings = Object.values(hh.items).filter((i) => i.category === def.category && i.id !== id).sort((a, b) => volume(a) - volume(b) || (a.id < b.id ? -1 : 1));
9976
+ const siblings = Object.values(hh.items).filter((i) => i.category === def?.category && i.id !== id).sort((a, b) => volume(a) - volume(b) || (a.id < b.id ? -1 : 1));
9973
9977
  return [id].concat(siblings.map((i) => i.id), substitutes[objectType] || []);
9974
9978
  };
9975
9979
  function distribute(type, probeItem, stacks) {
9976
- const spec = hh.build(type, probeItem, HH.createRecorder().api);
9980
+ const spec = hh.build(type, probeItem, recorderApi());
9977
9981
  const conts = spec.containers || [];
9978
9982
  const contents = {};
9979
9983
  for (const c of conts) contents[c.id] = c.slots.map(() => null);
@@ -10004,8 +10008,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
10004
10008
  }
10005
10009
  return { contents, rejected };
10006
10010
  }
10007
- function plan(res, popts = {}) {
10008
- popts = popts || {};
10011
+ function plan(source, options2 = {}) {
10012
+ const res = sourceOf(source);
10013
+ const popts = options2 || {};
10009
10014
  const o = { ox: popts.originX || 0, oz: popts.originZ || 0, inw: popts.inw || 1, base: popts.base || 0 };
10010
10015
  const K = conv(o);
10011
10016
  const floorSel = popts.floor;
@@ -10069,7 +10074,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
10069
10074
  const item = {
10070
10075
  x: K.x(m.x),
10071
10076
  z: K.z(m.z),
10072
- ry: m.frontU != null ? K.ry(m.frontU, m.frontV) : r3(m.ry || 0),
10077
+ ry: m.frontU != null ? K.ry(m.frontU, m.frontV ?? 0) : r3(m.ry || 0),
10073
10078
  w: m.w,
10074
10079
  d: m.d,
10075
10080
  h: m.h,
@@ -10082,7 +10087,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
10082
10087
  return item;
10083
10088
  }
10084
10089
  function entry2(id, type, item, origin, floor, extra) {
10085
- return Object.assign({ id, type, item, origin, floor, floorY: floorYOf(res, o, floor), connections: {} }, extra || {});
10090
+ const e = { id, type, item, origin, floor, floorY: floorYOf(res, o, floor), connections: {} };
10091
+ return Object.assign(e, extra || {});
10086
10092
  }
10087
10093
  function store(targets, stacks) {
10088
10094
  let rest2 = stacks || [];
@@ -10095,10 +10101,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
10095
10101
  }
10096
10102
  const hidden = [];
10097
10103
  function distRect(c, x, z) {
10098
- const dx = x - c.item.x;
10099
- const dz = z - c.item.z;
10100
- const s = Math.sin(c.item.ry);
10101
- const co = Math.cos(c.item.ry);
10104
+ const ci = at(c.item);
10105
+ const dx = x - ci.x;
10106
+ const dz = z - ci.z;
10107
+ const s = Math.sin(ci.ry);
10108
+ const co = Math.cos(ci.ry);
10102
10109
  const lx = dx * co - dz * s;
10103
10110
  const lz = dx * s + dz * co;
10104
10111
  return Math.hypot(Math.max(Math.abs(lx) - (c.item.w || 0) / 2, 0), Math.max(Math.abs(lz) - (c.item.d || 0) / 2, 0));
@@ -10106,9 +10113,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
10106
10113
  function hideCylinders() {
10107
10114
  for (const b of hidden) {
10108
10115
  let target = null;
10116
+ const bi = at(b.item);
10109
10117
  for (const c2 of result.items) {
10110
10118
  if (c2.type !== "kitchen_cabinet_base" || c2.floor !== b.floor) continue;
10111
- const dist = distRect(c2, b.item.x, b.item.z);
10119
+ const dist = distRect(c2, bi.x, bi.z);
10112
10120
  if (!target || dist < target.dist) target = { c: c2, dist };
10113
10121
  }
10114
10122
  if (!target) {
@@ -10116,16 +10124,18 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
10116
10124
  continue;
10117
10125
  }
10118
10126
  const c = target.c;
10119
- const spec = hh.build(c.type, c.item, HH.createRecorder().api);
10120
- const sn = Math.sin(c.item.ry);
10121
- const cs = Math.cos(c.item.ry);
10127
+ const spec = hh.build(c.type, c.item, recorderApi());
10128
+ const ci = at(c.item);
10129
+ const sn = Math.sin(ci.ry);
10130
+ const cs = Math.cos(ci.ry);
10122
10131
  let door = null;
10123
10132
  for (const it of spec.interactions || []) {
10124
- const pt = it.action && it.action.do === "toggle_part" ? (spec.parts || []).find((q) => q.id === it.action.part) : null;
10125
- if (!pt || pt.motion.kind !== "hinge") continue;
10126
- const wx = c.item.x + it.pos[0] * cs + it.pos[2] * sn;
10127
- const wz = c.item.z - it.pos[0] * sn + it.pos[2] * cs;
10128
- const dist = Math.hypot(wx - b.item.x, wz - b.item.z);
10133
+ const action = it.action;
10134
+ const pt = action.do === "toggle_part" ? (spec.parts || []).find((q) => q.id === action.part) : null;
10135
+ if (!pt || !pt.motion || pt.motion.kind !== "hinge") continue;
10136
+ const wx = ci.x + it.pos[0] * cs + it.pos[2] * sn;
10137
+ const wz = ci.z - it.pos[0] * sn + it.pos[2] * cs;
10138
+ const dist = Math.hypot(wx - bi.x, wz - bi.z);
10129
10139
  if (!door || dist < door.dist) door = { id: pt.id, dist };
10130
10140
  }
10131
10141
  if (!door) {
@@ -10155,9 +10165,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
10155
10165
  const campCylinder = firstOfType("gas_canister");
10156
10166
  function planFireplace(m, e, item) {
10157
10167
  const def = hh.types.find((t) => t.id === "fireplace");
10158
- const lack = (def.d - m.d) / 2;
10159
- item.x = r3(item.x + Math.sin(item.ry) * lack);
10160
- item.z = r3(item.z + Math.cos(item.ry) * lack);
10168
+ const lack = ((def ? def.d : m.d) - m.d) / 2;
10169
+ const p = at(item);
10170
+ item.x = r3(p.x + Math.sin(p.ry) * lack);
10171
+ item.z = r3(p.z + Math.cos(p.ry) * lack);
10161
10172
  const room = (res.rooms || []).find((c) => c.id === m.room && c.floor === m.floor);
10162
10173
  const f = (res.floors || []).find((x) => x.index === m.floor);
10163
10174
  item.h = r3(room && room.ceiling || f && f.ceiling || 2.6);
@@ -10185,7 +10196,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
10185
10196
  e.connections.gas = m.gas || "cylinder";
10186
10197
  e.connections.cylinder = m.cylinder || null;
10187
10198
  }
10188
- if (map.type === "gas_bottle") result.cylinders.push({ id: e.id, stove: m.link || null, place: m.placement || null });
10199
+ if (map.type === "gas_bottle") result.cylinders.push({ id: e.id, stove: typeof m.link === "string" ? m.link : null, place: m.placement || null });
10189
10200
  if (map.type === "gas_canister") {
10190
10201
  result.canisters.push(e.id);
10191
10202
  if (campCylinder && campCylinder.id === m.id && campStove) result.cylinders.push({ id: e.id, stove: campStove.id, place: null });
@@ -10209,7 +10220,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
10209
10220
  push(e);
10210
10221
  }
10211
10222
  function planCompound(m, kind) {
10212
- const ry = m.frontU != null ? K.ry(m.frontU, m.frontV) : r3(m.ry || 0);
10223
+ const ry = m.frontU != null ? K.ry(m.frontU, m.frontV ?? 0) : r3(m.ry || 0);
10213
10224
  const cx = K.x(m.x);
10214
10225
  const cz = K.z(m.z);
10215
10226
  const part = (id, type, w, d, h, y, lx, lz) => {
@@ -10261,11 +10272,12 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
10261
10272
  } else {
10262
10273
  const basin = part(m.id, "sink_bathroom", m.w, m.d, m.h + 0.2, 0, 0, 0);
10263
10274
  list2.push(basin);
10264
- if (contents[m.id]) {
10265
- if (contents[m.id].length) {
10275
+ const own = contents[m.id];
10276
+ if (own) {
10277
+ if (own.length) {
10266
10278
  const cabinet = part(m.id + "#cabinet", "bathroom_cabinet", Math.max(0.45, Math.min(0.6, m.w)), 0.15, 0.75, 1.3, 0, -(m.d / 2) + 0.075);
10267
10279
  list2.push(cabinet);
10268
- store([["bathroom_cabinet", cabinet.item], ["sink_bathroom", basin.item]], contents[m.id]);
10280
+ store([["bathroom_cabinet", cabinet.item], ["sink_bathroom", basin.item]], own);
10269
10281
  } else {
10270
10282
  store([["sink_bathroom", basin.item]], []);
10271
10283
  }
@@ -10305,7 +10317,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
10305
10317
  result.chairs = [];
10306
10318
  if (opts.chairs !== false && popts.chairs !== false) {
10307
10319
  const blocked = blockedOf(res, { originX: o.ox, originZ: o.oz, inw: o.inw });
10308
- const floorKind = (q) => !q.decor && !q.flat && (q.y || 0) < 0.9 && q.aabb;
10320
+ const floorKind = (q) => !q.decor && !q.flat && (q.y || 0) < 0.9 && !!q.aabb;
10309
10321
  const placed = [];
10310
10322
  const free = (m, cu, cv, half) => {
10311
10323
  const a = [cu - half + 0.03, cv - half + 0.03, cu + half - 0.03, cv + half - 0.03];
@@ -10321,8 +10333,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
10321
10333
  for (const m of res.items || []) {
10322
10334
  const mode = chairTables[m.type];
10323
10335
  if (!mode || !onFloor(m.floor, m.outdoor) || m.frontU == null) continue;
10324
- const f = [m.frontU, m.frontV];
10325
- const pp = [-m.frontV, m.frontU];
10336
+ const fv = m.frontV ?? 0;
10337
+ const f = [m.frontU, fv];
10338
+ const pp = [-fv, m.frontU];
10326
10339
  const seats = [];
10327
10340
  if (mode === "dining") {
10328
10341
  const n = Math.max(1, Math.min(3, Math.floor((m.w - 0.1) / 0.55)));
@@ -10391,8 +10404,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
10391
10404
  const item = { x: K.x(s.u), z: K.z(s.v), ry: K.ry(s.frontU, s.frontV), y: s.y, seed: seedOf2(s.id) };
10392
10405
  item.controls = (s.controls || []).map((id) => id + ":lamp");
10393
10406
  const e = entry2(s.id, "wall_switch", item, s.id, s.floor);
10394
- e.connections.controls = (s.controls || []).slice();
10395
- e.lit = e.connections.controls.some((id) => (result.items.find((x) => x.id === id) || {}).lit === true);
10407
+ const controls = (s.controls || []).slice();
10408
+ e.connections.controls = controls;
10409
+ e.lit = controls.some((id) => result.items.find((x) => x.id === id)?.lit === true);
10396
10410
  if (floorSel == null) {
10397
10411
  for (const id of s.controls || []) if (!lights.has(id)) result.warnings.push(msg("warn.switchUnknownLamp", { id: s.id, lamp: id }));
10398
10412
  }
@@ -10432,7 +10446,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
10432
10446
  seed: seedOf2("panel"),
10433
10447
  circuits: circuits.map((c) => ({ id: c.id, label: c.room }))
10434
10448
  };
10435
- if (!item.circuits.length) delete item.circuits;
10449
+ if (!item.circuits?.length) delete item.circuits;
10436
10450
  const e = entry2("panel", "electrical_panel", item, "panel", q.floor);
10437
10451
  push(e);
10438
10452
  result.panel = e.id;
@@ -10454,10 +10468,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
10454
10468
  }
10455
10469
  }
10456
10470
  const piped = result.items.filter((e) => e.type === "stove" && e.connections.gas === "piped");
10457
- if (piped.length) {
10471
+ const firstPiped = piped[0];
10472
+ if (firstPiped) {
10458
10473
  const med = utilities.gas && utilities.gas.meter;
10459
- const ref = med ? { x: K.x(med.u), z: K.z(med.v), y: med.y || 1 } : { x: piped[0].item.x, z: piped[0].item.z, y: 0.3 };
10460
- const e = entry2("piped_gas", "gas_piped", { x: ref.x, z: ref.z, ry: 0, y: ref.y, seed: seedOf2("piped_gas") }, "piped_gas", piped[0].floor);
10474
+ const ref = med ? { x: K.x(med.u), z: K.z(med.v), y: med.y || 1 } : { x: firstPiped.item.x, z: firstPiped.item.z, y: 0.3 };
10475
+ const e = entry2("piped_gas", "gas_piped", { x: ref.x, z: ref.z, ry: 0, y: ref.y, seed: seedOf2("piped_gas") }, "piped_gas", firstPiped.floor);
10461
10476
  e.floorY = o.base;
10462
10477
  push(e);
10463
10478
  for (const f of piped) f.connections.fuel = "piped_gas:gas";
@@ -10504,9 +10519,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
10504
10519
  for (const dv of spec.devices || []) if (L.outlet && dv.socket === "auto") dv.socket = L.outlet + ":socket";
10505
10520
  } else {
10506
10521
  const sh = L.shower || {};
10507
- const heater = (spec.devices || []).find((d) => d.id === "heater");
10522
+ const devices = spec.devices || [];
10523
+ const heater = devices.find((d) => d.id === "heater");
10508
10524
  if (heater && sh.electric === false) {
10509
- spec.devices = spec.devices.filter((d) => d !== heater);
10525
+ spec.devices = devices.filter((d) => d !== heater);
10510
10526
  for (const f of spec.fixtures || []) if (f.heater === "heater") delete f.heater;
10511
10527
  } else if (heater) {
10512
10528
  if (sh.powerW) heater.watts = sh.powerW;
@@ -10517,7 +10533,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
10517
10533
  for (const b of spec.burners || []) if (b.fuel === "auto") b.fuel = L.fuel;
10518
10534
  }
10519
10535
  if (entry2.type === "fireplace" && L.chimneyTop) {
10520
- const it = entry2.item;
10536
+ const it = at(entry2.item);
10521
10537
  const dx = L.chimneyTop[0] - it.x;
10522
10538
  const dz = L.chimneyTop[2] - it.z;
10523
10539
  const lx = Math.cos(it.ry) * dx - Math.sin(it.ry) * dz;
@@ -10536,8 +10552,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
10536
10552
  }
10537
10553
  return spec;
10538
10554
  }
10539
- function createWorld(res, copts = {}) {
10540
- copts = copts || {};
10555
+ function createWorld(source, options2 = {}) {
10556
+ const res = sourceOf(source);
10557
+ const copts = options2 || {};
10541
10558
  const info = { points: /* @__PURE__ */ new Map(), water: copts.water || null, lightsOn: copts.lightsOn };
10542
10559
  const electrical = res.utilities && res.utilities.electrical;
10543
10560
  const world = GI.createWorld({
@@ -10565,8 +10582,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
10565
10582
  const info = worlds.get(world);
10566
10583
  return info ? info.points.get(fixtureId) : void 0;
10567
10584
  }
10568
- function blockedOf(res, bopts = {}) {
10569
- bopts = bopts || {};
10585
+ function blockedOf(source, options2 = {}) {
10586
+ const res = sourceOf(source);
10587
+ const bopts = options2 || {};
10570
10588
  const ox = bopts.originX || 0;
10571
10589
  const oz = bopts.originZ || 0;
10572
10590
  const inw = bopts.inw || 1;
@@ -10601,33 +10619,33 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
10601
10619
  return false;
10602
10620
  };
10603
10621
  }
10604
- function add(world, planned, aopts = {}) {
10605
- aopts = aopts || {};
10622
+ function add(world, planned, options2 = {}) {
10623
+ const aopts = options2 || {};
10606
10624
  const info = worlds.get(world);
10607
- const build = aopts.build || ((type, item) => hh.build(type, item, HH.createRecorder().api));
10625
+ const build = aopts.build || ((type, item) => hh.build(type, item, recorderApi()));
10608
10626
  const isBlocked = aopts.isBlocked || null;
10609
10627
  const out = { added: 0, parts: [], hinges: {}, moved: {}, warnings: [] };
10610
10628
  const override = aopts.lightsOn !== void 0 ? aopts.lightsOn : info && info.lightsOn;
10611
10629
  if (override !== void 0) {
10612
10630
  for (const e of planned.items) if (e.type === "lamp") e.lit = typeof override === "function" ? !!override(e.id, e) : !!override;
10613
10631
  for (const e of planned.items) if (e.type === "lantern_lamp") e.lit = typeof override === "function" ? !!override(e.id, e) : !!override;
10614
- for (const e of planned.items) if (e.type === "wall_switch") e.lit = (e.connections.controls || []).some((id) => (planned.items.find((x) => x.id === id) || {}).lit === true);
10632
+ for (const e of planned.items) if (e.type === "wall_switch") e.lit = (e.connections.controls || []).some((id) => planned.items.find((x) => x.id === id)?.lit === true);
10615
10633
  }
10616
10634
  for (const e of planned.items) if (e.type === "lantern_lamp") e.item.on = !!e.lit;
10617
10635
  let scratch = null;
10618
- function probe(e, hinge, setback) {
10636
+ function probe(e, hinge, setback, blockedAt) {
10619
10637
  if (!scratch) scratch = GI.createWorld({ locale, strings: hh.strings, items: hh.items, loot: hh.loot, grid: false });
10620
- const it = e.item;
10638
+ const it = at(e.item);
10621
10639
  const x = it.x + Math.sin(it.ry) * setback;
10622
10640
  const z = it.z + Math.cos(it.ry) * setback;
10623
- const spec = hh.build(e.type, Object.assign({}, it, { hinge }), HH.createRecorder().api);
10641
+ const spec = hh.build(e.type, Object.assign({}, e.item, { hinge }), recorderApi());
10624
10642
  scratch.add("__probe", spec, { x, y: e.floorY, z, ry: it.ry });
10625
- const blocks = (px, pz) => isBlocked(px, pz, e.floor);
10643
+ const blocks = (px, pz) => blockedAt(px, pz, e.floor);
10626
10644
  let sum = 0;
10627
10645
  let min = 1;
10628
10646
  let n = 0;
10629
10647
  for (const p of spec.parts || []) {
10630
- if (p.motion.kind !== "hinge" || !(p.bounds || p.sweep)) continue;
10648
+ if (!p.motion || p.motion.kind !== "hinge" || !(p.bounds || p.sweep)) continue;
10631
10649
  const t = scratch.fitSweep("__probe", p.id, blocks);
10632
10650
  sum += t;
10633
10651
  min = Math.min(min, t);
@@ -10639,12 +10657,12 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
10639
10657
  const better = (a, b) => a.sum > b.sum + 1e-9 || Math.abs(a.sum - b.sum) <= 1e-9 && a.min > b.min + 1e-9;
10640
10658
  function fitDoors(e) {
10641
10659
  if (!isBlocked || !householdTypes.has(e.type)) return;
10642
- const standard = probe(e, void 0, 0);
10660
+ const standard = probe(e, void 0, 0, isBlocked);
10643
10661
  if (!standard.n || standard.min >= 1) return;
10644
10662
  let bestSide;
10645
10663
  let m = standard;
10646
10664
  for (const side of ["left", "right"]) {
10647
- const r = probe(e, side, 0);
10665
+ const r = probe(e, side, 0, isBlocked);
10648
10666
  if (better(r, m)) {
10649
10667
  m = r;
10650
10668
  bestSide = side;
@@ -10657,13 +10675,14 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
10657
10675
  if (m.min >= 1) return;
10658
10676
  let target = { setback: 0, r: m };
10659
10677
  for (const setback of [0.01, 0.02, 0.03, 0.04]) {
10660
- const r = probe(e, bestSide, setback);
10678
+ const r = probe(e, bestSide, setback, isBlocked);
10661
10679
  if (better(r, target.r)) target = { setback, r };
10662
10680
  if (r.min >= 1) break;
10663
10681
  }
10664
10682
  if (target.setback > 0 && target.r.sum > m.sum + 0.2) {
10665
- e.item.x = r3(e.item.x + Math.sin(e.item.ry) * target.setback);
10666
- e.item.z = r3(e.item.z + Math.cos(e.item.ry) * target.setback);
10683
+ const p = at(e.item);
10684
+ e.item.x = r3(p.x + Math.sin(p.ry) * target.setback);
10685
+ e.item.z = r3(p.z + Math.cos(p.ry) * target.setback);
10667
10686
  e.moved = target.setback;
10668
10687
  out.moved[e.id] = target.setback;
10669
10688
  }
@@ -10686,7 +10705,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
10686
10705
  }
10687
10706
  }
10688
10707
  connect(e, spec);
10689
- world.add(e.id, spec, { x: e.item.x, y: e.floorY, z: e.item.z, ry: e.item.ry });
10708
+ const pose = at(e.item);
10709
+ world.add(e.id, spec, { x: pose.x, y: e.floorY, z: pose.z, ry: pose.ry });
10690
10710
  out.added++;
10691
10711
  if (!isBlocked) continue;
10692
10712
  for (const p of spec.parts || []) {
@@ -10926,22 +10946,22 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
10926
10946
  A.stats.external = Ne;
10927
10947
  let shadows = 0;
10928
10948
  for (let i = 0; i < N; i++) {
10929
- const g = asg[i], at = slotOf(i), b0 = LZU.uRB0.value, b1 = LZU.uRB1.value, b2 = LZU.uRB2.value, by = LZU.uRBY.value, ex = LZU.uREx.value;
10949
+ const g = asg[i], at2 = slotOf(i), b0 = LZU.uRB0.value, b1 = LZU.uRB1.value, b2 = LZU.uRB2.value, by = LZU.uRBY.value, ex = LZU.uREx.value;
10930
10950
  A.crates[i] = g ? { k: g.k, rects: g.rects, y: g.y, exterior: !!g.exterior, exclusion: g.exclusion || null, d: g.d } : null;
10931
10951
  if (!g) {
10932
10952
  put(i, { intensity: 0 });
10933
- for (const a of [b0, b1, b2, ex]) a.set(EMPTY, at * 4);
10934
- by.set([0, 0], at * 2);
10953
+ for (const a of [b0, b1, b2, ex]) a.set(EMPTY, at2 * 4);
10954
+ by.set([0, 0], at2 * 2);
10935
10955
  continue;
10936
10956
  }
10937
10957
  const lamp = g.exterior ? { intensity: g.lumens, distance: g.dist, decay: g.decay } : { intensity: (7 + g.range * 0.8) * lim(1 + 0.4 * (Math.sqrt(g.n) - 1), 1, 2.2), distance: Math.max(3, g.dm * 1.3 + 0.5), decay: 1.6 };
10938
10958
  put(i, { position: g.pos, color: g.color, ...lamp });
10939
10959
  const rs = g.rects;
10940
- b0.set(rs[0] || EMPTY, at * 4);
10941
- b1.set(rs[1] || EMPTY, at * 4);
10942
- b2.set(rs[2] || EMPTY, at * 4);
10943
- by.set(g.y, at * 2);
10944
- ex.set(g.exclusion || EMPTY, at * 4);
10960
+ b0.set(rs[0] || EMPTY, at2 * 4);
10961
+ b1.set(rs[1] || EMPTY, at2 * 4);
10962
+ b2.set(rs[2] || EMPTY, at2 * 4);
10963
+ by.set(g.y, at2 * 2);
10964
+ ex.set(g.exclusion || EMPTY, at2 * 4);
10945
10965
  if (i < NS) {
10946
10966
  shadows++;
10947
10967
  const s2 = g.house.group.uuid + "|" + g.k + "|" + g.pos.map((v) => v.toFixed(2)).join(",");
@@ -11016,10 +11036,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
11016
11036
  for (const p of list2 || []) {
11017
11037
  if (isDoor && !(open.has(p.id) ? open.get(p.id) : !!p.open)) continue;
11018
11038
  const alongU = p.axis === "u", y = house.y0(p.floor) + (isDoor ? 1 : ((p.y0 != null ? p.y0 : 0.9) + (p.y1 != null ? p.y1 : 2)) / 2);
11019
- const at = (side) => _p.set(alongU ? p.at : p.pos + side, y, alongU ? p.pos + side : p.at).applyMatrix4(M).clone();
11020
- if (at(0).distanceTo(P0) > OPENING_REACH) continue;
11039
+ const at2 = (side) => _p.set(alongU ? p.at : p.pos + side, y, alongU ? p.pos + side : p.at).applyMatrix4(M).clone();
11040
+ if (at2(0).distanceTo(P0) > OPENING_REACH) continue;
11021
11041
  for (const side of [-OPENING_SIDE, OPENING_SIDE]) {
11022
- const w = at(side), hit = boxAt(boxes, w.x, w.y, w.z);
11042
+ const w = at2(side), hit = boxAt(boxes, w.x, w.y, w.z);
11023
11043
  if (hit) into.set(hit.c, hit);
11024
11044
  }
11025
11045
  }
@@ -11027,11 +11047,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
11027
11047
  reach(house.r.doors, true);
11028
11048
  reach(house.r.windows, false);
11029
11049
  };
11030
- const clear = (at) => {
11031
- for (const key of ["uRB0", "uRB1", "uRB2", "uREx"]) LZU[key].value.set(EMPTY, at * 4);
11032
- LZU.uRBY.value.set([0, 0], at * 2);
11050
+ const clear = (at2) => {
11051
+ for (const key of ["uRB0", "uRB1", "uRB2", "uREx"]) LZU[key].value.set(EMPTY, at2 * 4);
11052
+ LZU.uRBY.value.set([0, 0], at2 * 2);
11033
11053
  };
11034
- const write = (at, into) => {
11054
+ const write = (at2, into) => {
11035
11055
  const rs = [];
11036
11056
  let lo = 1e9, hi = -1e9;
11037
11057
  for (const q of into.values()) {
@@ -11043,21 +11063,21 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
11043
11063
  const r23 = rs.pop(), r1 = rs[rs.length - 1];
11044
11064
  rs[rs.length - 1] = [Math.min(r1[0], r23[0]), Math.min(r1[1], r23[1]), Math.max(r1[2], r23[2]), Math.max(r1[3], r23[3])];
11045
11065
  }
11046
- LZU.uRB0.value.set(rs[0] || EMPTY, at * 4);
11047
- LZU.uRB1.value.set(rs[1] || EMPTY, at * 4);
11048
- LZU.uRB2.value.set(rs[2] || EMPTY, at * 4);
11049
- LZU.uRBY.value.set([lo, hi], at * 2);
11050
- LZU.uREx.value.set(EMPTY, at * 4);
11066
+ LZU.uRB0.value.set(rs[0] || EMPTY, at2 * 4);
11067
+ LZU.uRB1.value.set(rs[1] || EMPTY, at2 * 4);
11068
+ LZU.uRB2.value.set(rs[2] || EMPTY, at2 * 4);
11069
+ LZU.uRBY.value.set([lo, hi], at2 * 2);
11070
+ LZU.uREx.value.set(EMPTY, at2 * 4);
11051
11071
  };
11052
11072
  const A = {
11053
11073
  /** 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. */
11054
11074
  update(houses2) {
11055
11075
  const indoors = cfg.effectLightsIndoors !== false, list2 = (Array.isArray(houses2) ? houses2 : [houses2]).filter(Boolean), cache2 = /* @__PURE__ */ new Map();
11056
- for (let at = 0; at < size; at++) {
11057
- if (ours.has(at)) continue;
11058
- const light = budget.group.children[at];
11076
+ for (let at2 = 0; at2 < size; at2++) {
11077
+ if (ours.has(at2)) continue;
11078
+ const light = budget.group.children[at2];
11059
11079
  if (!indoors || !light || !light.isPointLight || !(light.intensity > 0) || !light.visible) {
11060
- clear(at);
11080
+ clear(at2);
11061
11081
  continue;
11062
11082
  }
11063
11083
  const into = /* @__PURE__ */ new Map();
@@ -11072,8 +11092,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
11072
11092
  if (here) into.set(here.c, here);
11073
11093
  openingSides(house, M, boxes, into);
11074
11094
  }
11075
- if (into.size) write(at, into);
11076
- else clear(at);
11095
+ if (into.size) write(at2, into);
11096
+ else clear(at2);
11077
11097
  }
11078
11098
  }
11079
11099
  };
@@ -11430,11 +11450,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
11430
11450
  }
11431
11451
  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] }));
11432
11452
  };
11433
- var severAt = (inp, root, at, gap) => {
11453
+ var severAt = (inp, root, at2, gap) => {
11434
11454
  const dims = root.s;
11435
11455
  const L = [0, 1, 2].sort((x, y) => dims[y] - dims[x])[0];
11436
11456
  const half = dims[L] / 2;
11437
- const lo = clamp(at - gap / 2, -half, half), hi = clamp(at + gap / 2, -half, half);
11457
+ const lo = clamp(at2 - gap / 2, -half, half), hi = clamp(at2 + gap / 2, -half, half);
11438
11458
  const out = [];
11439
11459
  const stub = (from, to) => {
11440
11460
  if (to - from < 0.02) return;
@@ -13816,7 +13836,13 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
13816
13836
  const typed = listeners.get(e.type);
13817
13837
  if (typed) for (const fn of [...typed]) fn(e);
13818
13838
  };
13819
- const base = (o, source) => source === void 0 ? { object: o.id, time } : { object: o.id, time, source };
13839
+ let blow;
13840
+ const base = (o, source) => ({
13841
+ object: o.id,
13842
+ time,
13843
+ ...source === void 0 ? {} : { source },
13844
+ ...blow === void 0 ? {} : { blow }
13845
+ });
13820
13846
  const pendingCollapse = /* @__PURE__ */ new Map();
13821
13847
  let runDepth = 0;
13822
13848
  const drainBlasts = () => {
@@ -14086,9 +14112,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
14086
14112
  const [lo, hi] = contactBox(p, above);
14087
14113
  return toWorld(p.pos, p.q, [(lo[0] + hi[0]) / 2, (lo[1] + hi[1]) / 2, (lo[2] + hi[2]) / 2]);
14088
14114
  };
14089
- const overSupportAt = (o, p, at, lower) => {
14115
+ const overSupportAt = (o, p, at2, lower) => {
14090
14116
  const g = p.bend;
14091
- const local = toLocal(p.pos, p.q, at);
14117
+ const local = toLocal(p.pos, p.q, at2);
14092
14118
  const depth = g.gravity >= 0 ? g.dims[g.gravity] : Math.min(...g.depthAxes.map((a) => g.dims[a]));
14093
14119
  let best = -1;
14094
14120
  let bestGap = depth;
@@ -14242,10 +14268,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
14242
14268
  const incoming = new Array(n).fill(0);
14243
14269
  const bending = new Array(n).fill(0);
14244
14270
  const through = parts.map(() => []);
14245
- const pass = (k, at, mass) => {
14246
- const over = overSupportAt(o, parts[k], at, lowerOf.get(k) ?? []);
14271
+ const pass = (k, at2, mass) => {
14272
+ const over = overSupportAt(o, parts[k], at2, lowerOf.get(k) ?? []);
14247
14273
  if (over < 0) bending[k] += mass;
14248
- else through[k].push({ at, mass, over });
14274
+ else through[k].push({ at: at2, mass, over });
14249
14275
  };
14250
14276
  for (let h = queue.length - 1; h >= 0; h--) {
14251
14277
  const i = queue[h];
@@ -14312,7 +14338,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
14312
14338
  const terrain = cfg.terrains.get(cfg.terrain);
14313
14339
  if (!terrain) throw new Error(`unknown wind terrain "${cfg.terrain}"`);
14314
14340
  const inputs = [];
14315
- const at = /* @__PURE__ */ new Map();
14341
+ const at2 = /* @__PURE__ */ new Map();
14316
14342
  const keep = [];
14317
14343
  let ground = Infinity, foundationTop = -Infinity;
14318
14344
  o.parts.forEach((p, i) => {
@@ -14321,7 +14347,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
14321
14347
  const role = p.spec.structure?.role;
14322
14348
  const e = p.mat.strength?.elasticGPa;
14323
14349
  const kind = role === "wall" ? "wall" : role === "column" ? "column" : role === "beam" ? "beam" : role === "foundation" ? "foundation" : "other";
14324
- at.set(i, inputs.length);
14350
+ at2.set(i, inputs.length);
14325
14351
  keep.push(i);
14326
14352
  ground = Math.min(ground, box3.min[1]);
14327
14353
  if (kind === "foundation") foundationTop = Math.max(foundationTop, box3.max[1]);
@@ -14332,7 +14358,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
14332
14358
  const beams = [];
14333
14359
  for (const j of o.byPart.get(o.parts[i].spec.id)) {
14334
14360
  if (j.st.open) continue;
14335
- const b = at.get(j.ia === i ? j.ib : j.ia);
14361
+ const b = at2.get(j.ia === i ? j.ib : j.ia);
14336
14362
  if (b !== void 0 && inputs[b].kind === "beam") beams.push(b);
14337
14363
  }
14338
14364
  inputs[k].beams = beams;
@@ -15148,7 +15174,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
15148
15174
  }
15149
15175
  };
15150
15176
  const tornToEdge = (o) => {
15151
- const { p, cell, a, b, at, crater: crater2, reach, gone } = o;
15177
+ const { p, cell, a, b, at: at2, crater: crater2, reach, gone } = o;
15152
15178
  const half = p.dims[a] / 2;
15153
15179
  const lo = cell.center[a] - cell.size[a] / 2, hi = cell.center[a] + cell.size[a] / 2;
15154
15180
  const free = (side, sign2) => {
@@ -15156,8 +15182,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
15156
15182
  const probe = side + sign2 * 1e-3;
15157
15183
  return gridOf(p).some((c, k) => gone(k) && Math.abs(probe - c.center[a]) <= c.size[a] / 2 && Math.abs(cell.center[b] - c.center[b]) <= c.size[b] / 2);
15158
15184
  };
15159
- if (at - lo <= reach && free(lo, -1)) return [lo, Math.max(crater2[1], lo)];
15160
- if (hi - at <= reach && free(hi, 1)) return [Math.min(crater2[0], hi), hi];
15185
+ if (at2 - lo <= reach && free(lo, -1)) return [lo, Math.max(crater2[1], lo)];
15186
+ if (hi - at2 <= reach && free(hi, 1)) return [Math.min(crater2[0], hi), hi];
15161
15187
  return crater2;
15162
15188
  };
15163
15189
  const craterAcross = (energy) => clamp2(0.03 + 12e-4 * Math.sqrt(Math.max(0, energy)), 0.03, 0.14);
@@ -15961,7 +15987,17 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
15961
15987
  res.events = cur.slice(start);
15962
15988
  return res;
15963
15989
  };
15964
- const doHit = (o, hit, only) => {
15990
+ const withBlow = (type, fn) => () => {
15991
+ const outer = blow;
15992
+ blow = type;
15993
+ try {
15994
+ return fn();
15995
+ } finally {
15996
+ blow = outer;
15997
+ }
15998
+ };
15999
+ const doHit = (o, hit, only) => withBlow(hit.type, () => resolveHit(o, hit, only))();
16000
+ const resolveHit = (o, hit, only) => {
15965
16001
  let point = hit.point;
15966
16002
  let dir = hit.direction;
15967
16003
  if (hit.space === "local") {
@@ -16400,6 +16436,44 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
16400
16436
  blastProbe() {
16401
16437
  return { ...blastProbe };
16402
16438
  },
16439
+ blastReach(point, target, options2 = {}) {
16440
+ const L = vec.distance(point, target);
16441
+ if (L < 1e-4) return 1;
16442
+ const shelter = { ...SHELTER_DEFAULTS, ...options2.shelter };
16443
+ const ignore = new Set(options2.ignore ?? []);
16444
+ const d = vec.scale(vec.sub(target, point), 1 / L);
16445
+ let t = 1;
16446
+ for (const o of objects.values()) {
16447
+ if (ignore.has(o.id) || raySphereMiss(point, d, toWorld(o.st.position, o.st.rotation, o.bc), o.br, L)) continue;
16448
+ for (const p of o.parts) {
16449
+ if (isLost(p) || p.st.stripped) continue;
16450
+ const r = rayPart(o, p, point, d);
16451
+ if (!r) continue;
16452
+ const t0 = Math.max(r.t0, 0);
16453
+ const path = Math.min(r.t1, L) - t0;
16454
+ if (path < shelter.minPath) continue;
16455
+ if (p.st.opening && insideOpening(o, p, toLocal(o.st.position, o.st.rotation, vec.madd(point, d, t0 + path / 2)))) continue;
16456
+ t *= Math.exp(-(p.mat.hpPerKg * p.mat.density * path * (p.mat.damageScale?.shelter ?? 1)) / shelter.k);
16457
+ if (t < 1e-4) return 0;
16458
+ }
16459
+ }
16460
+ for (const im of impls.values()) {
16461
+ if (ignore.has(im.impl.id)) continue;
16462
+ for (const ps of im.impl.describe().parts) {
16463
+ const mat = materials.get(ps.material);
16464
+ if (!mat) continue;
16465
+ const ov = im.poses.get(ps.id);
16466
+ const r = rayPartRaw(im.position, im.rotation, ps, ov?.q ?? quatFromEuler(...ps.rotation ?? [0, 0, 0]), point, d, ov?.pos);
16467
+ if (!r || r.t1 < 0 || r.t0 > L) continue;
16468
+ let path = Math.min(r.t1, L) - Math.max(r.t0, 0);
16469
+ if (ps.thickness !== void 0 && ps.thickness > 0) path = Math.min(path, 2 * ps.thickness);
16470
+ else if (path < shelter.minPath) continue;
16471
+ t *= Math.exp(-(mat.hpPerKg * mat.density * path * (mat.damageScale?.shelter ?? 1)) / shelter.k);
16472
+ if (t < 1e-4) return 0;
16473
+ }
16474
+ }
16475
+ return t;
16476
+ },
16403
16477
  explode(point, energy, radius, source, blast) {
16404
16478
  blastProbe.partsTested = 0;
16405
16479
  blastProbe.partsNear = 0;
@@ -16424,7 +16498,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
16424
16498
  },
16425
16499
  shoot(origin, direction, energy, caliber, source, round3) {
16426
16500
  const extras = typeof round3 === "number" ? { piercing: round3 } : round3 ?? {};
16427
- return withCollapses(run(() => {
16501
+ return withCollapses(run(withBlow("ballistic", () => {
16428
16502
  const results = [];
16429
16503
  const d = vec.normalize(direction);
16430
16504
  const ignore = /* @__PURE__ */ new Set();
@@ -16435,10 +16509,12 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
16435
16509
  if (!rh) break;
16436
16510
  const im = impls.get(rh.object);
16437
16511
  if (im) {
16438
- const r = implHit(im, { type: "ballistic", point: rh.point, part: rh.part, normal: rh.normal, direction: d, energy: E, caliber, ...extras, ...source !== void 0 ? { source } : {} });
16512
+ const scaled = extras.penetration && E < energy ? { ...extras, penetration: Object.fromEntries(Object.entries(extras.penetration).map(([k, mm]) => [k, mm * (E / energy)])) } : extras;
16513
+ const r = implHit(im, { type: "ballistic", point: rh.point, part: rh.part, normal: rh.normal, direction: d, energy: E, caliber, ...scaled, ...source !== void 0 ? { source } : {} });
16439
16514
  r.part = rh.part;
16440
16515
  r.point = rh.point;
16441
- r.exitEnergy = r.penetrated ? E * 0.5 : 0;
16516
+ const left = r.events.find((e) => e.type === "hole" && e.energyLeft !== void 0);
16517
+ r.exitEnergy = r.penetrated ? left?.energyLeft ?? E * 0.5 : 0;
16442
16518
  results.push(r);
16443
16519
  ignore.add(`${im.impl.id}\0${rh.part}`);
16444
16520
  if (!r.penetrated) break;
@@ -16457,7 +16533,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
16457
16533
  E = out.exitEnergy;
16458
16534
  }
16459
16535
  return results;
16460
- }));
16536
+ })));
16461
16537
  },
16462
16538
  disassemble(objectId, jointId, tool, dt) {
16463
16539
  const im = impls.get(objectId);
@@ -16765,8 +16841,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
16765
16841
  list: () => defs,
16766
16842
  get,
16767
16843
  register(def) {
16768
- const at = defs.findIndex((a) => a.id === def.id);
16769
- if (at >= 0) defs[at] = def;
16844
+ const at2 = defs.findIndex((a) => a.id === def.id);
16845
+ if (at2 >= 0) defs[at2] = def;
16770
16846
  else defs.push(def);
16771
16847
  },
16772
16848
  part: (assemblyId, options) => buildAssemblyPart(get(assemblyId), assemblyId, options)
@@ -17837,9 +17913,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
17837
17913
  }
17838
17914
  if (context.isTimber && construction.roof.shape !== "slab") addCeilingJoists(context);
17839
17915
  }
17916
+ var isStair = (item) => item.category === "stairs" && typeof item.link === "object" && item.link !== null;
17840
17917
  function flightsOf(items) {
17841
17918
  const flights = /* @__PURE__ */ new Map();
17842
- for (const item of items.filter((it) => it.category === "stairs" && it.link)) {
17919
+ for (const item of items.filter(isStair)) {
17843
17920
  const key = `${item.link.from}_${item.link.to}`;
17844
17921
  if (!flights.has(key) || item.floor === item.link.from) flights.set(key, item);
17845
17922
  }
@@ -20219,17 +20296,17 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
20219
20296
  for (const { c, part, units: units2, thick, mid } of shown) {
20220
20297
  if (!near(c)) continue;
20221
20298
  const centre = mask.start + (c.a + c.b) / 2, y = (c.y0 + c.y1) / 2, len = c.b - c.a, h = c.y1 - c.y0;
20222
- const at = (s, dy, d, colour) => {
20299
+ const at2 = (s, dy, d, colour) => {
20223
20300
  const unit = clipToBox({ x: mask.alongU ? centre : mid, y, z: mask.alongU ? mid : centre, sx: mask.alongU ? s : d, sy: dy, sz: mask.alongU ? d : s, colour }, view.bounds);
20224
20301
  if (unit) list2.push(unit);
20225
20302
  };
20226
20303
  const base = fragmentMaterial(part.material).color.getHex();
20227
20304
  const n = noise2(centre * 3.1, y * 2.7, 0.07);
20228
20305
  if (units2.mortar === "concrete") {
20229
- at(len, h, thick - 2 * COVER2, tone(base, 0.9 + 0.04 * n));
20306
+ at2(len, h, thick - 2 * COVER2, tone(base, 0.9 + 0.04 * n));
20230
20307
  } else {
20231
- at(len, h, thick - 2 * COVER2 - 0.012, 10327948);
20232
- at(len - 0.012, h - 0.012, thick - 2 * COVER2, tone(base, 0.8 + 0.35 * n));
20308
+ at2(len, h, thick - 2 * COVER2 - 0.012, 10327948);
20309
+ at2(len - 0.012, h - 0.012, thick - 2 * COVER2, tone(base, 0.8 + 0.35 * n));
20233
20310
  }
20234
20311
  }
20235
20312
  if (view.units) {
@@ -20545,11 +20622,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
20545
20622
  };
20546
20623
  const extract = (it) => {
20547
20624
  const g0 = it.mesh.geometry, src = it.mesh.userData._p0 || g0.attributes.position.array, g = new THREE.BufferGeometry();
20548
- const at = (name, k, arr) => g.setAttribute(name, new THREE.BufferAttribute((arr || g0.attributes[name].array).slice(it.v0 * k, it.v1 * k), k));
20549
- at("position", 3, src);
20550
- at("normal", 3);
20551
- at("color", 3);
20552
- at("uv", 2);
20625
+ const at2 = (name, k, arr) => g.setAttribute(name, new THREE.BufferAttribute((arr || g0.attributes[name].array).slice(it.v0 * k, it.v1 * k), k));
20626
+ at2("position", 3, src);
20627
+ at2("normal", 3);
20628
+ at2("color", 3);
20629
+ at2("uv", 2);
20553
20630
  const ix = g0.index.array.slice(it.i0, it.i1);
20554
20631
  for (let k = 0; k < ix.length; k++) ix[k] -= it.v0;
20555
20632
  g.setIndex(new THREE.BufferAttribute(ix, 1));
@@ -21264,9 +21341,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
21264
21341
  shared.vegMaterials.setActors(focus ? [{ x: focus.x, y: focus.y, z: focus.z, radius: 0.9 }] : []);
21265
21342
  }
21266
21343
  if (focus && cfg.lod !== false) {
21267
- const L = Object.assign({ interior: 45, hide: 220 }, cfg.lod), d = distance2(focus), at = h.lodCurrent;
21344
+ const L = Object.assign({ interior: 45, hide: 220 }, cfg.lod), d = distance2(focus), at2 = h.lodCurrent;
21268
21345
  let n = d > L.hide ? 2 : d > L.interior ? 1 : 0;
21269
- if (n < at) n = d < (at === 2 ? L.hide : L.interior) * 0.92 ? n : at;
21346
+ if (n < at2) n = d < (at2 === 2 ? L.hide : L.interior) * 0.92 ? n : at2;
21270
21347
  applyLod(Math.max(n, h.lodMin));
21271
21348
  }
21272
21349
  },
@@ -22017,14 +22094,14 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
22017
22094
 
22018
22095
  // src/test-area/scenarios.ts
22019
22096
  var SPECIMEN_AT = [-5, 0, -11];
22020
- function createHouseScenarios(text, at = SPECIMEN_AT) {
22097
+ function createHouseScenarios(text, at2 = SPECIMEN_AT) {
22021
22098
  const T2 = text.steps;
22022
22099
  const spawn = (room, item) => {
22023
- const so = room.spawn(item, [at[0], at[1], at[2]], 0);
22100
+ const so = room.spawn(item, [at2[0], at2[1], at2[2]], 0);
22024
22101
  const parts = so.root.userData.floorplanSpec.parts;
22025
22102
  const centre = (id) => {
22026
22103
  const p = parts.find((x) => x.id === id);
22027
- return [p.position[0] + at[0], p.position[1] + at[1], p.position[2] + at[2]];
22104
+ return [p.position[0] + at2[0], p.position[1] + at2[1], p.position[2] + at2[2]];
22028
22105
  };
22029
22106
  return { oid: so.objectId, parts, centre };
22030
22107
  };
@@ -22399,9 +22476,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
22399
22476
  };
22400
22477
  const eyeOf = (p) => {
22401
22478
  const camera = host?.camera;
22402
- const at = toLocal2(p, camera.position.x, camera.position.y, camera.position.z);
22479
+ const at2 = toLocal2(p, camera.position.x, camera.position.y, camera.position.z);
22403
22480
  const forward = new THREE.Vector3(0, 0, -1).applyQuaternion(camera.quaternion).transformDirection(new THREE.Matrix4().copy(p.house.group.matrixWorld).invert());
22404
- return { x: at.x, y: at.y, z: at.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))) };
22481
+ 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))) };
22405
22482
  };
22406
22483
  const blocked = (x, z, radius) => {
22407
22484
  for (const p of placed) {
@@ -22469,7 +22546,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
22469
22546
  const doorReach = (p, fo) => {
22470
22547
  const g = fo.gs[0];
22471
22548
  const pivot = fo.handles[0].pivot;
22472
- const at = (angle) => {
22549
+ const at2 = (angle) => {
22473
22550
  const c = Math.cos(angle);
22474
22551
  const s = Math.sin(angle);
22475
22552
  const v = new THREE.Vector3(pivot[0] * c + pivot[2] * s, pivot[1], -pivot[0] * s + pivot[2] * c).add(g.position);
@@ -22478,8 +22555,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
22478
22555
  const camera = host?.camera;
22479
22556
  const eye = new THREE.Vector3(camera.position.x, 0, camera.position.z);
22480
22557
  const flat = (v) => Math.hypot(v.x - eye.x, v.z - eye.z);
22481
- const closed = at(0);
22482
- const open = at(fo.rot);
22558
+ const closed = at2(0);
22559
+ const open = at2(fo.rot);
22483
22560
  const opening = !fo.open;
22484
22561
  return {
22485
22562
  handle: () => {
@@ -23187,8 +23264,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
23187
23264
  facadeExtra: (f, rng2, w, d, op) => {
23188
23265
  f.roof = "slab";
23189
23266
  f.finish = rng2() < 0.55 ? "exposed_block" : "partial_plaster";
23190
- f.slab = { exposedRebar: true, waterTank: { u: r22(0.5 + rng2() * (w - 1.5)), v: r22(1 + rng2() * (d - 2)) }, annex: rng2() < 0.55 ? { rect: [0, 0, r22(w * (0.5 + rng2() * 0.3)), r22(d * 0.4)], state: "exposed_brick" } : null };
23191
- if (floorsOf(op) > 1) f.slab.exposedRebar = rng2() < 0.6;
23267
+ const slab = { exposedRebar: true, waterTank: { u: r22(0.5 + rng2() * (w - 1.5)), v: r22(1 + rng2() * (d - 2)) }, annex: rng2() < 0.55 ? { rect: [0, 0, r22(w * (0.5 + rng2() * 0.3)), r22(d * 0.4)], state: "exposed_brick" } : null };
23268
+ f.slab = slab;
23269
+ if (floorsOf(op) > 1) slab.exposedRebar = rng2() < 0.6;
23192
23270
  }
23193
23271
  };
23194
23272
  }
@@ -23900,13 +23978,13 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
23900
23978
  function apartment(strip, floor, k, from, to, plate, ceiling, win) {
23901
23979
  const wide = to - from, depth = strip.depth, facade = r22(depth * 0.56);
23902
23980
  const id = (name) => `apt_${floor}_${strip.id}_${k}_${name}`;
23903
- const at = (f0, f1, b0, b1) => boxOf2(strip, r22(from + wide * f0), r22(from + wide * f1), b0, b1);
23981
+ const at2 = (f0, f1, b0, b1) => boxOf2(strip, r22(from + wide * f0), r22(from + wide * f1), b0, b1);
23904
23982
  const rooms = [
23905
- { id: id("living"), type: "living", label: "living", library: "living_tower", rect: at(0, 0.58, 0, facade) },
23906
- { id: id("bedroom"), type: "bedroom", label: "bedroom", rect: at(0.58, 1, 0, facade) },
23907
- { id: id("kitchen"), type: "kitchen", label: "kitchen", library: "kitchen_integrated", rect: at(0, 0.4, facade, depth) },
23908
- { id: id("hall"), type: "hall", label: "hall", rect: at(0.4, 0.78, facade, depth) },
23909
- { id: id("bath"), type: "bathroom", label: "bathroom", library: "bathroom_wc", rect: at(0.78, 1, facade, depth) }
23983
+ { id: id("living"), type: "living", label: "living", library: "living_tower", rect: at2(0, 0.58, 0, facade) },
23984
+ { id: id("bedroom"), type: "bedroom", label: "bedroom", rect: at2(0.58, 1, 0, facade) },
23985
+ { id: id("kitchen"), type: "kitchen", label: "kitchen", library: "kitchen_integrated", rect: at2(0, 0.4, facade, depth) },
23986
+ { id: id("hall"), type: "hall", label: "hall", rect: at2(0.4, 0.78, facade, depth) },
23987
+ { id: id("bath"), type: "bathroom", label: "bathroom", library: "bathroom_wc", rect: at2(0.78, 1, facade, depth) }
23910
23988
  ];
23911
23989
  const doors = [
23912
23990
  stripDoor(id("hall"), strip, floor, 0.95),
@@ -23939,8 +24017,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
23939
24017
  if (strip.id !== "south") return apartments(plan)(strip, context);
23940
24018
  const { floor, plate, ceiling } = context;
23941
24019
  const lobby = `lobby_${floor}_entrance`, concierge = `concierge_${floor}`, bikes = `bike_room_${floor}`;
23942
- const at = (f0, f1) => boxOf2(strip, r22(strip.length * f0), r22(strip.length * f1), 0, strip.depth);
23943
- const [left, middle, right] = [at(0, 0.3), at(0.3, 0.7), at(0.7, 1)];
24020
+ const at2 = (f0, f1) => boxOf2(strip, r22(strip.length * f0), r22(strip.length * f1), 0, strip.depth);
24021
+ const [left, middle, right] = [at2(0, 0.3), at2(0.3, 0.7), at2(0.7, 1)];
23944
24022
  const rooms = [
23945
24023
  { id: concierge, type: "office", label: "concierge", rect: left },
23946
24024
  { id: lobby, type: "hall", label: "lobby_entrance", rect: middle },