@adatechnology/game-floorplan 0.2.2 → 0.3.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.
package/dist/index.js CHANGED
@@ -1,134 +1,120 @@
1
- // src/constants.ts
2
- var WALL_THICKNESS = 0.15;
3
- var MARGIN = WALL_THICKNESS / 2;
4
- var EPS = 1e-6;
5
- var TOL = 1e-3;
6
- var DOOR_CLEARANCE = 0.9;
7
- var PLAYER_RADIUS = 0.24;
8
- var DOOR_EDGE_CLEARANCE = 0.12;
9
- var WINDOW_EDGE_CLEARANCE = 0.3;
10
- var OVERHEAD_TOP = 2.15;
11
- var WINDOWLESS_ROOM_TYPES = /* @__PURE__ */ new Set([
12
- "stockroom",
13
- "kitchen",
14
- "laundry",
15
- "bathroom",
16
- "void",
17
- "elevator",
18
- "stairs",
19
- "pantry",
20
- "closet",
21
- "corridor",
22
- "hall",
23
- "balcony",
24
- "terrace",
25
- "garage",
26
- "storage",
27
- "porch",
28
- "tent",
29
- "outhouse"
30
- ]);
31
- var CAR_CLEARANCE = { driver: 0.8, side: 0.6, end: 0.6 };
32
-
33
1
  // src/registry.ts
34
- var CATALOG = {};
35
- var LIBRARIES = {};
36
- var LIBRARY_ALIAS = {};
37
- var TEMPLATES = {};
38
- var STYLES = {};
39
- var STYLE_ALIAS = {};
40
- var PALETTE_STYLES = {};
41
- var ROOM_COLORS = [];
42
- var PASSES = [];
43
- var DECOR_PROFILES = {};
44
- var GROUND_OVERLAYS = [];
45
- var STATE_PLUGINS = { normal: { apply: () => [] } };
46
- var WALL_TOP_HEIGHTS = { none: 0 };
47
- var SEED_KEYS = { rooms: {}, items: {} };
48
- var stateDistribution = { weights: null, seedKey: "state" };
2
+ var createRegistries = () => ({
3
+ CATALOG: {},
4
+ LIBRARIES: {},
5
+ LIBRARY_ALIAS: {},
6
+ TEMPLATES: {},
7
+ STYLES: {},
8
+ STYLE_ALIAS: {},
9
+ PALETTE_STYLES: {},
10
+ ROOM_COLORS: [],
11
+ PASSES: [],
12
+ DECOR_PROFILES: {},
13
+ GROUND_OVERLAYS: [],
14
+ STATE_PLUGINS: { normal: { apply: () => [] } },
15
+ WALL_TOP_HEIGHTS: { none: 0 },
16
+ SEED_KEYS: { rooms: {}, items: {} },
17
+ stateDistribution: { weights: null, seedKey: "state" },
18
+ RESULT_EXTENSIONS: [],
19
+ FINDS_PROVIDERS: [],
20
+ VIEWER_LAYERS: [],
21
+ LOCALES: {},
22
+ ZONES: {},
23
+ PERMEABLE_GROUNDS: /* @__PURE__ */ new Set()
24
+ });
25
+ var defaultRegistries;
26
+ var activeRegistries;
27
+ var getDefaultRegistries = () => defaultRegistries ?? (defaultRegistries = createRegistries());
28
+ var registries = () => activeRegistries ?? getDefaultRegistries();
29
+ function withRegistries(scope, run) {
30
+ const previous = activeRegistries;
31
+ activeRegistries = scope;
32
+ try {
33
+ return run();
34
+ } finally {
35
+ activeRegistries = previous;
36
+ }
37
+ }
49
38
  function registerItem(def) {
50
39
  const h = def.h;
51
40
  const item = Object.assign({ front: "+z", window: h <= 0.95, low: h <= 0.5, flat: false, deck: false, rim: 0 }, def);
52
- CATALOG[def.type] = item;
41
+ registries().CATALOG[def.type] = item;
53
42
  return item;
54
43
  }
55
44
  function registerItems(list) {
56
45
  for (const d of list) registerItem(d);
57
46
  }
58
47
  function registerLibrary(id, library) {
59
- LIBRARIES[id] = library;
48
+ registries().LIBRARIES[id] = library;
60
49
  }
61
50
  function registerLibraryAlias(alias, id) {
62
- LIBRARY_ALIAS[alias] = id;
51
+ registries().LIBRARY_ALIAS[alias] = id;
63
52
  }
64
53
  function registerTemplate(t) {
65
- TEMPLATES[t.id] = t;
54
+ registries().TEMPLATES[t.id] = t;
66
55
  return t;
67
56
  }
68
57
  function registerStyle(style) {
69
- STYLES[style.id] = style;
70
- for (const a of style.aliases || []) STYLE_ALIAS[a] = style.id;
58
+ registries().STYLES[style.id] = style;
59
+ for (const a of style.aliases || []) registries().STYLE_ALIAS[a] = style.id;
71
60
  }
72
61
  function registerPaletteStyle(id, def) {
73
- PALETTE_STYLES[id] = def;
62
+ registries().PALETTE_STYLES[id] = def;
74
63
  }
75
64
  function registerRoomColors(list) {
76
65
  for (const d of list) {
77
- const i = ROOM_COLORS.findIndex((x) => x.type === d.type);
78
- if (i >= 0) ROOM_COLORS.splice(i, 1);
79
- ROOM_COLORS.push(d);
66
+ const i = registries().ROOM_COLORS.findIndex((x) => x.type === d.type);
67
+ if (i >= 0) registries().ROOM_COLORS.splice(i, 1);
68
+ registries().ROOM_COLORS.push(d);
80
69
  }
81
- ROOM_COLORS.sort((a, b) => a.order - b.order);
70
+ registries().ROOM_COLORS.sort((a, b) => a.order - b.order);
82
71
  }
83
72
  function registerDecorProfile(styleId, profile) {
84
- DECOR_PROFILES[styleId] = profile;
73
+ registries().DECOR_PROFILES[styleId] = profile;
85
74
  }
86
75
  function registerPass(name, run) {
87
- const i = PASSES.findIndex((p) => p.name === name);
88
- if (i >= 0) PASSES[i] = { name, run };
89
- else PASSES.push({ name, run });
76
+ const i = registries().PASSES.findIndex((p) => p.name === name);
77
+ if (i >= 0) registries().PASSES[i] = { name, run };
78
+ else registries().PASSES.push({ name, run });
90
79
  }
