@adatechnology/game-floorplan-three 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6030,7 +6030,7 @@ function installLightsRoomLight(K, shared) {
6030
6030
 
6031
6031
  // src/destruction/pieces.ts
6032
6032
  import { assemblyPart, getAssembly } from "@adatechnology/game-destruction";
6033
- import { DENSITY, toDestructibleSpec } from "@adatechnology/game-floorplan-structure";
6033
+ import { DENSITY, MEMBER_CAGE, toDestructibleSpec } from "@adatechnology/game-floorplan-structure";
6034
6034
 
6035
6035
  // src/destruction/panels.ts
6036
6036
  import { resolveSlabPanels, slabPanelView } from "@adatechnology/game-floorplan-structure";
@@ -6210,7 +6210,11 @@ function installDestructionPieces(K, shared) {
6210
6210
  if (!pc || pc.unit) return sp;
6211
6211
  if (pc.role !== "wall" && pc.role !== "partition") {
6212
6212
  const def = getAssembly(pc.assembly || (pc.material === "concrete" && ["slab", "beam", "column", "stair"].includes(pc.role) ? "reinforced_concrete" : ""));
6213
- return def && def.reinforcement && !sp.reinforcement ? { ...sp, reinforcement: { ...def.reinforcement } } : sp;
6213
+ const out = def && def.reinforcement && !sp.reinforcement ? { ...sp, reinforcement: { ...def.reinforcement } } : sp;
6214
+ if (out.reinforcement && !out.units && pc.material === "concrete" && (pc.role === "beam" || pc.role === "column") && sp.shape.kind === "box") {
6215
+ return { ...out, units: { size: [MEMBER_CAGE.unit, MEMBER_CAGE.unit, Math.min(...sp.shape.size)], bond: "stack", mortar: "concrete" } };
6216
+ }
6217
+ return out;
6214
6218
  }
6215
6219
  if (!pc.assembly) return sp;
6216
6220
  const [u0, v0, u1, v1] = pc.aabb, alongU = u1 - u0 >= v1 - v0;
@@ -6306,29 +6310,44 @@ function installDestructionBreach(K, _shared) {
6306
6310
  }
6307
6311
  return list;
6308
6312
  }
6313
+ const specOf = (id) => opts.spec()?.parts?.find((q) => q.id === id);
6314
+ const same = (a, b) => a.every((x, k) => Math.abs(x - b[k]) < 1e-3);
6315
+ function hostOf(partId) {
6316
+ if (structure.meshesOf(partId).some((it) => it.mesh)) return partId;
6317
+ const part = structure.byId.get(partId);
6318
+ if (!part) return null;
6319
+ for (const q of structure.parts) {
6320
+ if (q.id !== partId && same(q.aabb, part.aabb) && Math.abs(q.y1 - part.y0) < 0.01 && structure.meshesOf(q.id).some((it) => it.mesh)) return q.id;
6321
+ }
6322
+ return null;
6323
+ }
6324
+ const riders = (hostId) => structure.parts.filter((q) => q.id !== hostId && specOf(q.id)?.units && hostOf(q.id) === hostId && !structure.meshesOf(q.id).some((it) => it.mesh)).map((q) => q.id);
6309
6325
  function draw2(partId, change) {
6310
6326
  if (lod !== 0) return false;
6311
- const part = structure.byId.get(partId);
6312
- const spec = opts.spec()?.parts?.find((q) => q.id === partId);
6313
- if (!part || !spec || !spec.units) return false;
6314
- let view = views.get(partId);
6327
+ const hostId = hostOf(partId);
6328
+ if (!hostId || !specOf(partId)?.units) return false;
6329
+ let view = views.get(hostId);
6315
6330
  if (!view) {
6316
- const items = structure.meshesOf(partId).filter((it) => it.mesh);
6331
+ const items = structure.meshesOf(hostId).filter((it) => it.mesh);
6317
6332
  if (!items.length) return false;
6318
- view = { mesh: null, saved: null, items, paint: wallColour(items), units: 0, opening: null, chips: [] };
6333
+ const parts = [hostId, ...riders(hostId)].filter((id) => structure.byId.get(id) && specOf(id)?.units);
6334
+ view = { mesh: null, saved: null, items, paint: wallColour(items), units: 0, opening: null, chips: new Map(parts.map((id) => [id, []])), openings: /* @__PURE__ */ new Map() };
6319
6335
  }
6336
+ if (!view.chips.has(partId)) return false;
6320
6337
  if (change.opening) {
6321
- if (view.opening) return false;
6322
- view.opening = { center: local(change.opening.center), radius: change.opening.radius };
6338
+ if (view.openings.has(partId)) return false;
6339
+ view.openings.set(partId, { center: local(change.opening.center), radius: change.opening.radius });
6340
+ if (partId === hostId) view.opening = view.openings.get(partId);
6323
6341
  }
6324
- if (change.chip) view.chips.push(local(change.chip));
6325
- const list = courses(part, spec.units, view.opening, view.chips, view.paint);
6342
+ if (change.chip) view.chips.get(partId).push(local(change.chip));
6343
+ const list = [];
6344
+ for (const [id, chips] of view.chips) list.push(...courses(structure.byId.get(id), specOf(id).units, view.openings.get(id) ?? null, chips, view.paint));
6326
6345
  if (view.mesh) {
6327
6346
  view.mesh.removeFromParent();
6328
6347
  view.mesh.dispose();
6329
6348
  }
6330
6349
  const mesh = new THREE.InstancedMesh(unitGeometry, unitMaterial, Math.max(1, list.length));
6331
- mesh.name = "breach:" + partId;
6350
+ mesh.name = "breach:" + hostId;
6332
6351
  list.forEach((u, i) => {
6333
6352
  tmp.m.compose(tmp.p.set(u.x, u.y, u.z), tmp.q.identity(), tmp.s.set(u.sx, u.sy, u.sz));
6334
6353
  mesh.setMatrixAt(i, tmp.m);
@@ -6343,7 +6362,7 @@ function installDestructionBreach(K, _shared) {
6343
6362
  if (!view.saved) view.saved = view.items.map((it) => ({ it, v0: it.v0, data: collapseRange(it.mesh, it.v0, it.v1) }));
6344
6363
  view.mesh = mesh;
6345
6364
  view.units = list.length;
6346
- views.set(partId, view);
6365
+ views.set(hostId, view);
6347
6366
  invalidate();
6348
6367
  invalidateShadow();
6349
6368
  return true;
@@ -8515,6 +8534,7 @@ function createFloorplanTestModule(options = {}) {
8515
8534
  return {
8516
8535
  id: FLOORPLAN_AREA_ID,
8517
8536
  name: text.name,
8537
+ ground: { surface: "lawn" },
8518
8538
  group: text.group,
8519
8539
  get houses() {
8520
8540
  return placed;
@@ -8622,6 +8642,9 @@ import { inferJoints } from "@adatechnology/game-destruction";
8622
8642
  import { markKitOrigin as markKitOrigin2 } from "@adatechnology/game-contracts";
8623
8643
  import { residentialItems } from "@adatechnology/game-floorplan-residential";
8624
8644
  var WOOD_THIN = 0.035;
8645
+ var BOARD_TOP_THICK = 0.07;
8646
+ var BOARD_TOP_WIDTH = 0.5;
8647
+ var BOARD_WIDTH = 0.16;
8625
8648
  var SHELF_SPAN = 1.5;
8626
8649
  var SHELVING = /shelf|bookcase|rack/;
8627
8650
  function carcass(parts, drawn) {
@@ -8636,6 +8659,39 @@ function carcass(parts, drawn) {
8636
8659
  } else if (y <= x && y <= z) part.structure = { role: "beam", maxSpan: SHELF_SPAN * Math.max(x, z) };
8637
8660
  }
8638
8661
  }
8662
+ function splitTopsIntoBoards(drawn, materialOf, carcassed) {
8663
+ const tops = /* @__PURE__ */ new Map();
8664
+ if (carcassed) return tops;
8665
+ for (let i = drawn.length - 1; i >= 0; i--) {
8666
+ const p = drawn[i];
8667
+ if (p.key !== "wood" || materialOf.wood !== "wood_hard" || p.shape.kind !== "box" || p.rotation) continue;
8668
+ const [sx, sy, sz] = p.shape.size;
8669
+ const across = sx < sz ? 0 : 2;
8670
+ const width = Math.min(sx, sz);
8671
+ if (sy < WOOD_THIN || sy > BOARD_TOP_THICK || sy >= width || width < BOARD_TOP_WIDTH) continue;
8672
+ const count = Math.round(width / BOARD_WIDTH);
8673
+ const boards = [];
8674
+ for (let b = 0; b < count; b++) {
8675
+ const size = [sx, sy, sz];
8676
+ size[across] = width / count;
8677
+ const position = [...p.position];
8678
+ position[across] += (b + 0.5) * size[across] - width / 2;
8679
+ const min = [...p.min];
8680
+ const max = [...p.max];
8681
+ min[across] = position[across] - size[across] / 2;
8682
+ max[across] = position[across] + size[across] / 2;
8683
+ boards.push({ ...p, id: `${p.id}_b${b}`, shape: { kind: "box", size }, position, min, max, color: shadeBoard(p.color, b) });
8684
+ }
8685
+ drawn.splice(i, 1, ...boards);
8686
+ tops.set(p.id, boards);
8687
+ }
8688
+ return tops;
8689
+ }
8690
+ function shadeBoard(color, board) {
8691
+ const k = [1, 0.94, 1.05, 0.97, 1.03][board % 5];
8692
+ const channel = (shift) => Math.min(255, Math.round((color >> shift & 255) * k)) << shift;
8693
+ return channel(16) | channel(8) | channel(0);
8694
+ }
8639
8695
  var FLOOR_TOUCH = 5e-3;
8640
8696
  var LOOSE = /* @__PURE__ */ new Set(["paper"]);
8641
8697
  var BASE_MATERIAL = {
@@ -8713,10 +8769,12 @@ function recordSpec(THREE, name, root, run, opts) {
8713
8769
  };
8714
8770
  run(A);
8715
8771
  if (!drawn.length) throw new Error(`game-floorplan-three: the builder of "${name}" drew no box or cylinder`);
8772
+ const boardsOf = splitTopsIntoBoards(drawn, materialOf, Boolean(opts.carcass));
8716
8773
  const parts = drawn.map((p) => {
8717
8774
  const thin = Math.min(...p.shape.kind === "box" ? p.shape.size : p.shape.kind === "cylinder" ? [p.shape.radius * 2, p.shape.height] : [p.shape.radius * 2]);
8718
8775
  let material = materialOf[p.key] ?? "plastic";
8719
- if (p.key === "wood" && material === "wood_hard" && thin < WOOD_THIN) material = "wood_soft";
8776
+ if (p.key === "wood" && material === "wood_hard" && opts.carcass && !opts.materials?.wood) material = "mdf";
8777
+ else if (p.key === "wood" && material === "wood_hard" && thin < WOOD_THIN) material = "wood_soft";
8720
8778
  const part = { id: p.id, material, shape: p.shape, position: p.position };
8721
8779
  if (p.rotation) part.rotation = p.rotation;
8722
8780
  if (!LOOSE.has(material) && p.min[1] <= FLOOR_TOUCH) part.anchored = true;
@@ -8751,6 +8809,13 @@ function recordSpec(THREE, name, root, run, opts) {
8751
8809
  if (area < 0.6 * footprint || p.max[1] < 0.2) continue;
8752
8810
  if (!top || p.max[1] > top.y + 1e-6) top = { part: p.id, y: p.max[1] };
8753
8811
  }
8812
+ for (const boards of boardsOf.values()) {
8813
+ if (top && boards[0].max[1] <= top.y - 1e-6) continue;
8814
+ const mid = boards[boards.length >> 1];
8815
+ const min = [Math.min(...boards.map((b) => b.min[0])), Math.min(...boards.map((b) => b.min[2]))];
8816
+ const max = [Math.max(...boards.map((b) => b.max[0])), Math.max(...boards.map((b) => b.max[2]))];
8817
+ if ((max[0] - min[0]) * (max[1] - min[1]) >= 0.6 * footprint && mid.max[1] >= 0.2) top = { part: mid.id, y: mid.max[1], parts: boards.map((b) => b.id) };
8818
+ }
8754
8819
  if (opts.carcass) carcass(parts, byId);
8755
8820
  const spec = { name, parts, joints, tags: ["furniture"] };
8756
8821
  const colors = new Map(drawn.map((p) => [p.id, p.color]));