@adatechnology/game-items-three 0.7.0 → 0.7.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.
@@ -89,7 +89,7 @@ var GameItemsThree = (() => {
89
89
  wearableMaterials: () => wearableMaterials
90
90
  });
91
91
 
92
- // ../game-shapes/dist/chunk-IFXQIDSA.js
92
+ // ../game-shapes/dist/chunk-M5N2SHNL.js
93
93
  function canonicalJson(value) {
94
94
  if (value === null) return "null";
95
95
  if (typeof value === "number") return Object.is(value, -0) ? "-0" : Number.isFinite(value) ? String(value) : JSON.stringify(String(value));
@@ -1246,13 +1246,13 @@ var GameItemsThree = (() => {
1246
1246
  uvArray.push(x);
1247
1247
  uvArray.push(y);
1248
1248
  };
1249
- const at = (index, axis) => verticesArray[index * 3 + axis];
1249
+ const at2 = (index, axis) => verticesArray[index * 3 + axis];
1250
1250
  const f3 = (a, b, c) => {
1251
1251
  addVertex(a);
1252
1252
  addVertex(b);
1253
1253
  addVertex(c);
1254
1254
  const nextIndex = verticesArray.length / 3;
1255
- for (const index of [nextIndex - 3, nextIndex - 2, nextIndex - 1]) addUV(at(index, 0), at(index, 1));
1255
+ for (const index of [nextIndex - 3, nextIndex - 2, nextIndex - 1]) addUV(at2(index, 0), at2(index, 1));
1256
1256
  };
1257
1257
  const f4 = (a, b, c, d) => {
1258
1258
  addVertex(a);
@@ -1266,8 +1266,8 @@ var GameItemsThree = (() => {
1266
1266
  const iB = nextIndex - 3;
1267
1267
  const iC = nextIndex - 2;
1268
1268
  const iD = nextIndex - 1;
1269
- const byX = Math.abs(at(iA, 1) - at(iB, 1)) < Math.abs(at(iA, 0) - at(iB, 0));
1270
- const uv = (index) => [byX ? at(index, 0) : at(index, 1), 1 - at(index, 2)];
1269
+ const byX = Math.abs(at2(iA, 1) - at2(iB, 1)) < Math.abs(at2(iA, 0) - at2(iB, 0));
1270
+ const uv = (index) => [byX ? at2(index, 0) : at2(index, 1), 1 - at2(index, 2)];
1271
1271
  const uvs = [uv(iA), uv(iB), uv(iC), uv(iD)];
1272
1272
  for (const k of [0, 1, 3, 1, 2, 3]) addUV(...uvs[k]);
1273
1273
  };
@@ -1372,8 +1372,8 @@ var GameItemsThree = (() => {
1372
1372
  groups: []
1373
1373
  };
1374
1374
  }
1375
- function make(kind, p) {
1376
- switch (kind) {
1375
+ function make(p) {
1376
+ switch (p.kind) {
1377
1377
  case "box": {
1378
1378
  const size = p.size ?? [1, 1, 1];
1379
1379
  const segments = p.segments ?? [1, 1, 1];
@@ -1402,7 +1402,7 @@ var GameItemsThree = (() => {
1402
1402
  case "mesh":
1403
1403
  return mesh(p);
1404
1404
  default:
1405
- throw new Error(`game-shapes: unknown primitive kind "${String(kind)}"`);
1405
+ throw new Error(`game-shapes: unknown primitive kind "${String(p.kind)}"`);
1406
1406
  }
1407
1407
  }
1408
1408
  var templates = /* @__PURE__ */ new Map();
@@ -1410,7 +1410,7 @@ var GameItemsThree = (() => {
1410
1410
  var stats = { hits: 0, misses: 0 };
1411
1411
  function buildPrimitive(node, options = {}) {
1412
1412
  const parameters = primitiveParameters(node, options);
1413
- if (node.kind === "mesh" || templateLimit === 0) return make(node.kind, parameters);
1413
+ if (node.kind === "mesh" || templateLimit === 0) return make({ ...node, ...parameters });
1414
1414
  const key2 = `${node.kind}:${canonicalJson(parameters)}`;
1415
1415
  const cached = templates.get(key2);
1416
1416
  if (cached) {
@@ -1420,7 +1420,7 @@ var GameItemsThree = (() => {
1420
1420
  return cached;
1421
1421
  }
1422
1422
  stats.misses++;
1423
- const built = make(node.kind, parameters);
1423
+ const built = make({ ...node, ...parameters });
1424
1424
  templates.set(key2, built);
1425
1425
  if (templates.size > templateLimit) templates.delete(templates.keys().next().value);
1426
1426
  return built;
@@ -1518,7 +1518,7 @@ var GameItemsThree = (() => {
1518
1518
  const positions2 = new Float32Array(total * 3);
1519
1519
  const normals = new Float32Array(total * 3);
1520
1520
  const uvs = new Float32Array(total * 2);
1521
- let at = 0;
1521
+ let at2 = 0;
1522
1522
  for (const piece2 of pieces) {
1523
1523
  const e = piece2.matrix;
1524
1524
  const nm = normalMatrix(e);
@@ -1532,15 +1532,15 @@ var GameItemsThree = (() => {
1532
1532
  if (piece2.normals) n[k * 3 + c] = piece2.normals[i * 3 + c];
1533
1533
  }
1534
1534
  if (piece2.uvs) {
1535
- uvs[(at + k) * 2] = piece2.uvs[i * 2];
1536
- uvs[(at + k) * 2 + 1] = piece2.uvs[i * 2 + 1];
1535
+ uvs[(at2 + k) * 2] = piece2.uvs[i * 2];
1536
+ uvs[(at2 + k) * 2 + 1] = piece2.uvs[i * 2 + 1];
1537
1537
  }
1538
1538
  }
1539
1539
  for (let k = 0; k < count; k++) {
1540
1540
  const q = transformPoint(e, p[k * 3], p[k * 3 + 1], p[k * 3 + 2]);
1541
- positions2[(at + k) * 3] = q[0];
1542
- positions2[(at + k) * 3 + 1] = q[1];
1543
- positions2[(at + k) * 3 + 2] = q[2];
1541
+ positions2[(at2 + k) * 3] = q[0];
1542
+ positions2[(at2 + k) * 3 + 1] = q[1];
1543
+ positions2[(at2 + k) * 3 + 2] = q[2];
1544
1544
  const x = n[k * 3];
1545
1545
  const y = n[k * 3 + 1];
1546
1546
  const z = n[k * 3 + 2];
@@ -1548,11 +1548,11 @@ var GameItemsThree = (() => {
1548
1548
  const vy = nm[1] * x + nm[4] * y + nm[7] * z;
1549
1549
  const vz = nm[2] * x + nm[5] * y + nm[8] * z;
1550
1550
  const s = 1 / (Math.sqrt(vx * vx + vy * vy + vz * vz) || 1);
1551
- normals[(at + k) * 3] = vx * s;
1552
- normals[(at + k) * 3 + 1] = vy * s;
1553
- normals[(at + k) * 3 + 2] = vz * s;
1551
+ normals[(at2 + k) * 3] = vx * s;
1552
+ normals[(at2 + k) * 3 + 1] = vy * s;
1553
+ normals[(at2 + k) * 3 + 2] = vz * s;
1554
1554
  }
1555
- at += count;
1555
+ at2 += count;
1556
1556
  }
1557
1557
  return { positions: positions2, normals, uvs };
1558
1558
  }
@@ -11385,17 +11385,17 @@ var GameItemsThree = (() => {
11385
11385
  const index = geometry2.index ? geometry2.index.array : null;
11386
11386
  const count = index ? index.length : position.count;
11387
11387
  for (let item = 0; item < count; item++) {
11388
- const at = index ? index[item] : item;
11389
- vertex.fromBufferAttribute(position, at).applyMatrix4(matrix);
11388
+ const at2 = index ? index[item] : item;
11389
+ vertex.fromBufferAttribute(position, at2).applyMatrix4(matrix);
11390
11390
  positions2.push(vertex.x, vertex.y, vertex.z);
11391
- if (normalAttribute) normal.fromBufferAttribute(normalAttribute, at).applyMatrix3(normalMatrix2).normalize();
11391
+ if (normalAttribute) normal.fromBufferAttribute(normalAttribute, at2).applyMatrix3(normalMatrix2).normalize();
11392
11392
  else normal.set(0, 1, 0);
11393
11393
  normals.push(normal.x, normal.y, normal.z);
11394
11394
  color2.copy(material2.color || color2.set(1, 1, 1)).convertLinearToSRGB();
11395
11395
  if (sample && uv && material2.map) {
11396
11396
  const repeat = material2.map.repeat;
11397
- const u = (uv.getX(at) * repeat.x % 1 + 1) % 1;
11398
- const v = (uv.getY(at) * repeat.y % 1 + 1) % 1;
11397
+ const u = (uv.getX(at2) * repeat.x % 1 + 1) % 1;
11398
+ const v = (uv.getY(at2) * repeat.y % 1 + 1) % 1;
11399
11399
  const pixelX = Math.min(sample.width - 1, u * sample.width | 0);
11400
11400
  const pixelY = Math.min(sample.height - 1, (1 - v) * sample.height | 0);
11401
11401
  const offset = (pixelY * sample.width + pixelX) * 4;
@@ -12073,8 +12073,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
12073
12073
  // was `maquina`
12074
12074
  });
12075
12075
  var OWN_TYPES = Object.freeze({ lamp: true, gas_piped: true });
12076
+ var sourceOf = (result) => result;
12076
12077
  var ADAPTER_VERSION = "0.3.0";
12077
12078
  var r3 = (n) => Math.round(n * 1e3) / 1e3;
12079
+ var at = (it) => ({ x: it.x ?? 0, z: it.z ?? 0, ry: it.ry ?? 0 });
12078
12080
  function mergeRegistry(base, extra) {
12079
12081
  const out = { ...base };
12080
12082
  if (extra) {
@@ -12093,14 +12095,15 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
12093
12095
  });
12094
12096
  function create2(options = {}) {
12095
12097
  const opts = options || {};
12096
- const GI = opts.GameInteractables;
12097
- const HH = opts.GameInteractablesHousehold;
12098
12098
  const locale = opts.locale || "pt-BR";
12099
12099
  const msg = createTranslator(locale, opts.messages);
12100
- if (!GI || !HH) throw new Error(msg("error.missingKit"));
12100
+ if (!opts.GameInteractables || !opts.GameInteractablesHousehold) throw new Error(msg("error.missingKit"));
12101
+ const GI = opts.GameInteractables;
12102
+ const HH = opts.GameInteractablesHousehold;
12101
12103
  const region = opts.region || "br";
12102
12104
  const hh = HH.createHousehold({ locale, region });
12103
12105
  const householdTypes = new Set(hh.types.map((t) => t.id));
12106
+ const recorderApi = () => HH.createRecorder().api;
12104
12107
  const worlds = /* @__PURE__ */ new WeakMap();
12105
12108
  const typeMap = mergeRegistry(TYPE_MAP, opts.typeMap);
12106
12109
  const contentMap = mergeRegistry(CONTENT_MAP, opts.contentMap);
@@ -12136,7 +12139,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
12136
12139
  function colorOf(res, color2) {
12137
12140
  if (color2 == null) return void 0;
12138
12141
  if (typeof color2 === "number") return color2;
12139
- const v = res.colors && res.colors[color2];
12142
+ const v = res.colors && res.colors[String(color2)];
12140
12143
  return typeof v === "number" ? v : void 0;
12141
12144
  }
12142
12145
  function paletteOf(res) {
@@ -12165,17 +12168,18 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
12165
12168
  drawerItem: (s) => drawerItems.has(s.itemId)
12166
12169
  };
12167
12170
  const itemOfContent = (c) => {
12168
- const id = contentMap[c.itemId] || (hh.items[c.itemId] ? c.itemId : null) || contentByGroup[stackGroup(c)] || contentFallback;
12171
+ const group = stackGroup(c);
12172
+ const id = contentMap[c.itemId] || (hh.items[c.itemId] ? c.itemId : null) || (group != null ? contentByGroup[group] : void 0) || contentFallback;
12169
12173
  return hh.items[id] ? id : contentFallback;
12170
12174
  };
12171
12175
  const volume = (d) => d.size[0] * d.size[1] * d.size[2];
12172
12176
  const candidates = (id, objectType) => {
12173
12177
  const def = hh.items[id];
12174
- 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));
12178
+ 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));
12175
12179
  return [id].concat(siblings.map((i) => i.id), substitutes[objectType] || []);
12176
12180
  };
12177
12181
  function distribute(type, probeItem, stacks) {
12178
- const spec = hh.build(type, probeItem, HH.createRecorder().api);
12182
+ const spec = hh.build(type, probeItem, recorderApi());
12179
12183
  const conts = spec.containers || [];
12180
12184
  const contents = {};
12181
12185
  for (const c of conts) contents[c.id] = c.slots.map(() => null);
@@ -12206,8 +12210,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
12206
12210
  }
12207
12211
  return { contents, rejected };
12208
12212
  }
12209
- function plan(res, popts = {}) {
12210
- popts = popts || {};
12213
+ function plan(source, options2 = {}) {
12214
+ const res = sourceOf(source);
12215
+ const popts = options2 || {};
12211
12216
  const o = { ox: popts.originX || 0, oz: popts.originZ || 0, inw: popts.inw || 1, base: popts.base || 0 };
12212
12217
  const K = conv(o);
12213
12218
  const floorSel = popts.floor;
@@ -12271,7 +12276,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
12271
12276
  const item = {
12272
12277
  x: K.x(m.x),
12273
12278
  z: K.z(m.z),
12274
- ry: m.frontU != null ? K.ry(m.frontU, m.frontV) : r3(m.ry || 0),
12279
+ ry: m.frontU != null ? K.ry(m.frontU, m.frontV ?? 0) : r3(m.ry || 0),
12275
12280
  w: m.w,
12276
12281
  d: m.d,
12277
12282
  h: m.h,
@@ -12284,7 +12289,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
12284
12289
  return item;
12285
12290
  }
12286
12291
  function entry2(id, type, item, origin, floor, extra) {
12287
- return Object.assign({ id, type, item, origin, floor, floorY: floorYOf(res, o, floor), connections: {} }, extra || {});
12292
+ const e = { id, type, item, origin, floor, floorY: floorYOf(res, o, floor), connections: {} };
12293
+ return Object.assign(e, extra || {});
12288
12294
  }
12289
12295
  function store(targets, stacks) {
12290
12296
  let rest2 = stacks || [];
@@ -12297,10 +12303,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
12297
12303
  }
12298
12304
  const hidden = [];
12299
12305
  function distRect(c, x, z) {
12300
- const dx = x - c.item.x;
12301
- const dz = z - c.item.z;
12302
- const s = Math.sin(c.item.ry);
12303
- const co = Math.cos(c.item.ry);
12306
+ const ci = at(c.item);
12307
+ const dx = x - ci.x;
12308
+ const dz = z - ci.z;
12309
+ const s = Math.sin(ci.ry);
12310
+ const co = Math.cos(ci.ry);
12304
12311
  const lx = dx * co - dz * s;
12305
12312
  const lz = dx * s + dz * co;
12306
12313
  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));
@@ -12308,9 +12315,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
12308
12315
  function hideCylinders() {
12309
12316
  for (const b of hidden) {
12310
12317
  let target = null;
12318
+ const bi = at(b.item);
12311
12319
  for (const c2 of result.items) {
12312
12320
  if (c2.type !== "kitchen_cabinet_base" || c2.floor !== b.floor) continue;
12313
- const dist = distRect(c2, b.item.x, b.item.z);
12321
+ const dist = distRect(c2, bi.x, bi.z);
12314
12322
  if (!target || dist < target.dist) target = { c: c2, dist };
12315
12323
  }
12316
12324
  if (!target) {
@@ -12318,16 +12326,18 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
12318
12326
  continue;
12319
12327
  }
12320
12328
  const c = target.c;
12321
- const spec = hh.build(c.type, c.item, HH.createRecorder().api);
12322
- const sn = Math.sin(c.item.ry);
12323
- const cs = Math.cos(c.item.ry);
12329
+ const spec = hh.build(c.type, c.item, recorderApi());
12330
+ const ci = at(c.item);
12331
+ const sn = Math.sin(ci.ry);
12332
+ const cs = Math.cos(ci.ry);
12324
12333
  let door = null;
12325
12334
  for (const it of spec.interactions || []) {
12326
- const pt = it.action && it.action.do === "toggle_part" ? (spec.parts || []).find((q) => q.id === it.action.part) : null;
12327
- if (!pt || pt.motion.kind !== "hinge") continue;
12328
- const wx = c.item.x + it.pos[0] * cs + it.pos[2] * sn;
12329
- const wz = c.item.z - it.pos[0] * sn + it.pos[2] * cs;
12330
- const dist = Math.hypot(wx - b.item.x, wz - b.item.z);
12335
+ const action = it.action;
12336
+ const pt = action.do === "toggle_part" ? (spec.parts || []).find((q) => q.id === action.part) : null;
12337
+ if (!pt || !pt.motion || pt.motion.kind !== "hinge") continue;
12338
+ const wx = ci.x + it.pos[0] * cs + it.pos[2] * sn;
12339
+ const wz = ci.z - it.pos[0] * sn + it.pos[2] * cs;
12340
+ const dist = Math.hypot(wx - bi.x, wz - bi.z);
12331
12341
  if (!door || dist < door.dist) door = { id: pt.id, dist };
12332
12342
  }
12333
12343
  if (!door) {
@@ -12357,9 +12367,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
12357
12367
  const campCylinder = firstOfType("gas_canister");
12358
12368
  function planFireplace(m, e, item) {
12359
12369
  const def = hh.types.find((t) => t.id === "fireplace");
12360
- const lack = (def.d - m.d) / 2;
12361
- item.x = r3(item.x + Math.sin(item.ry) * lack);
12362
- item.z = r3(item.z + Math.cos(item.ry) * lack);
12370
+ const lack = ((def ? def.d : m.d) - m.d) / 2;
12371
+ const p = at(item);
12372
+ item.x = r3(p.x + Math.sin(p.ry) * lack);
12373
+ item.z = r3(p.z + Math.cos(p.ry) * lack);
12363
12374
  const room = (res.rooms || []).find((c) => c.id === m.room && c.floor === m.floor);
12364
12375
  const f = (res.floors || []).find((x) => x.index === m.floor);
12365
12376
  item.h = r3(room && room.ceiling || f && f.ceiling || 2.6);
@@ -12387,7 +12398,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
12387
12398
  e.connections.gas = m.gas || "cylinder";
12388
12399
  e.connections.cylinder = m.cylinder || null;
12389
12400
  }
12390
- if (map.type === "gas_bottle") result.cylinders.push({ id: e.id, stove: m.link || null, place: m.placement || null });
12401
+ if (map.type === "gas_bottle") result.cylinders.push({ id: e.id, stove: typeof m.link === "string" ? m.link : null, place: m.placement || null });
12391
12402
  if (map.type === "gas_canister") {
12392
12403
  result.canisters.push(e.id);
12393
12404
  if (campCylinder && campCylinder.id === m.id && campStove2) result.cylinders.push({ id: e.id, stove: campStove2.id, place: null });
@@ -12411,7 +12422,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
12411
12422
  push(e);
12412
12423
  }
12413
12424
  function planCompound(m, kind) {
12414
- const ry = m.frontU != null ? K.ry(m.frontU, m.frontV) : r3(m.ry || 0);
12425
+ const ry = m.frontU != null ? K.ry(m.frontU, m.frontV ?? 0) : r3(m.ry || 0);
12415
12426
  const cx = K.x(m.x);
12416
12427
  const cz = K.z(m.z);
12417
12428
  const part = (id, type, w, d, h, y, lx, lz) => {
@@ -12463,11 +12474,12 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
12463
12474
  } else {
12464
12475
  const basin = part(m.id, "sink_bathroom", m.w, m.d, m.h + 0.2, 0, 0, 0);
12465
12476
  list2.push(basin);
12466
- if (contents[m.id]) {
12467
- if (contents[m.id].length) {
12477
+ const own = contents[m.id];
12478
+ if (own) {
12479
+ if (own.length) {
12468
12480
  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);
12469
12481
  list2.push(cabinet);
12470
- store([["bathroom_cabinet", cabinet.item], ["sink_bathroom", basin.item]], contents[m.id]);
12482
+ store([["bathroom_cabinet", cabinet.item], ["sink_bathroom", basin.item]], own);
12471
12483
  } else {
12472
12484
  store([["sink_bathroom", basin.item]], []);
12473
12485
  }
@@ -12507,7 +12519,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
12507
12519
  result.chairs = [];
12508
12520
  if (opts.chairs !== false && popts.chairs !== false) {
12509
12521
  const blocked = blockedOf(res, { originX: o.ox, originZ: o.oz, inw: o.inw });
12510
- const floorKind = (q) => !q.decor && !q.flat && (q.y || 0) < 0.9 && q.aabb;
12522
+ const floorKind = (q) => !q.decor && !q.flat && (q.y || 0) < 0.9 && !!q.aabb;
12511
12523
  const placed = [];
12512
12524
  const free = (m, cu, cv, half) => {
12513
12525
  const a = [cu - half + 0.03, cv - half + 0.03, cu + half - 0.03, cv + half - 0.03];
@@ -12523,8 +12535,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
12523
12535
  for (const m of res.items || []) {
12524
12536
  const mode = chairTables[m.type];
12525
12537
  if (!mode || !onFloor(m.floor, m.outdoor) || m.frontU == null) continue;
12526
- const f = [m.frontU, m.frontV];
12527
- const pp = [-m.frontV, m.frontU];
12538
+ const fv = m.frontV ?? 0;
12539
+ const f = [m.frontU, fv];
12540
+ const pp = [-fv, m.frontU];
12528
12541
  const seats = [];
12529
12542
  if (mode === "dining") {
12530
12543
  const n = Math.max(1, Math.min(3, Math.floor((m.w - 0.1) / 0.55)));
@@ -12593,8 +12606,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
12593
12606
  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) };
12594
12607
  item.controls = (s.controls || []).map((id) => id + ":lamp");
12595
12608
  const e = entry2(s.id, "wall_switch", item, s.id, s.floor);
12596
- e.connections.controls = (s.controls || []).slice();
12597
- e.lit = e.connections.controls.some((id) => (result.items.find((x) => x.id === id) || {}).lit === true);
12609
+ const controls = (s.controls || []).slice();
12610
+ e.connections.controls = controls;
12611
+ e.lit = controls.some((id) => result.items.find((x) => x.id === id)?.lit === true);
12598
12612
  if (floorSel == null) {
12599
12613
  for (const id of s.controls || []) if (!lights.has(id)) result.warnings.push(msg("warn.switchUnknownLamp", { id: s.id, lamp: id }));
12600
12614
  }
@@ -12634,7 +12648,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
12634
12648
  seed: seedOf2("panel"),
12635
12649
  circuits: circuits.map((c) => ({ id: c.id, label: c.room }))
12636
12650
  };
12637
- if (!item.circuits.length) delete item.circuits;
12651
+ if (!item.circuits?.length) delete item.circuits;
12638
12652
  const e = entry2("panel", "electrical_panel", item, "panel", q.floor);
12639
12653
  push(e);
12640
12654
  result.panel = e.id;
@@ -12656,10 +12670,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
12656
12670
  }
12657
12671
  }
12658
12672
  const piped = result.items.filter((e) => e.type === "stove" && e.connections.gas === "piped");
12659
- if (piped.length) {
12673
+ const firstPiped = piped[0];
12674
+ if (firstPiped) {
12660
12675
  const med = utilities.gas && utilities.gas.meter;
12661
- 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 };
12662
- 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);
12676
+ 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 };
12677
+ 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);
12663
12678
  e.floorY = o.base;
12664
12679
  push(e);
12665
12680
  for (const f of piped) f.connections.fuel = "piped_gas:gas";
@@ -12706,9 +12721,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
12706
12721
  for (const dv of spec.devices || []) if (L.outlet && dv.socket === "auto") dv.socket = L.outlet + ":socket";
12707
12722
  } else {
12708
12723
  const sh = L.shower || {};
12709
- const heater = (spec.devices || []).find((d) => d.id === "heater");
12724
+ const devices = spec.devices || [];
12725
+ const heater = devices.find((d) => d.id === "heater");
12710
12726
  if (heater && sh.electric === false) {
12711
- spec.devices = spec.devices.filter((d) => d !== heater);
12727
+ spec.devices = devices.filter((d) => d !== heater);
12712
12728
  for (const f of spec.fixtures || []) if (f.heater === "heater") delete f.heater;
12713
12729
  } else if (heater) {
12714
12730
  if (sh.powerW) heater.watts = sh.powerW;
@@ -12719,7 +12735,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
12719
12735
  for (const b of spec.burners || []) if (b.fuel === "auto") b.fuel = L.fuel;
12720
12736
  }
12721
12737
  if (entry2.type === "fireplace" && L.chimneyTop) {
12722
- const it = entry2.item;
12738
+ const it = at(entry2.item);
12723
12739
  const dx = L.chimneyTop[0] - it.x;
12724
12740
  const dz = L.chimneyTop[2] - it.z;
12725
12741
  const lx = Math.cos(it.ry) * dx - Math.sin(it.ry) * dz;
@@ -12738,8 +12754,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
12738
12754
  }
12739
12755
  return spec;
12740
12756
  }
12741
- function createWorld(res, copts = {}) {
12742
- copts = copts || {};
12757
+ function createWorld(source, options2 = {}) {
12758
+ const res = sourceOf(source);
12759
+ const copts = options2 || {};
12743
12760
  const info = { points: /* @__PURE__ */ new Map(), water: copts.water || null, lightsOn: copts.lightsOn };
12744
12761
  const electrical = res.utilities && res.utilities.electrical;
12745
12762
  const world = GI.createWorld({
@@ -12767,8 +12784,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
12767
12784
  const info = worlds.get(world);
12768
12785
  return info ? info.points.get(fixtureId) : void 0;
12769
12786
  }
12770
- function blockedOf(res, bopts = {}) {
12771
- bopts = bopts || {};
12787
+ function blockedOf(source, options2 = {}) {
12788
+ const res = sourceOf(source);
12789
+ const bopts = options2 || {};
12772
12790
  const ox = bopts.originX || 0;
12773
12791
  const oz = bopts.originZ || 0;
12774
12792
  const inw = bopts.inw || 1;
@@ -12803,33 +12821,33 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
12803
12821
  return false;
12804
12822
  };
12805
12823
  }
12806
- function add(world, planned, aopts = {}) {
12807
- aopts = aopts || {};
12824
+ function add(world, planned, options2 = {}) {
12825
+ const aopts = options2 || {};
12808
12826
  const info = worlds.get(world);
12809
- const build = aopts.build || ((type, item) => hh.build(type, item, HH.createRecorder().api));
12827
+ const build = aopts.build || ((type, item) => hh.build(type, item, recorderApi()));
12810
12828
  const isBlocked = aopts.isBlocked || null;
12811
12829
  const out = { added: 0, parts: [], hinges: {}, moved: {}, warnings: [] };
12812
12830
  const override = aopts.lightsOn !== void 0 ? aopts.lightsOn : info && info.lightsOn;
12813
12831
  if (override !== void 0) {
12814
12832
  for (const e of planned.items) if (e.type === "lamp") e.lit = typeof override === "function" ? !!override(e.id, e) : !!override;
12815
12833
  for (const e of planned.items) if (e.type === "lantern_lamp") e.lit = typeof override === "function" ? !!override(e.id, e) : !!override;
12816
- 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);
12834
+ 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);
12817
12835
  }
12818
12836
  for (const e of planned.items) if (e.type === "lantern_lamp") e.item.on = !!e.lit;
12819
12837
  let scratch = null;
12820
- function probe(e, hinge, setback) {
12838
+ function probe(e, hinge, setback, blockedAt) {
12821
12839
  if (!scratch) scratch = GI.createWorld({ locale, strings: hh.strings, items: hh.items, loot: hh.loot, grid: false });
12822
- const it = e.item;
12840
+ const it = at(e.item);
12823
12841
  const x = it.x + Math.sin(it.ry) * setback;
12824
12842
  const z = it.z + Math.cos(it.ry) * setback;
12825
- const spec = hh.build(e.type, Object.assign({}, it, { hinge }), HH.createRecorder().api);
12843
+ const spec = hh.build(e.type, Object.assign({}, e.item, { hinge }), recorderApi());
12826
12844
  scratch.add("__probe", spec, { x, y: e.floorY, z, ry: it.ry });
12827
- const blocks = (px, pz) => isBlocked(px, pz, e.floor);
12845
+ const blocks = (px, pz) => blockedAt(px, pz, e.floor);
12828
12846
  let sum = 0;
12829
12847
  let min = 1;
12830
12848
  let n = 0;
12831
12849
  for (const p of spec.parts || []) {
12832
- if (p.motion.kind !== "hinge" || !(p.bounds || p.sweep)) continue;
12850
+ if (!p.motion || p.motion.kind !== "hinge" || !(p.bounds || p.sweep)) continue;
12833
12851
  const t = scratch.fitSweep("__probe", p.id, blocks);
12834
12852
  sum += t;
12835
12853
  min = Math.min(min, t);
@@ -12841,12 +12859,12 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
12841
12859
  const better = (a, b) => a.sum > b.sum + 1e-9 || Math.abs(a.sum - b.sum) <= 1e-9 && a.min > b.min + 1e-9;
12842
12860
  function fitDoors(e) {
12843
12861
  if (!isBlocked || !householdTypes.has(e.type)) return;
12844
- const standard = probe(e, void 0, 0);
12862
+ const standard = probe(e, void 0, 0, isBlocked);
12845
12863
  if (!standard.n || standard.min >= 1) return;
12846
12864
  let bestSide;
12847
12865
  let m = standard;
12848
12866
  for (const side of ["left", "right"]) {
12849
- const r = probe(e, side, 0);
12867
+ const r = probe(e, side, 0, isBlocked);
12850
12868
  if (better(r, m)) {
12851
12869
  m = r;
12852
12870
  bestSide = side;
@@ -12859,13 +12877,14 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
12859
12877
  if (m.min >= 1) return;
12860
12878
  let target = { setback: 0, r: m };
12861
12879
  for (const setback of [0.01, 0.02, 0.03, 0.04]) {
12862
- const r = probe(e, bestSide, setback);
12880
+ const r = probe(e, bestSide, setback, isBlocked);
12863
12881
  if (better(r, target.r)) target = { setback, r };
12864
12882
  if (r.min >= 1) break;
12865
12883
  }
12866
12884
  if (target.setback > 0 && target.r.sum > m.sum + 0.2) {
12867
- e.item.x = r3(e.item.x + Math.sin(e.item.ry) * target.setback);
12868
- e.item.z = r3(e.item.z + Math.cos(e.item.ry) * target.setback);
12885
+ const p = at(e.item);
12886
+ e.item.x = r3(p.x + Math.sin(p.ry) * target.setback);
12887
+ e.item.z = r3(p.z + Math.cos(p.ry) * target.setback);
12869
12888
  e.moved = target.setback;
12870
12889
  out.moved[e.id] = target.setback;
12871
12890
  }
@@ -12888,7 +12907,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
12888
12907
  }
12889
12908
  }
12890
12909
  connect(e, spec);
12891
- world.add(e.id, spec, { x: e.item.x, y: e.floorY, z: e.item.z, ry: e.item.ry });
12910
+ const pose = at(e.item);
12911
+ world.add(e.id, spec, { x: pose.x, y: e.floorY, z: pose.z, ry: pose.ry });
12892
12912
  out.added++;
12893
12913
  if (!isBlocked) continue;
12894
12914
  for (const p of spec.parts || []) {
@@ -13251,11 +13271,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
13251
13271
  }
13252
13272
  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] }));
13253
13273
  };
13254
- var severAt = (inp, root, at, gap) => {
13274
+ var severAt = (inp, root, at2, gap) => {
13255
13275
  const dims = root.s;
13256
13276
  const L = [0, 1, 2].sort((x, y) => dims[y] - dims[x])[0];
13257
13277
  const half = dims[L] / 2;
13258
- const lo = clamp(at - gap / 2, -half, half), hi = clamp(at + gap / 2, -half, half);
13278
+ const lo = clamp(at2 - gap / 2, -half, half), hi = clamp(at2 + gap / 2, -half, half);
13259
13279
  const out = [];
13260
13280
  const stub = (from, to) => {
13261
13281
  if (to - from < 0.02) return;
@@ -15637,7 +15657,13 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
15637
15657
  const typed = listeners.get(e.type);
15638
15658
  if (typed) for (const fn of [...typed]) fn(e);
15639
15659
  };
15640
- const base = (o, source) => source === void 0 ? { object: o.id, time } : { object: o.id, time, source };
15660
+ let blow;
15661
+ const base = (o, source) => ({
15662
+ object: o.id,
15663
+ time,
15664
+ ...source === void 0 ? {} : { source },
15665
+ ...blow === void 0 ? {} : { blow }
15666
+ });
15641
15667
  const pendingCollapse = /* @__PURE__ */ new Map();
15642
15668
  let runDepth = 0;
15643
15669
  const drainBlasts = () => {
@@ -15907,9 +15933,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
15907
15933
  const [lo, hi] = contactBox(p, above);
15908
15934
  return toWorld(p.pos, p.q, [(lo[0] + hi[0]) / 2, (lo[1] + hi[1]) / 2, (lo[2] + hi[2]) / 2]);
15909
15935
  };
15910
- const overSupportAt = (o, p, at, lower) => {
15936
+ const overSupportAt = (o, p, at2, lower) => {
15911
15937
  const g = p.bend;
15912
- const local = toLocal(p.pos, p.q, at);
15938
+ const local = toLocal(p.pos, p.q, at2);
15913
15939
  const depth = g.gravity >= 0 ? g.dims[g.gravity] : Math.min(...g.depthAxes.map((a) => g.dims[a]));
15914
15940
  let best = -1;
15915
15941
  let bestGap = depth;
@@ -16063,10 +16089,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
16063
16089
  const incoming = new Array(n).fill(0);
16064
16090
  const bending = new Array(n).fill(0);
16065
16091
  const through = parts.map(() => []);
16066
- const pass = (k, at, mass) => {
16067
- const over = overSupportAt(o, parts[k], at, lowerOf.get(k) ?? []);
16092
+ const pass = (k, at2, mass) => {
16093
+ const over = overSupportAt(o, parts[k], at2, lowerOf.get(k) ?? []);
16068
16094
  if (over < 0) bending[k] += mass;
16069
- else through[k].push({ at, mass, over });
16095
+ else through[k].push({ at: at2, mass, over });
16070
16096
  };
16071
16097
  for (let h = queue.length - 1; h >= 0; h--) {
16072
16098
  const i = queue[h];
@@ -16133,7 +16159,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
16133
16159
  const terrain = cfg.terrains.get(cfg.terrain);
16134
16160
  if (!terrain) throw new Error(`unknown wind terrain "${cfg.terrain}"`);
16135
16161
  const inputs = [];
16136
- const at = /* @__PURE__ */ new Map();
16162
+ const at2 = /* @__PURE__ */ new Map();
16137
16163
  const keep = [];
16138
16164
  let ground = Infinity, foundationTop = -Infinity;
16139
16165
  o.parts.forEach((p, i) => {
@@ -16142,7 +16168,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
16142
16168
  const role = p.spec.structure?.role;
16143
16169
  const e = p.mat.strength?.elasticGPa;
16144
16170
  const kind = role === "wall" ? "wall" : role === "column" ? "column" : role === "beam" ? "beam" : role === "foundation" ? "foundation" : "other";
16145
- at.set(i, inputs.length);
16171
+ at2.set(i, inputs.length);
16146
16172
  keep.push(i);
16147
16173
  ground = Math.min(ground, box4.min[1]);
16148
16174
  if (kind === "foundation") foundationTop = Math.max(foundationTop, box4.max[1]);
@@ -16153,7 +16179,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
16153
16179
  const beams = [];
16154
16180
  for (const j of o.byPart.get(o.parts[i].spec.id)) {
16155
16181
  if (j.st.open) continue;
16156
- const b = at.get(j.ia === i ? j.ib : j.ia);
16182
+ const b = at2.get(j.ia === i ? j.ib : j.ia);
16157
16183
  if (b !== void 0 && inputs[b].kind === "beam") beams.push(b);
16158
16184
  }
16159
16185
  inputs[k].beams = beams;
@@ -16969,7 +16995,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
16969
16995
  }
16970
16996
  };
16971
16997
  const tornToEdge = (o) => {
16972
- const { p, cell, a, b, at, crater: crater2, reach, gone } = o;
16998
+ const { p, cell, a, b, at: at2, crater: crater2, reach, gone } = o;
16973
16999
  const half = p.dims[a] / 2;
16974
17000
  const lo = cell.center[a] - cell.size[a] / 2, hi = cell.center[a] + cell.size[a] / 2;
16975
17001
  const free = (side, sign2) => {
@@ -16977,8 +17003,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
16977
17003
  const probe = side + sign2 * 1e-3;
16978
17004
  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);
16979
17005
  };
16980
- if (at - lo <= reach && free(lo, -1)) return [lo, Math.max(crater2[1], lo)];
16981
- if (hi - at <= reach && free(hi, 1)) return [Math.min(crater2[0], hi), hi];
17006
+ if (at2 - lo <= reach && free(lo, -1)) return [lo, Math.max(crater2[1], lo)];
17007
+ if (hi - at2 <= reach && free(hi, 1)) return [Math.min(crater2[0], hi), hi];
16982
17008
  return crater2;
16983
17009
  };
16984
17010
  const craterAcross = (energy) => clamp2(0.03 + 12e-4 * Math.sqrt(Math.max(0, energy)), 0.03, 0.14);
@@ -17782,7 +17808,17 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
17782
17808
  res.events = cur.slice(start);
17783
17809
  return res;
17784
17810
  };
17785
- const doHit = (o, hit, only) => {
17811
+ const withBlow = (type, fn) => () => {
17812
+ const outer = blow;
17813
+ blow = type;
17814
+ try {
17815
+ return fn();
17816
+ } finally {
17817
+ blow = outer;
17818
+ }
17819
+ };
17820
+ const doHit = (o, hit, only) => withBlow(hit.type, () => resolveHit(o, hit, only))();
17821
+ const resolveHit = (o, hit, only) => {
17786
17822
  let point = hit.point;
17787
17823
  let dir = hit.direction;
17788
17824
  if (hit.space === "local") {
@@ -18221,6 +18257,44 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
18221
18257
  blastProbe() {
18222
18258
  return { ...blastProbe };
18223
18259
  },
18260
+ blastReach(point, target, options2 = {}) {
18261
+ const L = vec.distance(point, target);
18262
+ if (L < 1e-4) return 1;
18263
+ const shelter = { ...SHELTER_DEFAULTS, ...options2.shelter };
18264
+ const ignore = new Set(options2.ignore ?? []);
18265
+ const d = vec.scale(vec.sub(target, point), 1 / L);
18266
+ let t = 1;
18267
+ for (const o of objects.values()) {
18268
+ if (ignore.has(o.id) || raySphereMiss(point, d, toWorld(o.st.position, o.st.rotation, o.bc), o.br, L)) continue;
18269
+ for (const p of o.parts) {
18270
+ if (isLost(p) || p.st.stripped) continue;
18271
+ const r = rayPart(o, p, point, d);
18272
+ if (!r) continue;
18273
+ const t0 = Math.max(r.t0, 0);
18274
+ const path = Math.min(r.t1, L) - t0;
18275
+ if (path < shelter.minPath) continue;
18276
+ if (p.st.opening && insideOpening(o, p, toLocal(o.st.position, o.st.rotation, vec.madd(point, d, t0 + path / 2)))) continue;
18277
+ t *= Math.exp(-(p.mat.hpPerKg * p.mat.density * path * (p.mat.damageScale?.shelter ?? 1)) / shelter.k);
18278
+ if (t < 1e-4) return 0;
18279
+ }
18280
+ }
18281
+ for (const im of impls.values()) {
18282
+ if (ignore.has(im.impl.id)) continue;
18283
+ for (const ps of im.impl.describe().parts) {
18284
+ const mat = materials.get(ps.material);
18285
+ if (!mat) continue;
18286
+ const ov = im.poses.get(ps.id);
18287
+ const r = rayPartRaw(im.position, im.rotation, ps, ov?.q ?? quatFromEuler(...ps.rotation ?? [0, 0, 0]), point, d, ov?.pos);
18288
+ if (!r || r.t1 < 0 || r.t0 > L) continue;
18289
+ let path = Math.min(r.t1, L) - Math.max(r.t0, 0);
18290
+ if (ps.thickness !== void 0 && ps.thickness > 0) path = Math.min(path, 2 * ps.thickness);
18291
+ else if (path < shelter.minPath) continue;
18292
+ t *= Math.exp(-(mat.hpPerKg * mat.density * path * (mat.damageScale?.shelter ?? 1)) / shelter.k);
18293
+ if (t < 1e-4) return 0;
18294
+ }
18295
+ }
18296
+ return t;
18297
+ },
18224
18298
  explode(point, energy, radius, source, blast) {
18225
18299
  blastProbe.partsTested = 0;
18226
18300
  blastProbe.partsNear = 0;
@@ -18245,7 +18319,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
18245
18319
  },
18246
18320
  shoot(origin, direction, energy, caliber, source, round3) {
18247
18321
  const extras = typeof round3 === "number" ? { piercing: round3 } : round3 ?? {};
18248
- return withCollapses(run(() => {
18322
+ return withCollapses(run(withBlow("ballistic", () => {
18249
18323
  const results = [];
18250
18324
  const d = vec.normalize(direction);
18251
18325
  const ignore = /* @__PURE__ */ new Set();
@@ -18256,10 +18330,12 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
18256
18330
  if (!rh) break;
18257
18331
  const im = impls.get(rh.object);
18258
18332
  if (im) {
18259
- 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 } : {} });
18333
+ const scaled = extras.penetration && E < energy ? { ...extras, penetration: Object.fromEntries(Object.entries(extras.penetration).map(([k, mm]) => [k, mm * (E / energy)])) } : extras;
18334
+ 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 } : {} });
18260
18335
  r.part = rh.part;
18261
18336
  r.point = rh.point;
18262
- r.exitEnergy = r.penetrated ? E * 0.5 : 0;
18337
+ const left = r.events.find((e) => e.type === "hole" && e.energyLeft !== void 0);
18338
+ r.exitEnergy = r.penetrated ? left?.energyLeft ?? E * 0.5 : 0;
18263
18339
  results.push(r);
18264
18340
  ignore.add(`${im.impl.id}\0${rh.part}`);
18265
18341
  if (!r.penetrated) break;
@@ -18278,7 +18354,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
18278
18354
  E = out.exitEnergy;
18279
18355
  }
18280
18356
  return results;
18281
- }));
18357
+ })));
18282
18358
  },
18283
18359
  disassemble(objectId, jointId, tool2, dt) {
18284
18360
  const im = impls.get(objectId);
@@ -18586,8 +18662,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
18586
18662
  list: () => defs,
18587
18663
  get,
18588
18664
  register(def) {
18589
- const at = defs.findIndex((a) => a.id === def.id);
18590
- if (at >= 0) defs[at] = def;
18665
+ const at2 = defs.findIndex((a) => a.id === def.id);
18666
+ if (at2 >= 0) defs[at2] = def;
18591
18667
  else defs.push(def);
18592
18668
  },
18593
18669
  part: (assemblyId, options) => buildAssemblyPart(get(assemblyId), assemblyId, options)
@@ -26450,22 +26526,22 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
26450
26526
  A.stats.external = Ne;
26451
26527
  let shadows = 0;
26452
26528
  for (let i = 0; i < N; i++) {
26453
- 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;
26529
+ 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;
26454
26530
  A.crates[i] = g ? { k: g.k, rects: g.rects, y: g.y, exterior: !!g.exterior, exclusion: g.exclusion || null, d: g.d } : null;
26455
26531
  if (!g) {
26456
26532
  put(i, { intensity: 0 });
26457
- for (const a of [b0, b1, b2, ex]) a.set(EMPTY, at * 4);
26458
- by.set([0, 0], at * 2);
26533
+ for (const a of [b0, b1, b2, ex]) a.set(EMPTY, at2 * 4);
26534
+ by.set([0, 0], at2 * 2);
26459
26535
  continue;
26460
26536
  }
26461
26537
  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 };
26462
26538
  put(i, { position: g.pos, color: g.color, ...lamp });
26463
26539
  const rs = g.rects;
26464
- b0.set(rs[0] || EMPTY, at * 4);
26465
- b1.set(rs[1] || EMPTY, at * 4);
26466
- b2.set(rs[2] || EMPTY, at * 4);
26467
- by.set(g.y, at * 2);
26468
- ex.set(g.exclusion || EMPTY, at * 4);
26540
+ b0.set(rs[0] || EMPTY, at2 * 4);
26541
+ b1.set(rs[1] || EMPTY, at2 * 4);
26542
+ b2.set(rs[2] || EMPTY, at2 * 4);
26543
+ by.set(g.y, at2 * 2);
26544
+ ex.set(g.exclusion || EMPTY, at2 * 4);
26469
26545
  if (i < NS) {
26470
26546
  shadows++;
26471
26547
  const s2 = g.house.group.uuid + "|" + g.k + "|" + g.pos.map((v) => v.toFixed(2)).join(",");
@@ -26540,10 +26616,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
26540
26616
  for (const p of list2 || []) {
26541
26617
  if (isDoor && !(open.has(p.id) ? open.get(p.id) : !!p.open)) continue;
26542
26618
  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);
26543
- const at = (side) => _p.set(alongU ? p.at : p.pos + side, y, alongU ? p.pos + side : p.at).applyMatrix4(M).clone();
26544
- if (at(0).distanceTo(P0) > OPENING_REACH) continue;
26619
+ const at2 = (side) => _p.set(alongU ? p.at : p.pos + side, y, alongU ? p.pos + side : p.at).applyMatrix4(M).clone();
26620
+ if (at2(0).distanceTo(P0) > OPENING_REACH) continue;
26545
26621
  for (const side of [-OPENING_SIDE, OPENING_SIDE]) {
26546
- const w = at(side), hit = boxAt(boxes, w.x, w.y, w.z);
26622
+ const w = at2(side), hit = boxAt(boxes, w.x, w.y, w.z);
26547
26623
  if (hit) into.set(hit.c, hit);
26548
26624
  }
26549
26625
  }
@@ -26551,11 +26627,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
26551
26627
  reach(house.r.doors, true);
26552
26628
  reach(house.r.windows, false);
26553
26629
  };
26554
- const clear = (at) => {
26555
- for (const key2 of ["uRB0", "uRB1", "uRB2", "uREx"]) LZU[key2].value.set(EMPTY, at * 4);
26556
- LZU.uRBY.value.set([0, 0], at * 2);
26630
+ const clear = (at2) => {
26631
+ for (const key2 of ["uRB0", "uRB1", "uRB2", "uREx"]) LZU[key2].value.set(EMPTY, at2 * 4);
26632
+ LZU.uRBY.value.set([0, 0], at2 * 2);
26557
26633
  };
26558
- const write = (at, into) => {
26634
+ const write = (at2, into) => {
26559
26635
  const rs = [];
26560
26636
  let lo = 1e9, hi = -1e9;
26561
26637
  for (const q of into.values()) {
@@ -26567,21 +26643,21 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
26567
26643
  const r23 = rs.pop(), r1 = rs[rs.length - 1];
26568
26644
  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])];
26569
26645
  }
26570
- LZU.uRB0.value.set(rs[0] || EMPTY, at * 4);
26571
- LZU.uRB1.value.set(rs[1] || EMPTY, at * 4);
26572
- LZU.uRB2.value.set(rs[2] || EMPTY, at * 4);
26573
- LZU.uRBY.value.set([lo, hi], at * 2);
26574
- LZU.uREx.value.set(EMPTY, at * 4);
26646
+ LZU.uRB0.value.set(rs[0] || EMPTY, at2 * 4);
26647
+ LZU.uRB1.value.set(rs[1] || EMPTY, at2 * 4);
26648
+ LZU.uRB2.value.set(rs[2] || EMPTY, at2 * 4);
26649
+ LZU.uRBY.value.set([lo, hi], at2 * 2);
26650
+ LZU.uREx.value.set(EMPTY, at2 * 4);
26575
26651
  };
26576
26652
  const A = {
26577
26653
  /** 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. */
26578
26654
  update(houses) {
26579
26655
  const indoors = cfg.effectLightsIndoors !== false, list2 = (Array.isArray(houses) ? houses : [houses]).filter(Boolean), cache2 = /* @__PURE__ */ new Map();
26580
- for (let at = 0; at < size; at++) {
26581
- if (ours.has(at)) continue;
26582
- const light = budget.group.children[at];
26656
+ for (let at2 = 0; at2 < size; at2++) {
26657
+ if (ours.has(at2)) continue;
26658
+ const light = budget.group.children[at2];
26583
26659
  if (!indoors || !light || !light.isPointLight || !(light.intensity > 0) || !light.visible) {
26584
- clear(at);
26660
+ clear(at2);
26585
26661
  continue;
26586
26662
  }
26587
26663
  const into = /* @__PURE__ */ new Map();
@@ -26596,8 +26672,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
26596
26672
  if (here) into.set(here.c, here);
26597
26673
  openingSides(house, M, boxes, into);
26598
26674
  }
26599
- if (into.size) write(at, into);
26600
- else clear(at);
26675
+ if (into.size) write(at2, into);
26676
+ else clear(at2);
26601
26677
  }
26602
26678
  }
26603
26679
  };
@@ -27080,17 +27156,17 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
27080
27156
  for (const { c, part, units: units2, thick, mid } of shown) {
27081
27157
  if (!near(c)) continue;
27082
27158
  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;
27083
- const at = (s, dy, d, colour) => {
27159
+ const at2 = (s, dy, d, colour) => {
27084
27160
  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);
27085
27161
  if (unit) list2.push(unit);
27086
27162
  };
27087
27163
  const base = fragmentMaterial(part.material).color.getHex();
27088
27164
  const n = noise2(centre * 3.1, y * 2.7, 0.07);
27089
27165
  if (units2.mortar === "concrete") {
27090
- at(len, h, thick - 2 * COVER2, tone(base, 0.9 + 0.04 * n));
27166
+ at2(len, h, thick - 2 * COVER2, tone(base, 0.9 + 0.04 * n));
27091
27167
  } else {
27092
- at(len, h, thick - 2 * COVER2 - 0.012, 10327948);
27093
- at(len - 0.012, h - 0.012, thick - 2 * COVER2, tone(base, 0.8 + 0.35 * n));
27168
+ at2(len, h, thick - 2 * COVER2 - 0.012, 10327948);
27169
+ at2(len - 0.012, h - 0.012, thick - 2 * COVER2, tone(base, 0.8 + 0.35 * n));
27094
27170
  }
27095
27171
  }
27096
27172
  if (view.units) {
@@ -27404,11 +27480,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
27404
27480
  };
27405
27481
  const extract = (it) => {
27406
27482
  const g0 = it.mesh.geometry, src = it.mesh.userData._p0 || g0.attributes.position.array, g = new THREE.BufferGeometry();
27407
- const at = (name, k, arr) => g.setAttribute(name, new THREE.BufferAttribute((arr || g0.attributes[name].array).slice(it.v0 * k, it.v1 * k), k));
27408
- at("position", 3, src);
27409
- at("normal", 3);
27410
- at("color", 3);
27411
- at("uv", 2);
27483
+ const at2 = (name, k, arr) => g.setAttribute(name, new THREE.BufferAttribute((arr || g0.attributes[name].array).slice(it.v0 * k, it.v1 * k), k));
27484
+ at2("position", 3, src);
27485
+ at2("normal", 3);
27486
+ at2("color", 3);
27487
+ at2("uv", 2);
27412
27488
  const ix = g0.index.array.slice(it.i0, it.i1);
27413
27489
  for (let k = 0; k < ix.length; k++) ix[k] -= it.v0;
27414
27490
  g.setIndex(new THREE.BufferAttribute(ix, 1));
@@ -28121,9 +28197,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
28121
28197
  shared2.vegMaterials.setActors(focus ? [{ x: focus.x, y: focus.y, z: focus.z, radius: 0.9 }] : []);
28122
28198
  }
28123
28199
  if (focus && cfg.lod !== false) {
28124
- const L = Object.assign({ interior: 45, hide: 220 }, cfg.lod), d = distance2(focus), at = h.lodCurrent;
28200
+ const L = Object.assign({ interior: 45, hide: 220 }, cfg.lod), d = distance2(focus), at2 = h.lodCurrent;
28125
28201
  let n = d > L.hide ? 2 : d > L.interior ? 1 : 0;
28126
- if (n < at) n = d < (at === 2 ? L.hide : L.interior) * 0.92 ? n : at;
28202
+ if (n < at2) n = d < (at2 === 2 ? L.hide : L.interior) * 0.92 ? n : at2;
28127
28203
  applyLod(Math.max(n, h.lodMin));
28128
28204
  }
28129
28205
  },
@@ -28743,8 +28819,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
28743
28819
  facadeExtra: (f, rng3, w, d, op) => {
28744
28820
  f.roof = "slab";
28745
28821
  f.finish = rng3() < 0.55 ? "exposed_block" : "partial_plaster";
28746
- f.slab = { exposedRebar: true, waterTank: { u: r22(0.5 + rng3() * (w - 1.5)), v: r22(1 + rng3() * (d - 2)) }, annex: rng3() < 0.55 ? { rect: [0, 0, r22(w * (0.5 + rng3() * 0.3)), r22(d * 0.4)], state: "exposed_brick" } : null };
28747
- if (floorsOf(op) > 1) f.slab.exposedRebar = rng3() < 0.6;
28822
+ const slab = { exposedRebar: true, waterTank: { u: r22(0.5 + rng3() * (w - 1.5)), v: r22(1 + rng3() * (d - 2)) }, annex: rng3() < 0.55 ? { rect: [0, 0, r22(w * (0.5 + rng3() * 0.3)), r22(d * 0.4)], state: "exposed_brick" } : null };
28823
+ f.slab = slab;
28824
+ if (floorsOf(op) > 1) slab.exposedRebar = rng3() < 0.6;
28748
28825
  }
28749
28826
  };
28750
28827
  }
@@ -29456,13 +29533,13 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
29456
29533
  function apartment(strip, floor, k, from, to, plate, ceiling, win) {
29457
29534
  const wide = to - from, depth = strip.depth, facade = r22(depth * 0.56);
29458
29535
  const id = (name) => `apt_${floor}_${strip.id}_${k}_${name}`;
29459
- const at = (f0, f1, b0, b1) => boxOf2(strip, r22(from + wide * f0), r22(from + wide * f1), b0, b1);
29536
+ const at2 = (f0, f1, b0, b1) => boxOf2(strip, r22(from + wide * f0), r22(from + wide * f1), b0, b1);
29460
29537
  const rooms = [
29461
- { id: id("living"), type: "living", label: "living", library: "living_tower", rect: at(0, 0.58, 0, facade) },
29462
- { id: id("bedroom"), type: "bedroom", label: "bedroom", rect: at(0.58, 1, 0, facade) },
29463
- { id: id("kitchen"), type: "kitchen", label: "kitchen", library: "kitchen_integrated", rect: at(0, 0.4, facade, depth) },
29464
- { id: id("hall"), type: "hall", label: "hall", rect: at(0.4, 0.78, facade, depth) },
29465
- { id: id("bath"), type: "bathroom", label: "bathroom", library: "bathroom_wc", rect: at(0.78, 1, facade, depth) }
29538
+ { id: id("living"), type: "living", label: "living", library: "living_tower", rect: at2(0, 0.58, 0, facade) },
29539
+ { id: id("bedroom"), type: "bedroom", label: "bedroom", rect: at2(0.58, 1, 0, facade) },
29540
+ { id: id("kitchen"), type: "kitchen", label: "kitchen", library: "kitchen_integrated", rect: at2(0, 0.4, facade, depth) },
29541
+ { id: id("hall"), type: "hall", label: "hall", rect: at2(0.4, 0.78, facade, depth) },
29542
+ { id: id("bath"), type: "bathroom", label: "bathroom", library: "bathroom_wc", rect: at2(0.78, 1, facade, depth) }
29466
29543
  ];
29467
29544
  const doors = [
29468
29545
  stripDoor(id("hall"), strip, floor, 0.95),
@@ -29495,8 +29572,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
29495
29572
  if (strip.id !== "south") return apartments(plan)(strip, context);
29496
29573
  const { floor, plate, ceiling } = context;
29497
29574
  const lobby = `lobby_${floor}_entrance`, concierge = `concierge_${floor}`, bikes = `bike_room_${floor}`;
29498
- const at = (f0, f1) => boxOf2(strip, r22(strip.length * f0), r22(strip.length * f1), 0, strip.depth);
29499
- const [left, middle, right] = [at(0, 0.3), at(0.3, 0.7), at(0.7, 1)];
29575
+ const at2 = (f0, f1) => boxOf2(strip, r22(strip.length * f0), r22(strip.length * f1), 0, strip.depth);
29576
+ const [left, middle, right] = [at2(0, 0.3), at2(0.3, 0.7), at2(0.7, 1)];
29500
29577
  const rooms = [
29501
29578
  { id: concierge, type: "office", label: "concierge", rect: left },
29502
29579
  { id: lobby, type: "hall", label: "lobby_entrance", rect: middle },
@@ -30883,10 +30960,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
30883
30960
  run: ({ expect }) => {
30884
30961
  const food = catalog.list().find((item) => item.shelfLife !== void 0 && item.shelfLife > 0);
30885
30962
  if (!food) return expect(false, "the catalog has a food that spoils");
30886
- const at = (storage, days2) => freshness(food, { ageDays: 0, since: 0, storage, storedAt: 0 }, days2, { rules: catalog.rules });
30963
+ const at2 = (storage, days2) => freshness(food, { ageDays: 0, since: 0, storage, storedAt: 0 }, days2, { rules: catalog.rules });
30887
30964
  const days = food.shelfLife * 1.2;
30888
- expect(at("ambient", days) !== "fresh", "at room temperature it is past its best", { food: food.id });
30889
- if ("fridge" in catalog.rules.storage) expect(at("fridge", days) !== at("ambient", days) || at("fridge", days) === "fresh", "in the fridge it keeps better", { food: food.id });
30965
+ expect(at2("ambient", days) !== "fresh", "at room temperature it is past its best", { food: food.id });
30966
+ if ("fridge" in catalog.rules.storage) expect(at2("fridge", days) !== at2("ambient", days) || at2("fridge", days) === "fresh", "in the fridge it keeps better", { food: food.id });
30890
30967
  }
30891
30968
  },
30892
30969
  {