91
80
  function registerGroundOverlay(rule) {
92
- GROUND_OVERLAYS.push(rule);
81
+ registries().GROUND_OVERLAYS.push(rule);
93
82
  }
94
83
  function registerWallTop(id, height) {
95
- WALL_TOP_HEIGHTS[id] = height;
84
+ registries().WALL_TOP_HEIGHTS[id] = height;
96
85
  }
97
86
  function registerSeedKeys(keys) {
98
- Object.assign(SEED_KEYS.rooms, keys.rooms);
99
- Object.assign(SEED_KEYS.items, keys.items);
87
+ Object.assign(registries().SEED_KEYS.rooms, keys.rooms);
88
+ Object.assign(registries().SEED_KEYS.items, keys.items);
100
89
  }
101
90
  function registerState(name, plugin) {
102
- STATE_PLUGINS[name] = plugin;
91
+ registries().STATE_PLUGINS[name] = plugin;
103
92
  }
104
93
  function setStateDistribution(weights, seedKey = "state") {
105
- stateDistribution.weights = weights;
106
- stateDistribution.seedKey = seedKey;
94
+ registries().stateDistribution.weights = weights;
95
+ registries().stateDistribution.seedKey = seedKey;
107
96
  }
108
- var listStates = () => Object.keys(STATE_PLUGINS);
97
+ var listStates = () => Object.keys(registries().STATE_PLUGINS);
109
98
  function resolveStyleId(id) {
110
99
  const s = id ?? "";
111
- return STYLE_ALIAS[s] ?? s;
100
+ return registries().STYLE_ALIAS[s] ?? s;
112
101
  }
113
102
  function styleOf(id) {
114
- return STYLES[id] || STYLES.budget || Object.values(STYLES)[0];
103
+ return registries().STYLES[id] || registries().STYLES.budget || Object.values(registries().STYLES)[0];
115
104
  }
116
- var RESULT_EXTENSIONS = [];
117
- var FINDS_PROVIDERS = [];
118
105
  function registerResultExtension(name, run, options = {}) {
119
106
  const entry = { name, run, after: options.after || [], seedKey: options.seedKey ?? name };
120
- const i = RESULT_EXTENSIONS.findIndex((x) => x.name === name);
121
- if (i >= 0) RESULT_EXTENSIONS[i] = entry;
122
- else RESULT_EXTENSIONS.push(entry);
107
+ const i = registries().RESULT_EXTENSIONS.findIndex((x) => x.name === name);
108
+ if (i >= 0) registries().RESULT_EXTENSIONS[i] = entry;
109
+ else registries().RESULT_EXTENSIONS.push(entry);
123
110
  }
124
111
  function registerFindsProvider(provider) {
125
- FINDS_PROVIDERS.push(provider);
112
+ registries().FINDS_PROVIDERS.push(provider);
126
113
  }
127
- var VIEWER_LAYERS = [];
128
114
  function registerViewerLayer(layer) {
129
- const i = VIEWER_LAYERS.findIndex((x) => x.id === layer.id);
130
- if (i >= 0) VIEWER_LAYERS[i] = layer;
131
- else VIEWER_LAYERS.push(layer);
115
+ const i = registries().VIEWER_LAYERS.findIndex((x) => x.id === layer.id);
116
+ if (i >= 0) registries().VIEWER_LAYERS[i] = layer;
117
+ else registries().VIEWER_LAYERS.push(layer);
132
118
  }
133
119
 
134
120
  // src/rng.ts
@@ -208,7 +194,7 @@ function orderOf(extensions) {
208
194
  return ordered;
209
195
  }
210
196
  function runResultExtensions(plan, result, seed, options) {
211
- for (const extension of orderOf(RESULT_EXTENSIONS)) {
197
+ for (const extension of orderOf(registries().RESULT_EXTENSIONS)) {
212
198
  result[extension.name] = extension.run({ plan, result, seed, rng: createRng(seed, extension.seedKey), options });
213
199
  }
214
200
  }
@@ -219,13 +205,45 @@ function findsAnswer(query) {
219
205
  return answerOf(query);
220
206
  }
221
207
  function answerOf(query) {
222
- for (const provider of FINDS_PROVIDERS) {
208
+ for (const provider of registries().FINDS_PROVIDERS) {
223
209
  const stacks = provider.findsFor(query);
224
210
  if (stacks !== null) return stacks;
225
211
  }
226
212
  return null;
227
213
  }
228
214
 
215
+ // src/constants.ts
216
+ var WALL_THICKNESS = 0.15;
217
+ var MARGIN = WALL_THICKNESS / 2;
218
+ var EPS = 1e-6;
219
+ var TOL = 1e-3;
220
+ var DOOR_CLEARANCE = 0.9;
221
+ var PLAYER_RADIUS = 0.24;
222
+ var DOOR_EDGE_CLEARANCE = 0.12;
223
+ var WINDOW_EDGE_CLEARANCE = 0.3;
224
+ var OVERHEAD_TOP = 2.15;
225
+ var WINDOWLESS_ROOM_TYPES = /* @__PURE__ */ new Set([
226
+ "stockroom",
227
+ "kitchen",
228
+ "laundry",
229
+ "bathroom",
230
+ "void",
231
+ "elevator",
232
+ "stairs",
233
+ "pantry",
234
+ "closet",
235
+ "corridor",
236
+ "hall",
237
+ "balcony",
238
+ "terrace",
239
+ "garage",
240
+ "storage",
241
+ "porch",
242
+ "tent",
243
+ "outhouse"
244
+ ]);
245
+ var CAR_CLEARANCE = { driver: 0.8, side: 0.6, end: 0.6 };
246
+
229
247
  // src/geometry.ts
230
248
  var r2 = (x) => Math.round(x * 100) / 100;
231
249
  var clamp = (x, a, b) => Math.max(a, Math.min(b, x));
@@ -442,6 +460,7 @@ var STEP = 0.05;
442
460
  var MAX_CANDIDATES = 80;
443
461
  function carCandidates(slot, item, car) {
444
462
  const front = [slot.frontU, slot.frontV];
463
+ if (!slot._cell) throw new Error("Slot without a cell: " + slot.id);
445
464
  const zone = slot._cell.R;
446
465
  const alongV = Math.abs(front[1]) === 1;
447
466
  const [extentU, extentV] = extent([item.w, item.d], front);
@@ -660,13 +679,14 @@ function candidatesOf(cats) {
660
679
  const out = [];
661
680
  for (const [cat, weight] of Object.entries(cats)) {
662
681
  const exact = cat[0] === "=";
663
- const its = Object.values(CATALOG).filter((i) => !i.decor && (exact ? i.type === cat.slice(1) : !i.exactOnly && (i.category === cat || i.type === cat)));
682
+ const its = Object.values(registries().CATALOG).filter((i) => !i.decor && (exact ? i.type === cat.slice(1) : !i.exactOnly && (i.category === cat || i.type === cat)));
664
683
  for (const it of its) out.push({ it, weight: weight / its.length });
665
684
  }
666
685
  return out;
667
686
  }
668
687
  function itemRect(slot, it, shift) {
669
688
  const c = slot._cell;
689
+ if (!c) throw new Error("Slot without a cell: " + slot.id);
670
690
  const f = c.f;
671
691
  const [extentU, extentV] = extent([it.w, it.d], f);
672
692
  const R = c.R;
@@ -751,7 +771,7 @@ function fillRoom(plan, room, slots2, rng) {
751
771
  low: it.low,
752
772
  aabb: r.map(r2),
753
773
  _r: r,
754
- _seedId: SEED_KEYS.items[id] ?? id
774
+ _seedId: registries().SEED_KEYS.items[id] ?? id
755
775
  };
756
776
  if (it.overhead) m.overhead = !overheadOff;
757
777
  if (it.category === "car") {
@@ -771,7 +791,7 @@ function fillRoom(plan, room, slots2, rng) {
771
791
  if (!parent) return null;
772
792
  for (const { it } of cands) {
773
793
  if (it.w > parent.w + EPS || it.d > parent.d + EPS) continue;
774
- const f = [parent.frontU, parent.frontV];
794
+ const f = [parent.frontU ?? 0, parent.frontV ?? 1];
775
795
  const r = parent.aabb;
776
796
  const [extentU, extentV] = extent([it.w, it.d], f);
777
797
  const cu = (r[0] + r[2]) / 2 - f[0] * (parent.d / 2 - it.d / 2);
@@ -805,7 +825,7 @@ function fillRoom(plan, room, slots2, rng) {
805
825
  if (!car && !relax && s.clearance > 0 && !it.low) {
806
826
  cl2 = clearRect(r3, f, s.clearance);
807
827
  if (!insideUnion(cl2, inners)) continue;
808
- if (items.some((m2) => !m2.low && !m2.flat && !m2.on && overlaps(cl2, m2._r))) continue;
828
+ if (items.some((m2) => !m2.low && !m2.flat && !m2.on && overlaps(cl2, m2._r ?? m2.aabb))) continue;
809
829
  }
810
830
  if (!it.low && reserves.some((q) => overlaps(r3, q))) continue;
811
831
  let rimRect2 = null;
@@ -902,9 +922,9 @@ var placementKit = {
902
922
  clearRect,
903
923
  pickWeighted,
904
924
  createRng,
905
- roomSeedKey: (room) => room.seedKey ?? SEED_KEYS.rooms[room.id] ?? room.id,
925
+ roomSeedKey: (room) => room.seedKey ?? registries().SEED_KEYS.rooms[room.id] ?? room.id,
906
926
  shuffle,
907
- facingRotation: (f, type) => rotationFor(f, CATALOG[type].front),
927
+ facingRotation: (f, type) => rotationFor(f, registries().CATALOG[type].front),
908
928
  cutIntervals,
909
929
  blocksWindow
910
930
  };
@@ -912,7 +932,7 @@ function createPlacementContext(plan, items) {
912
932
  const added = [];
913
933
  const counters = {};
914
934
  const make = (room, type, r, f, o) => {
915
- const it = CATALOG[type];
935
+ const it = registries().CATALOG[type];
916
936
  const n = counters[room.id + type] = (counters[room.id + type] || 0) + 1;
917
937
  return Object.assign(
918
938
  {
@@ -958,9 +978,9 @@ function createPlacementContext(plan, items) {
958
978
  const gridOf = (c) => gridCache[c.id] || (gridCache[c.id] = createGrid(plan, c, ignored));
959
979
  function placeOnFloor(room, type, rng2, optsIn) {
960
980
  const opts = Object.assign({}, optsIn || {});
961
- const it0 = CATALOG[type];
981
+ const it0 = registries().CATALOG[type];
962
982
  if (it0.flat) opts.flat = true;
963
- const it = CATALOG[type];
983
+ const it = registries().CATALOG[type];
964
984
  const inners = usableParts(room, 0);
965
985
  const solidList = solidsOfRoom(room);
966
986
  const zones = zonesOfFloor(plan, room.floor);
@@ -993,7 +1013,7 @@ function createPlacementContext(plan, items) {
993
1013
  if (!(opts.without && opts.without(r))) return r;
994
1014
  continue;
995
1015
  }
996
- if (occupied.some((m) => !m.low && !m.flat && !m.on && !m.decor && m.clearance > 0 && overlaps(r, clearRect(m._r, [m.frontU, m.frontV], m.clearance)))) continue;
1016
+ if (occupied.some((m) => !m.low && !m.flat && !m.on && !m.decor && (m.clearance ?? 0) > 0 && overlaps(r, clearRect(m._r, [m.frontU ?? 0, m.frontV ?? 1], m.clearance ?? 0)))) continue;
997
1017
  if (opts.without && opts.without(r)) continue;
998
1018
  if (!reachable(opts.railing || gridOf(room), solidList.concat([r]), room.outdoor ? 0.16 : plan.playerRadius)) continue;
999
1019
  return r;
@@ -1039,7 +1059,7 @@ function groundCover(plan, items, ctx) {
1039
1059
  z.parts.forEach((p, i) => groundPiece(z, z.ground, p, 0, i));
1040
1060
  let k = 0;
1041
1061
  const clip = (r) => z.parts.map((p) => [Math.max(r[0], p[0]), Math.max(r[1], p[1]), Math.min(r[2], p[2]), Math.min(r[3], p[3])]).filter((q) => q[2] - q[0] > 0.3 && q[3] - q[1] > 0.3).sort((a, b) => areaOf(b) - areaOf(a))[0];
1042
- for (const rule of GROUND_OVERLAYS) {
1062
+ for (const rule of registries().GROUND_OVERLAYS) {
1043
1063
  const triggers = items.filter((m) => m.room === z.id && m.category === rule.category);
1044
1064
  if (rule.merge) {
1045
1065
  if (triggers.length && z.ground !== rule.unlessGround) {
@@ -1059,19 +1079,20 @@ function groundCover(plan, items, ctx) {
1059
1079
  function runPostProcess(plan, items, seed) {
1060
1080
  const ctx = createPlacementContext(plan, items);
1061
1081
  const rng = createRng(seed, STREAM.decor);
1062
- for (const pass of PASSES) pass.run({ plan, items, seed, rng, ctx, kit: placementKit, catalog: CATALOG, profiles: DECOR_PROFILES });
1082
+ for (const pass of registries().PASSES) pass.run({ plan, items, seed, rng, ctx, kit: placementKit, catalog: registries().CATALOG, profiles: registries().DECOR_PROFILES });
1063
1083
  groundCover(plan, items, ctx);
1064
1084
  return ctx;
1065
1085
  }
1066
1086
 
1067
1087
  // src/locale.ts
1068
- var locales = {};
1069
1088
  function registerLocale(code, table) {
1089
+ const { LOCALES: locales } = registries();
1070
1090
  const cur = locales[code] = locales[code] || {};
1071
1091
  for (const ns of Object.keys(table)) cur[ns] = Object.assign(cur[ns] || {}, table[ns]);
1072
1092
  }
1073
- var listLocales = () => Object.keys(locales);
1093
+ var listLocales = () => Object.keys(registries().LOCALES);
1074
1094
  function translate(ns, key, locale = "en", vars) {
1095
+ const { LOCALES: locales } = registries();
1075
1096
  const raw = locales[locale]?.[ns]?.[key] ?? locales.en?.[ns]?.[key] ?? key;
1076
1097
  return vars ? raw.replace(/\{(\w+)\}/g, (m, k) => k in vars ? String(vars[k]) : m) : raw;
1077
1098
  }
@@ -1200,22 +1221,20 @@ function addExteriorDoorPaths(plan, c) {
1200
1221
  }
1201
1222
 
1202
1223
  // src/zoning.ts
1203
- var ZONES = {};
1204
- var PERMEABLE_GROUNDS = /* @__PURE__ */ new Set();
1205
1224
  function defineZone(zone) {
1206
1225
  if (!zone || !zone.id) throw new Error("defineZone: id is required");
1207
- ZONES[zone.id] = zone;
1226
+ registries().ZONES[zone.id] = zone;
1208
1227
  return zone;
1209
1228
  }
1210
- var getZone = (id) => ZONES[id];
1211
- var listZones = () => Object.values(ZONES);
1229
+ var getZone = (id) => registries().ZONES[id];
1230
+ var listZones = () => Object.values(registries().ZONES);
1212
1231
  function registerPermeableGround(...types) {
1213
- for (const t of types) PERMEABLE_GROUNDS.add(t);
1232
+ for (const t of types) registries().PERMEABLE_GROUNDS.add(t);
1214
1233
  }
1215
1234
  function resolveZone(z) {
1216
1235
  if (!z) return null;
1217
1236
  if (typeof z === "string") {
1218
- const found = ZONES[z];
1237
+ const found = registries().ZONES[z];
1219
1238
  if (!found) throw new Error("Unknown zone: " + z);
1220
1239
  return found;
1221
1240
  }
@@ -1261,7 +1280,7 @@ function checkLot(zoneIn, lot, plan) {
1261
1280
  const far = m.built / area;
1262
1281
  if (far > zone.maxFar + 1e-9) out.push({ rule: "far", actual: r2(far), limit: zone.maxFar });
1263
1282
  let permeable = 0;
1264
- for (const z of lot.zones) if (PERMEABLE_GROUNDS.has(z.ground)) for (const p of z.parts) permeable += (p[2] - p[0]) * (p[3] - p[1]);
1283
+ for (const z of lot.zones) if (z.ground && registries().PERMEABLE_GROUNDS.has(z.ground)) for (const p of z.parts) permeable += (p[2] - p[0]) * (p[3] - p[1]);
1265
1284
  const permeability = permeable / area;
1266
1285
  if (permeability < zone.permeableMin - 1e-9) out.push({ rule: "permeable", actual: r2(permeability), limit: zone.permeableMin });
1267
1286
  return out;
@@ -1299,17 +1318,17 @@ function clampLotToZone(cfg, zone, adjustments, partyWalls = []) {
1299
1318
  }
1300
1319
  var ADJUSTABLE_ZONES = /* @__PURE__ */ new Set(["garden", "backyard", "side_yard"]);
1301
1320
  function enforcePermeability(lot, zones, zone, adjustments) {
1302
- const target = [...PERMEABLE_GROUNDS][0];
1321
+ const target = [...registries().PERMEABLE_GROUNDS][0];
1303
1322
  if (!target) return;
1304
1323
  const [u0, v0, u1, v1] = lot.rect;
1305
1324
  const area = (u1 - u0) * (v1 - v0);
1306
1325
  const areaOfZone = (z) => z.parts.reduce((s, p) => s + (p[2] - p[0]) * (p[3] - p[1]), 0);
1307
- let permeable = zones.filter((z) => PERMEABLE_GROUNDS.has(z.ground)).reduce((s, z) => s + areaOfZone(z), 0);
1326
+ let permeable = zones.filter((z) => z.ground != null && registries().PERMEABLE_GROUNDS.has(z.ground)).reduce((s, z) => s + areaOfZone(z), 0);
1308
1327
  if (permeable / area >= zone.permeableMin) return;
1309
- const candidates = zones.filter((z) => ADJUSTABLE_ZONES.has(z.type) && !PERMEABLE_GROUNDS.has(z.ground)).sort((a, b) => areaOfZone(b) - areaOfZone(a));
1328
+ const candidates = zones.filter((z) => ADJUSTABLE_ZONES.has(z.type) && !(z.ground != null && registries().PERMEABLE_GROUNDS.has(z.ground))).sort((a, b) => areaOfZone(b) - areaOfZone(a));
1310
1329
  for (const z of candidates) {
1311
1330
  if (permeable / area >= zone.permeableMin) break;
1312
- adjustments.push({ field: "ground:" + z.id, from: z.ground, to: target, reason: "permeable_min" });
1331
+ adjustments.push({ field: "ground:" + z.id, from: z.ground ?? "", to: target, reason: "permeable_min" });
1313
1332
  z.ground = target;
1314
1333
  permeable += areaOfZone(z);
1315
1334
  }
@@ -1539,9 +1558,9 @@ function buildLot(plan, tpl, w, d, op, style, styleId, bc) {
1539
1558
  paths: [],
1540
1559
  pool: !!cfg.pool
1541
1560
  };
1542
- const ZONES2 = [];
1561
+ const ZONES = [];
1543
1562
  const libs = Object.assign({ garden: "garden", backyard: "backyard", parking: "parking", sideYard: "side_yard" }, cfg.libraries || {});
1544
- const addZone = (id, type, label, parts, library) => ZONES2.push(Object.assign(normalizeRoom({ id, type, label, rect: parts[0], parts: parts.slice(1), library, outdoor: true }, -1), { floor: -1, outdoor: true }));
1563
+ const addZone = (id, type, label, parts, library) => ZONES.push(Object.assign(normalizeRoom({ id, type, label, rect: parts[0], parts: parts.slice(1), library, outdoor: true }, -1), { floor: -1, outdoor: true }));
1545
1564
  if (setback > 0) {
1546
1565
  let parts = [[-marginL, -setback, w + marginR, 0]];
1547
1566
  if (cfg.parking) {
@@ -1578,9 +1597,9 @@ function buildLot(plan, tpl, w, d, op, style, styleId, bc) {
1578
1597
  height: type === "pedestrian" ? style.height : Math.min(style.height, 2.2),
1579
1598
  side,
1580
1599
  to: toTarget || null,
1581
- material: "iron"
1600
+ material: "iron",
1601
+ zones: openingZones(u, v, "u", width, openingDepth(width, type === "pedestrian" ? width : 0))
1582
1602
  };
1583
- g.zones = openingZones(u, v, "u", width, openingDepth(width, type === "pedestrian" ? width : 0));
1584
1603
  gates.push(g);
1585
1604
  return g;
1586
1605
  };
@@ -1621,14 +1640,14 @@ function buildLot(plan, tpl, w, d, op, style, styleId, bc) {
1621
1640
  }
1622
1641
  }
1623
1642
  if (tpl.paths) paths.push(...tpl.paths(plan, { gates, setback, marginLeft: marginL, marginRight: marginR, backyard: backDepth, w, d }));
1624
- else addExteriorDoorPaths(plan, { gates, paths, zones: ZONES2, setback, marginLeft: marginL, marginRight: marginR, backyard: backDepth, w, target: cfg.doorTarget });
1643
+ else addExteriorDoorPaths(plan, { gates, paths, zones: ZONES, setback, marginLeft: marginL, marginRight: marginR, backyard: backDepth, w, target: cfg.doorTarget });
1625
1644
  plan.paths = paths;
1626
1645
  lot.paths = paths;
1627
1646
  const boundary = (rule) => {
1628
1647
  const type = pickWeighted(rLot, rule.type);
1629
1648
  const top = type === "railing" ? "none" : pickWeighted(rLot, rule.top);
1630
1649
  const wallHeight = type === "railing" ? 1.2 : type === "fence" ? style.fenceHeight ?? 2 : style.height;
1631
- return { type, material: type === "wall" ? "block" : type === "fence" ? "wire_mesh" : "iron", height: wallHeight, top, totalHeight: r2(wallHeight + WALL_TOP_HEIGHTS[top]), thickness: WALL_THICKNESS };
1650
+ return { type, material: type === "wall" ? "block" : type === "fence" ? "wire_mesh" : "iron", height: wallHeight, top, totalHeight: r2(wallHeight + registries().WALL_TOP_HEIGHTS[top]), thickness: WALL_THICKNESS };
1632
1651
  };
1633
1652
  const A = { front: boundary(style.front), left: boundary(style.sideYard), right: boundary(style.sideYard), back: boundary(style.back) };
1634
1653
  const seg = (side, u0, v0, u1, v1) => {
@@ -1661,10 +1680,10 @@ function buildLot(plan, tpl, w, d, op, style, styleId, bc) {
1661
1680
  else if (backDepth > 0) seg("left", 0, d, 0, d + backDepth);
1662
1681
  if (marginR > 0.5) seg("right", w + marginR, vi, w + marginR, d + backDepth);
1663
1682
  else if (backDepth > 0) seg("right", w, d, w, d + backDepth);
1664
- for (const z of ZONES2) z.ground = (cfg.grounds || {})[z.id] || (cfg.grounds || {})[z.type] || pickWeighted(rLot, style.grounds[z.type] || { concrete: 1 });
1665
- if (bc.zone) enforcePermeability(lot, ZONES2, bc.zone, bc.adjustments || []);
1666
- plan.zones = ZONES2;
1667
- lot.zones = ZONES2;
1683
+ for (const z of ZONES) z.ground = (cfg.grounds || {})[z.id] || (cfg.grounds || {})[z.type] || pickWeighted(rLot, style.grounds[z.type] || { concrete: 1 });
1684
+ if (bc.zone) enforcePermeability(lot, ZONES, bc.zone, bc.adjustments || []);
1685
+ plan.zones = ZONES;
1686
+ lot.zones = ZONES;
1668
1687
  plan.lot = lot;
1669
1688
  }
1670
1689
 
@@ -1714,8 +1733,8 @@ var hexString = (n) => "#" + (n >>> 0).toString(16).padStart(6, "0");
1714
1733
  function palette(o) {
1715
1734
  const opt = o || {};
1716
1735
  let style = resolveStyleId(opt.style || "budget");
1717
- if (!PALETTE_STYLES[style]) style = PALETTE_STYLES.mid ? "mid" : Object.keys(PALETTE_STYLES)[0];
1718
- const def = PALETTE_STYLES[style];
1736
+ if (!registries().PALETTE_STYLES[style]) style = registries().PALETTE_STYLES.mid ? "mid" : Object.keys(registries().PALETTE_STYLES)[0];
1737
+ const def = registries().PALETTE_STYLES[style];
1719
1738
  const state = opt.state || "normal";
1720
1739
  const seed = opt.seed == null ? 1 : opt.seed;
1721
1740
  const rng = createRng(seed, STREAM.palettePrefix + (def.seedKey ?? style));
@@ -1733,14 +1752,14 @@ function palette(o) {
1733
1752
  const { wallMode, sMax } = early;
1734
1753
  const R = Object.assign({}, early.roles);
1735
1754
  const ctx = Object.assign({ style, sMax, wallMode, generalFloor: early.generalFloor }, draw);
1736
- for (const c of ROOM_COLORS) {
1755
+ for (const c of registries().ROOM_COLORS) {
1737
1756
  const [wh, wl] = c.wall(ctx);
1738
1757
  R["wall_" + c.type] = hsl(wh, c.neutral ? Math.min(0.16, sMax) : clamp(jit(sMax * 0.75, sMax * 0.5), 0.03, sMax), jit(wl, 0.03));
1739
1758
  const pi = c.floor || early.generalFloor;
1740
1759
  R["floor_" + c.type] = hsl(pi[0], Math.min(pi[1], wallMode ? 0.18 : 0.3), jit(pi[2], 0.04));
1741
1760
  }
1742
1761
  Object.assign(R, def.late(draw, early));
1743
- STATE_PLUGINS[state]?.adjustPalette?.(R);
1762
+ registries().STATE_PLUGINS[state]?.adjustPalette?.(R);
1744
1763
  const adjust = (color, ref, min, preferredDir) => {
1745
1764
  for (let i = 0; i < 60; i++) {
1746
1765
  if (Math.abs(lstar(hslToInt(color.h, color.s, color.l)) - lstar(hslToInt(ref.h, ref.s, ref.l))) >= min) return;
@@ -1750,7 +1769,7 @@ function palette(o) {
1750
1769
  color.l = clamp(color.l + dir * 0.015, 0.04, 0.96);
1751
1770
  }
1752
1771
  };
1753
- const types = ROOM_COLORS.map((c) => c.type);
1772
+ const types = registries().ROOM_COLORS.map((c) => c.type);
1754
1773
  for (const c of types) {
1755
1774
  const w = R["wall_" + c];
1756
1775
  const p = R["floor_" + c];
@@ -1804,8 +1823,8 @@ var SIDE_OPPOSITE = { front: "back", back: "front", left: "right", right: "left"
1804
1823
  var WINDOW_SIDE = { front: "front", back: "back", left: "left", right: "right" };
1805
1824
  function slotDefsOf(plan, room) {
1806
1825
  const name = room.library || room.type;
1807
- const key = LIBRARY_ALIAS[name] || name;
1808
- const library = room.slots ? room.slots : LIBRARIES[key];
1826
+ const key = registries().LIBRARY_ALIAS[name] || name;
1827
+ const library = room.slots ? room.slots : registries().LIBRARIES[key];
1809
1828
  if (!library) return [];
1810
1829
  const R = inner(room.parts[0]);
1811
1830
  let defs = typeof library === "function" ? library({ w: R[2] - R[0], d: R[3] - R[1] }) : library.slice();
@@ -1867,7 +1886,7 @@ function resolveSlot(plan, room, def, byId, zones) {
1867
1886
  };
1868
1887
  const invalid = (m) => Object.assign(base, { valid: false, reason: m, x: 0, z: 0, ry: 0, w: 0, d: 0, frontU: 0, frontV: 1, aabb: [0, 0, 0, 0], _cell: null });
1869
1888
  if (def.anchor === "on" || def.anchor === "inside") {
1870
- const parent = byId[def.parent];
1889
+ const parent = def.parent ? byId[def.parent] : void 0;
1871
1890
  if (!parent || !parent.valid) return invalid("invalid_parent");
1872
1891
  return Object.assign(base, { x: parent.x, z: parent.z, ry: parent.ry, w: parent.w, d: parent.d, frontU: parent.frontU, frontV: parent.frontV, aabb: parent.aabb, _cell: parent._cell });
1873
1892
  }
@@ -1908,9 +1927,10 @@ function resolveSlot(plan, room, def, byId, zones) {
1908
1927
  const windowList = windowsOfRoom(plan, room);
1909
1928
  win = windowList[def.n];
1910
1929
  if (!win) return invalid("no_window");
1930
+ const found2 = win;
1911
1931
  side = room.outdoor ? SIDE_OPPOSITE[win.side] : WINDOW_SIDE[win.side];
1912
1932
  const cand = room.parts.map(inner).find(
1913
- (r) => (win.axis === "u" ? win.u >= r[0] - TOL && win.u <= r[2] + TOL : win.v >= r[1] - TOL && win.v <= r[3] + TOL) && (side === "left" && eq(r[0], win.pos + MARGIN) || side === "right" && eq(r[2], win.pos - MARGIN) || side === "front" && eq(r[1], win.pos + MARGIN) || side === "back" && eq(r[3], win.pos - MARGIN))
1933
+ (r) => (found2.axis === "u" ? found2.u >= r[0] - TOL && found2.u <= r[2] + TOL : found2.v >= r[1] - TOL && found2.v <= r[3] + TOL) && (side === "left" && eq(r[0], found2.pos + MARGIN) || side === "right" && eq(r[2], found2.pos - MARGIN) || side === "front" && eq(r[1], found2.pos + MARGIN) || side === "back" && eq(r[3], found2.pos - MARGIN))
1914
1934
  );
1915
1935
  if (!cand) return invalid("window_outside_room");
1916
1936
  R = cand;
@@ -1995,8 +2015,8 @@ function resolveSlot(plan, room, def, byId, zones) {
1995
2015
  // src/generate.ts
1996
2016
  function templateOf(t) {
1997
2017
  if (typeof t === "string") {
1998
- if (!TEMPLATES[t]) throw new Error("Unknown template: " + t);
1999
- return TEMPLATES[t];
2018
+ if (!registries().TEMPLATES[t]) throw new Error("Unknown template: " + t);
2019
+ return registries().TEMPLATES[t];
2000
2020
  }
2001
2021
  return t;
2002
2022
  }
@@ -2014,10 +2034,10 @@ function slots(template, options) {
2014
2034
  return resolveSlots(plan).map(cleanSlot).map((s) => s.reason ? Object.assign(s, { reason: translate("reasons", s.reason, locale) }) : s);
2015
2035
  }
2016
2036
  function pickState(requested, seed) {
2017
- if (requested && STATE_PLUGINS[requested]) return requested;
2018
- const dist = stateDistribution.weights;
2037
+ if (requested && registries().STATE_PLUGINS[requested]) return requested;
2038
+ const dist = registries().stateDistribution.weights;
2019
2039
  if (!dist) return "normal";
2020
- const x = createRng(seed, stateDistribution.seedKey)();
2040
+ const x = createRng(seed, registries().stateDistribution.seedKey)();
2021
2041
  let acc = 0;
2022
2042
  const keys = Object.keys(dist);
2023
2043
  for (const k of keys) {
@@ -2027,13 +2047,13 @@ function pickState(requested, seed) {
2027
2047
  return keys[keys.length - 1];
2028
2048
  }
2029
2049
  function colorOf(m, seed) {
2030
- const spec = m.colorSpec || CATALOG[m.type]?.color || { role: "wood" };
2050
+ const spec = m.colorSpec || registries().CATALOG[m.type]?.color || { role: "wood" };
2031
2051
  if ("role" in spec) return spec.role;
2032
2052
  if ("hex" in spec) return spec.hex;
2033
2053
  return spec.pick[hashString(seed + "|" + (m._seedId ?? m.id)) % spec.pick.length];
2034
2054
  }
2035
2055
  function applyState(plan, items, ctx, seed, state) {
2036
- const plugin = STATE_PLUGINS[state];
2056
+ const plugin = registries().STATE_PLUGINS[state];
2037
2057
  if (!plugin || state === "normal") return [];
2038
2058
  const c = {
2039
2059
  plan,
@@ -2043,8 +2063,8 @@ function applyState(plan, items, ctx, seed, state) {
2043
2063
  rng: createRng(seed, plugin.seedKey ?? "state:" + state),
2044
2064
  ctx,
2045
2065
  kit: placementKit,
2046
- catalog: CATALOG,
2047
- wallTopHeights: WALL_TOP_HEIGHTS
2066
+ catalog: registries().CATALOG,
2067
+ wallTopHeights: registries().WALL_TOP_HEIGHTS
2048
2068
  };
2049
2069
  return plugin.apply(c);
2050
2070
  }
@@ -2064,7 +2084,7 @@ function generate(template, options) {
2064
2084
  const missing = [];
2065
2085
  for (const room of plan.rooms.concat(plan.zones)) {
2066
2086
  const sl = all.filter((s) => s.room === room.id && s.floor === room.floor);
2067
- const r = fillRoom(plan, room, sl, createRng(seed, STREAM.roomPrefix + (room.seedKey ?? SEED_KEYS.rooms[room.id] ?? room.id)));
2087
+ const r = fillRoom(plan, room, sl, createRng(seed, STREAM.roomPrefix + (room.seedKey ?? registries().SEED_KEYS.rooms[room.id] ?? room.id)));
2068
2088
  items.push(...r.items);
2069
2089
  missing.push(...r.missing);
2070
2090
  }
@@ -2104,7 +2124,7 @@ function generate(template, options) {
2104
2124
  if (m.category) out.color = colorOf(m, seed);
2105
2125
  return out;
2106
2126
  });
2107
- const result = {
2127
+ const base = {
2108
2128
  template: tpl.id,
2109
2129
  name: translate("templates", tpl.id, locale),
2110
2130
  use: useOf(tpl),
@@ -2141,8 +2161,8 @@ function generate(template, options) {
2141
2161
  building: tpl.building ? tpl.building(plan.w, plan.d, op) : null,
2142
2162
  missing
2143
2163
  };
2144
- if (tpl.singleFloor) result.floorNumber = op.floor || 3;
2145
- result.partyWalls = tpl.partyWalls || [];
2164
+ if (tpl.singleFloor) base.floorNumber = op.floor || 3;
2165
+ const result = Object.assign(base, { partyWalls: tpl.partyWalls || [] });
2146
2166
  if (plan.config) result.config = plan.config;
2147
2167
  runResultExtensions(plan, result, seed, op);
2148
2168
  if (zone) {
@@ -2177,7 +2197,7 @@ function limitsOf(template, options) {
2177
2197
  return { limits: tpl.limitsFn ? tpl.limitsFn(op) : tpl.limits, defaults: tpl.defaultsFn ? tpl.defaultsFn(op) : tpl.defaults };
2178
2198
  }
2179
2199
  function listTemplates(locale = "en") {
2180
- return Object.values(TEMPLATES).map((t) => {
2200
+ return Object.values(registries().TEMPLATES).map((t) => {
2181
2201
  const a = floorsOf(t, {});
2182
2202
  return {
2183
2203
  id: t.id,
@@ -2194,6 +2214,9 @@ function listTemplates(locale = "en") {
2194
2214
  });
2195
2215
  }
2196
2216
 
2217
+ // src/instance.ts
2218
+ import { applyPatches, removeIds } from "@adatechnology/game-contracts";
2219
+
2197
2220
  // src/ground-items.ts
2198
2221
  function registerGroundItems() {
2199
2222
  const flat = { category: "ground_cover", w: 1, d: 1, h: 0, decor: true, mount: "ground", flat: true };
@@ -2414,6 +2437,109 @@ var pt_BR_default = {
2414
2437
  }
2415
2438
  };
2416
2439
 
2440
+ // src/instance.ts
2441
+ var version = "0.1.0";
2442
+ function editCatalog(catalog, patch, remove) {
2443
+ if (!patch && !remove) return;
2444
+ const entries = new Map(Object.entries(catalog).map(([key, definition]) => [key, { ...definition, id: key }]));
2445
+ applyPatches(entries, patch);
2446
+ removeIds(entries, remove);
2447
+ for (const key of Object.keys(catalog)) {
2448
+ const edited = entries.get(key);
2449
+ if (!edited) delete catalog[key];
2450
+ else if (patch && key in patch) {
2451
+ const hadId = "id" in catalog[key];
2452
+ const next = { ...edited };
2453
+ if (!hadId) delete next.id;
2454
+ catalog[key] = next;
2455
+ }
2456
+ }
2457
+ }
2458
+ function buildEngine(scope) {
2459
+ const scoped = (fn) => ((...args) => withRegistries(scope, () => fn(...args)));
2460
+ return {
2461
+ version,
2462
+ generate: scoped(generate),
2463
+ slots: scoped(slots),
2464
+ validate: scoped(validate),
2465
+ palette: scoped(palette),
2466
+ limitsOf: scoped(limitsOf),
2467
+ listTemplates: scoped(listTemplates),
2468
+ listStates: scoped(listStates),
2469
+ // content registration
2470
+ registerItem: scoped(registerItem),
2471
+ registerItems: scoped(registerItems),
2472
+ registerLibrary: scoped(registerLibrary),
2473
+ registerLibraryAlias: scoped(registerLibraryAlias),
2474
+ registerTemplate: scoped(registerTemplate),
2475
+ registerStyle: scoped(registerStyle),
2476
+ registerPaletteStyle: scoped(registerPaletteStyle),
2477
+ registerRoomColors: scoped(registerRoomColors),
2478
+ registerDecorProfile: scoped(registerDecorProfile),
2479
+ registerPass: scoped(registerPass),
2480
+ registerGroundOverlay: scoped(registerGroundOverlay),
2481
+ registerWallTop: scoped(registerWallTop),
2482
+ registerSeedKeys: scoped(registerSeedKeys),
2483
+ registerState: scoped(registerState),
2484
+ setStateDistribution: scoped(setStateDistribution),
2485
+ registerLocale: scoped(registerLocale),
2486
+ registerResultExtension: scoped(registerResultExtension),
2487
+ registerFindsProvider: scoped(registerFindsProvider),
2488
+ findsFor: scoped(findsFor),
2489
+ findsAnswer: scoped(findsAnswer),
2490
+ registerViewerLayer: scoped(registerViewerLayer),
2491
+ viewerLayers: scope.VIEWER_LAYERS,
2492
+ blocksWindow: scoped(blocksWindow),
2493
+ // zoning
2494
+ defineZone: scoped(defineZone),
2495
+ getZone: scoped(getZone),
2496
+ listZones: scoped(listZones),
2497
+ checkLot: scoped(checkLot),
2498
+ registerPermeableGround: scoped(registerPermeableGround),
2499
+ // kits and data
2500
+ slotKit,
2501
+ translate: scoped(translate),
2502
+ listLocales: scoped(listLocales),
2503
+ templates: scope.TEMPLATES,
2504
+ catalog: scope.CATALOG,
2505
+ libraries: scope.LIBRARIES,
2506
+ styles: scope.STYLES,
2507
+ paletteStyles: scope.PALETTE_STYLES,
2508
+ createRng,
2509
+ mulberry32,
2510
+ hashString,
2511
+ overlaps,
2512
+ windowZone,
2513
+ openingZones,
2514
+ color: { lstar, hex: hexString, hsl: intToHsl, HIGH_SATURATION, MIN_WALL_FLOOR_CONTRAST, MIN_FABRIC_FLOOR_CONTRAST },
2515
+ constants: { WALL_THICKNESS, DOOR_CLEARANCE, PLAYER_RADIUS, DOOR_EDGE_CLEARANCE, WINDOW_EDGE_CLEARANCE, OVERHEAD_TOP, CAR_CLEARANCE }
2516
+ };
2517
+ }
2518
+ function seedBase(scope) {
2519
+ withRegistries(scope, () => {
2520
+ registerGroundItems();
2521
+ registerPermeableGround("lawn", "dirt");
2522
+ registerLocale("en", en_default);
2523
+ registerLocale("pt-BR", pt_BR_default);
2524
+ });
2525
+ }
2526
+ function createFloorplan(options = {}) {
2527
+ const scope = createRegistries();
2528
+ seedBase(scope);
2529
+ const instance = buildEngine(scope);
2530
+ for (const pack of options.packs ?? []) (typeof pack === "function" ? pack : pack.register)(instance);
2531
+ editCatalog(scope.CATALOG, options.patch?.items, options.remove?.items);
2532
+ editCatalog(scope.TEMPLATES, options.patch?.templates, options.remove?.templates);
2533
+ return instance;
2534
+ }
2535
+ var defaultEngine;
2536
+ function defaultFloorplan() {
2537
+ if (defaultEngine) return defaultEngine;
2538
+ seedBase(getDefaultRegistries());
2539
+ defaultEngine = buildEngine(getDefaultRegistries());
2540
+ return defaultEngine;
2541
+ }
2542
+
2417
2543
  // src/silhouettes/random.ts
2418
2544
  function createSilhouetteRandom(seed) {
2419
2545
  let state = seed;
@@ -2623,68 +2749,7 @@ function createDetachedHouse(p) {
2623
2749
  }
2624
2750
 
2625
2751
  // src/index.ts
2626
- registerGroundItems();
2627
- registerPermeableGround("lawn", "dirt");
2628
- registerLocale("en", en_default);
2629
- registerLocale("pt-BR", pt_BR_default);
2630
- var version = "0.1.0";
2631
- var engine = {
2632
- version,
2633
- generate,
2634
- slots,
2635
- validate,
2636
- palette,
2637
- limitsOf,
2638
- listTemplates,
2639
- listStates,
2640
- // content registration
2641
- registerItem,
2642
- registerItems,
2643
- registerLibrary,
2644
- registerLibraryAlias,
2645
- registerTemplate,
2646
- registerStyle,
2647
- registerPaletteStyle,
2648
- registerRoomColors,
2649
- registerDecorProfile,
2650
- registerPass,
2651
- registerGroundOverlay,
2652
- registerWallTop,
2653
- registerSeedKeys,
2654
- registerState,
2655
- setStateDistribution,
2656
- registerLocale,
2657
- registerResultExtension,
2658
- registerFindsProvider,
2659
- findsFor,
2660
- findsAnswer,
2661
- registerViewerLayer,
2662
- viewerLayers: VIEWER_LAYERS,
2663
- blocksWindow,
2664
- // zoning
2665
- defineZone,
2666
- getZone,
2667
- listZones,
2668
- checkLot,
2669
- registerPermeableGround,
2670
- // kits and data
2671
- slotKit,
2672
- translate,
2673
- listLocales,
2674
- templates: TEMPLATES,
2675
- catalog: CATALOG,
2676
- libraries: LIBRARIES,
2677
- styles: STYLES,
2678
- paletteStyles: PALETTE_STYLES,
2679
- createRng,
2680
- mulberry32,
2681
- hashString,
2682
- overlaps,
2683
- windowZone,
2684
- openingZones,
2685
- color: { lstar, hex: hexString, hsl: intToHsl, HIGH_SATURATION, MIN_WALL_FLOOR_CONTRAST, MIN_FABRIC_FLOOR_CONTRAST },
2686
- constants: { WALL_THICKNESS, DOOR_CLEARANCE, PLAYER_RADIUS, DOOR_EDGE_CLEARANCE, WINDOW_EDGE_CLEARANCE, OVERHEAD_TOP, CAR_CLEARANCE }
2687
- };
2752
+ var engine = defaultFloorplan();
2688
2753
  export {
2689
2754
  blocksWindow,
2690
2755
  boxRadius,
@@ -2695,11 +2760,13 @@ export {
2695
2760
  createCompanyHouse,
2696
2761
  createDetachedHouse,
2697
2762
  createFarmhouse,
2763
+ createFloorplan,
2698
2764
  createRng,
2699
2765
  createSilhouetteRandom,
2700
2766
  createSlabHouse,
2701
2767
  createStiltHouse,
2702
2768
  createTwoStoreyHouse,
2769
+ defaultFloorplan,
2703
2770
  defineZone,
2704
2771
  engine,
2705
2772
  findsAnswer,