@adatechnology/game-floorplan 0.1.0 → 0.2.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/MIGRATION.md +32 -0
- package/dist/index.d.ts +446 -62
- package/dist/index.global.js +847 -214
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +847 -214
- package/dist/index.js.map +1 -1
- package/package.json +5 -2
package/dist/index.js
CHANGED
|
@@ -4,6 +4,227 @@ var MARGIN = WALL_THICKNESS / 2;
|
|
|
4
4
|
var EPS = 1e-6;
|
|
5
5
|
var TOL = 1e-3;
|
|
6
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
|
+
// 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" };
|
|
49
|
+
function registerItem(def) {
|
|
50
|
+
const h = def.h;
|
|
51
|
+
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;
|
|
53
|
+
return item;
|
|
54
|
+
}
|
|
55
|
+
function registerItems(list) {
|
|
56
|
+
for (const d of list) registerItem(d);
|
|
57
|
+
}
|
|
58
|
+
function registerLibrary(id, library) {
|
|
59
|
+
LIBRARIES[id] = library;
|
|
60
|
+
}
|
|
61
|
+
function registerLibraryAlias(alias, id) {
|
|
62
|
+
LIBRARY_ALIAS[alias] = id;
|
|
63
|
+
}
|
|
64
|
+
function registerTemplate(t) {
|
|
65
|
+
TEMPLATES[t.id] = t;
|
|
66
|
+
return t;
|
|
67
|
+
}
|
|
68
|
+
function registerStyle(style) {
|
|
69
|
+
STYLES[style.id] = style;
|
|
70
|
+
for (const a of style.aliases || []) STYLE_ALIAS[a] = style.id;
|
|
71
|
+
}
|
|
72
|
+
function registerPaletteStyle(id, def) {
|
|
73
|
+
PALETTE_STYLES[id] = def;
|
|
74
|
+
}
|
|
75
|
+
function registerRoomColors(list) {
|
|
76
|
+
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);
|
|
80
|
+
}
|
|
81
|
+
ROOM_COLORS.sort((a, b) => a.order - b.order);
|
|
82
|
+
}
|
|
83
|
+
function registerDecorProfile(styleId, profile) {
|
|
84
|
+
DECOR_PROFILES[styleId] = profile;
|
|
85
|
+
}
|
|
86
|
+
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 });
|
|
90
|
+
}
|
|
91
|
+
function registerGroundOverlay(rule) {
|
|
92
|
+
GROUND_OVERLAYS.push(rule);
|
|
93
|
+
}
|
|
94
|
+
function registerWallTop(id, height) {
|
|
95
|
+
WALL_TOP_HEIGHTS[id] = height;
|
|
96
|
+
}
|
|
97
|
+
function registerSeedKeys(keys) {
|
|
98
|
+
Object.assign(SEED_KEYS.rooms, keys.rooms);
|
|
99
|
+
Object.assign(SEED_KEYS.items, keys.items);
|
|
100
|
+
}
|
|
101
|
+
function registerState(name, plugin) {
|
|
102
|
+
STATE_PLUGINS[name] = plugin;
|
|
103
|
+
}
|
|
104
|
+
function setStateDistribution(weights, seedKey = "state") {
|
|
105
|
+
stateDistribution.weights = weights;
|
|
106
|
+
stateDistribution.seedKey = seedKey;
|
|
107
|
+
}
|
|
108
|
+
var listStates = () => Object.keys(STATE_PLUGINS);
|
|
109
|
+
function resolveStyleId(id) {
|
|
110
|
+
const s = id ?? "";
|
|
111
|
+
return STYLE_ALIAS[s] ?? s;
|
|
112
|
+
}
|
|
113
|
+
function styleOf(id) {
|
|
114
|
+
return STYLES[id] || STYLES.budget || Object.values(STYLES)[0];
|
|
115
|
+
}
|
|
116
|
+
var RESULT_EXTENSIONS = [];
|
|
117
|
+
var FINDS_PROVIDERS = [];
|
|
118
|
+
function registerResultExtension(name, run, options = {}) {
|
|
119
|
+
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);
|
|
123
|
+
}
|
|
124
|
+
function registerFindsProvider(provider) {
|
|
125
|
+
FINDS_PROVIDERS.push(provider);
|
|
126
|
+
}
|
|
127
|
+
var VIEWER_LAYERS = [];
|
|
128
|
+
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);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// src/rng.ts
|
|
135
|
+
function mulberry32(a) {
|
|
136
|
+
return function() {
|
|
137
|
+
a |= 0;
|
|
138
|
+
a = a + 1831565813 | 0;
|
|
139
|
+
let t = Math.imul(a ^ a >>> 15, 1 | a);
|
|
140
|
+
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
|
|
141
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function hashString(input) {
|
|
145
|
+
const s = String(input);
|
|
146
|
+
let h = 1779033703 ^ s.length;
|
|
147
|
+
for (let i = 0; i < s.length; i++) {
|
|
148
|
+
h = Math.imul(h ^ s.charCodeAt(i), 3432918353);
|
|
149
|
+
h = h << 13 | h >>> 19;
|
|
150
|
+
}
|
|
151
|
+
h = Math.imul(h ^ h >>> 16, 2246822507);
|
|
152
|
+
h = Math.imul(h ^ h >>> 13, 3266489909);
|
|
153
|
+
return (h ^ h >>> 16) >>> 0;
|
|
154
|
+
}
|
|
155
|
+
var createRng = (seed, key) => mulberry32(hashString(seed + "|" + key));
|
|
156
|
+
function pickWeighted(rng, weights) {
|
|
157
|
+
const keys = Object.keys(weights);
|
|
158
|
+
let total = 0;
|
|
159
|
+
for (const k of keys) total += weights[k];
|
|
160
|
+
let x = rng() * total;
|
|
161
|
+
for (const k of keys) {
|
|
162
|
+
x -= weights[k];
|
|
163
|
+
if (x < 0) return k;
|
|
164
|
+
}
|
|
165
|
+
return keys[keys.length - 1];
|
|
166
|
+
}
|
|
167
|
+
function weightedOrder(candidates, rng) {
|
|
168
|
+
const arr = candidates.slice();
|
|
169
|
+
const out = [];
|
|
170
|
+
while (arr.length) {
|
|
171
|
+
let total = 0;
|
|
172
|
+
for (const c of arr) total += c.weight;
|
|
173
|
+
let x = rng() * total;
|
|
174
|
+
let i = 0;
|
|
175
|
+
for (; i < arr.length - 1; i++) {
|
|
176
|
+
x -= arr[i].weight;
|
|
177
|
+
if (x < 0) break;
|
|
178
|
+
}
|
|
179
|
+
out.push(arr.splice(i, 1)[0]);
|
|
180
|
+
}
|
|
181
|
+
return out;
|
|
182
|
+
}
|
|
183
|
+
function shuffle(arr, rng) {
|
|
184
|
+
const a = arr.slice();
|
|
185
|
+
for (let i = a.length - 1; i > 0; i--) {
|
|
186
|
+
const j = Math.floor(rng() * (i + 1));
|
|
187
|
+
[a[i], a[j]] = [a[j], a[i]];
|
|
188
|
+
}
|
|
189
|
+
return a;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// src/extensions.ts
|
|
193
|
+
function orderOf(extensions) {
|
|
194
|
+
const ordered = [];
|
|
195
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
196
|
+
const visit = (extension) => {
|
|
197
|
+
if (ordered.includes(extension)) return;
|
|
198
|
+
if (visiting.has(extension.name)) throw new Error("Result extensions depend on each other: " + extension.name);
|
|
199
|
+
visiting.add(extension.name);
|
|
200
|
+
for (const name of extension.after) {
|
|
201
|
+
const before = extensions.find((x) => x.name === name);
|
|
202
|
+
if (before) visit(before);
|
|
203
|
+
}
|
|
204
|
+
visiting.delete(extension.name);
|
|
205
|
+
ordered.push(extension);
|
|
206
|
+
};
|
|
207
|
+
extensions.forEach(visit);
|
|
208
|
+
return ordered;
|
|
209
|
+
}
|
|
210
|
+
function runResultExtensions(plan, result, seed, options) {
|
|
211
|
+
for (const extension of orderOf(RESULT_EXTENSIONS)) {
|
|
212
|
+
result[extension.name] = extension.run({ plan, result, seed, rng: createRng(seed, extension.seedKey), options });
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
function findsFor(query) {
|
|
216
|
+
return answerOf(query) ?? [];
|
|
217
|
+
}
|
|
218
|
+
function findsAnswer(query) {
|
|
219
|
+
return answerOf(query);
|
|
220
|
+
}
|
|
221
|
+
function answerOf(query) {
|
|
222
|
+
for (const provider of FINDS_PROVIDERS) {
|
|
223
|
+
const stacks = provider.findsFor(query);
|
|
224
|
+
if (stacks !== null) return stacks;
|
|
225
|
+
}
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
7
228
|
|
|
8
229
|
// src/geometry.ts
|
|
9
230
|
var r2 = (x) => Math.round(x * 100) / 100;
|
|
@@ -106,13 +327,39 @@ function normalizeRoom(c, floorIndex) {
|
|
|
106
327
|
};
|
|
107
328
|
}
|
|
108
329
|
function usableParts(room, extra) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
);
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
330
|
+
const m = MARGIN + (extra || 0);
|
|
331
|
+
const out = [];
|
|
332
|
+
for (const p of room.parts) {
|
|
333
|
+
out.push([p[0] + m, p[1] + m, p[2] - m, p[3] - m]);
|
|
334
|
+
for (let k = 0; k < 4; k++) {
|
|
335
|
+
const spans = [];
|
|
336
|
+
for (const q of room.parts) {
|
|
337
|
+
if (q === p) continue;
|
|
338
|
+
if (k === 0 && eq(q[2], p[0])) spans.push([Math.max(q[1], p[1]), Math.min(q[3], p[3])]);
|
|
339
|
+
else if (k === 2 && eq(q[0], p[2])) spans.push([Math.max(q[1], p[1]), Math.min(q[3], p[3])]);
|
|
340
|
+
else if (k === 1 && eq(q[3], p[1])) spans.push([Math.max(q[0], p[0]), Math.min(q[2], p[2])]);
|
|
341
|
+
else if (k === 3 && eq(q[1], p[3])) spans.push([Math.max(q[0], p[0]), Math.min(q[2], p[2])]);
|
|
342
|
+
}
|
|
343
|
+
spans.sort((x, y) => x[0] - y[0]);
|
|
344
|
+
const merged = [];
|
|
345
|
+
for (const span of spans) {
|
|
346
|
+
const last = merged[merged.length - 1];
|
|
347
|
+
if (last && span[0] <= last[1] + TOL) last[1] = Math.max(last[1], span[1]);
|
|
348
|
+
else merged.push([span[0], span[1]]);
|
|
349
|
+
}
|
|
350
|
+
for (const [a, b] of merged) {
|
|
351
|
+
if (b - a <= 0.3) continue;
|
|
352
|
+
const x0 = a + m;
|
|
353
|
+
const x1 = b - m;
|
|
354
|
+
if (x1 <= x0) continue;
|
|
355
|
+
if (k === 0) out.push([p[0], x0, p[0] + m, x1]);
|
|
356
|
+
else if (k === 2) out.push([p[2] - m, x0, p[2], x1]);
|
|
357
|
+
else if (k === 1) out.push([x0, p[1], x1, p[1] + m]);
|
|
358
|
+
else out.push([x0, p[3] - m, x1, p[3]]);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return out;
|
|
116
363
|
}
|
|
117
364
|
var inRect = (room, u, v) => room.parts.some((p) => u > p[0] + EPS && u < p[2] - EPS && v > p[1] + EPS && v < p[3] - EPS);
|
|
118
365
|
function buildWalls(rooms, doors, openPairs, floorIndex) {
|
|
@@ -167,7 +414,7 @@ function buildWalls(rooms, doors, openPairs, floorIndex) {
|
|
|
167
414
|
pieces = n;
|
|
168
415
|
}
|
|
169
416
|
for (const [x, y] of pieces) {
|
|
170
|
-
const isOpen = (t) => t === "balcony" || t === "terrace";
|
|
417
|
+
const isOpen = (t) => t === "balcony" || t === "terrace" || t === "porch";
|
|
171
418
|
const isVoid = typeOf[s.A] === "void" || typeOf[s.B] === "void";
|
|
172
419
|
if (isVoid && (s.A === "outside" || s.B === "outside")) continue;
|
|
173
420
|
const railing = isVoid || isOpen(typeOf[s.A]) && s.B === "outside" || isOpen(typeOf[s.B]) && s.A === "outside";
|
|
@@ -190,6 +437,39 @@ function buildWalls(rooms, doors, openPairs, floorIndex) {
|
|
|
190
437
|
return out;
|
|
191
438
|
}
|
|
192
439
|
|
|
440
|
+
// src/parking.ts
|
|
441
|
+
var STEP = 0.05;
|
|
442
|
+
var MAX_CANDIDATES = 80;
|
|
443
|
+
function carCandidates(slot, item, car) {
|
|
444
|
+
const front = [slot.frontU, slot.frontV];
|
|
445
|
+
const zone = slot._cell.R;
|
|
446
|
+
const alongV = Math.abs(front[1]) === 1;
|
|
447
|
+
const [extentU, extentV] = extent([item.w, item.d], front);
|
|
448
|
+
const across = alongV ? extentU : extentV;
|
|
449
|
+
const along = alongV ? extentV : extentU;
|
|
450
|
+
const a = alongV ? 0 : 1;
|
|
451
|
+
const b = alongV ? 1 : 0;
|
|
452
|
+
const driverSign = alongV ? front[1] : -front[0];
|
|
453
|
+
const frontSign = alongV ? front[1] : front[0];
|
|
454
|
+
const lowClear = driverSign < 0 ? car.driver : car.side;
|
|
455
|
+
const highClear = driverSign < 0 ? car.side : car.driver;
|
|
456
|
+
const lo = zone[a] + lowClear;
|
|
457
|
+
const hi = zone[a + 2] - highClear - across;
|
|
458
|
+
const alongLo = zone[b] + car.end;
|
|
459
|
+
const alongHi = zone[b + 2] - car.end - along;
|
|
460
|
+
if (hi < lo - 1e-6 || alongHi < alongLo - 1e-6) return [];
|
|
461
|
+
const preferred = (alongV ? slot.x : slot.z) - across / 2;
|
|
462
|
+
const alongStart = frontSign < 0 ? alongHi : alongLo;
|
|
463
|
+
const positions = [];
|
|
464
|
+
for (let p = lo; p <= hi + 1e-6; p += STEP) positions.push(Math.min(p, hi));
|
|
465
|
+
positions.sort((x, y) => Math.abs(x - preferred) - Math.abs(y - preferred));
|
|
466
|
+
return positions.slice(0, MAX_CANDIDATES).map((p) => {
|
|
467
|
+
const rect = alongV ? [p, alongStart, p + across, alongStart + along] : [alongStart, p, alongStart + along, p + across];
|
|
468
|
+
const clear = alongV ? [p - lowClear, alongStart - car.end, p + across + highClear, alongStart + along + car.end] : [alongStart - car.end, p - lowClear, alongStart + along + car.end, p + across + highClear];
|
|
469
|
+
return { rect, clear };
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
|
|
193
473
|
// src/queries.ts
|
|
194
474
|
function zonesOfFloor(plan, floorIndex) {
|
|
195
475
|
const out = [];
|
|
@@ -199,6 +479,7 @@ function zonesOfFloor(plan, floorIndex) {
|
|
|
199
479
|
for (const p of plan.doors) if (p.floor === 0 && p.exterior) out.push(...p.zones);
|
|
200
480
|
for (const g of plan.gates) out.push(...g.zones);
|
|
201
481
|
for (const c of plan.paths) out.push(...c.rects);
|
|
482
|
+
out.push(...plan.reservations);
|
|
202
483
|
}
|
|
203
484
|
return out;
|
|
204
485
|
}
|
|
@@ -220,7 +501,10 @@ function windowZone(j, outside) {
|
|
|
220
501
|
if (j.axis === "u") return n[1] > 0 ? [u - h, v - MARGIN, u + h, v + depth] : [u - h, v - depth, u + h, v + MARGIN];
|
|
221
502
|
return n[0] > 0 ? [u - MARGIN, v - h, u + depth, v + h] : [u - depth, v - h, u + MARGIN, v + h];
|
|
222
503
|
}
|
|
223
|
-
|
|
504
|
+
function blocksWindow(it, win, overheadOff) {
|
|
505
|
+
const height = Math.max(it.h, it.windowHeight || 0, it.overhead && !overheadOff ? OVERHEAD_TOP : 0);
|
|
506
|
+
return !it.window || it.h > 1 + EPS || height > win.y0 - 0.05 + EPS;
|
|
507
|
+
}
|
|
224
508
|
function windowsOfRoom(plan, room) {
|
|
225
509
|
if (!room.outdoor) return plan.windows.filter((j) => j.room === room.id);
|
|
226
510
|
return plan.windows.filter(
|
|
@@ -245,8 +529,22 @@ function isOpenSide(plan, room, pu, pv) {
|
|
|
245
529
|
}
|
|
246
530
|
|
|
247
531
|
// src/grid.ts
|
|
248
|
-
function
|
|
249
|
-
const
|
|
532
|
+
function wallBodies(plan, floor) {
|
|
533
|
+
const cache = plan.wallBodies = plan.wallBodies || {};
|
|
534
|
+
if (cache[floor]) return cache[floor];
|
|
535
|
+
const doors = plan.doors.filter((p) => p.floor === floor);
|
|
536
|
+
return cache[floor] = plan.walls.filter((p) => p.floor === floor && (p.type === "wall" || p.type === "railing")).map((p) => {
|
|
537
|
+
const a0 = p.axis === "u" ? p.u0 : p.v0;
|
|
538
|
+
const a1 = p.axis === "u" ? p.u1 : p.v1;
|
|
539
|
+
const pos = p.axis === "u" ? p.v0 : p.u0;
|
|
540
|
+
const atDoor = (x) => doors.some((q) => q.axis === p.axis && eq(q.pos, pos) && (eq(q.at - q.width / 2, x) || eq(q.at + q.width / 2, x)));
|
|
541
|
+
const e0 = atDoor(a0) ? 0 : MARGIN;
|
|
542
|
+
const e1 = atDoor(a1) ? 0 : MARGIN;
|
|
543
|
+
return p.axis === "u" ? [a0 - e0, pos - MARGIN, a1 + e1, pos + MARGIN] : [pos - MARGIN, a0 - e0, pos + MARGIN, a1 + e1];
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
function createGrid(plan, room, ignored, radius = plan.playerRadius) {
|
|
547
|
+
const gs = room.outdoor ? 0.3 : 0.1;
|
|
250
548
|
let u0 = 1e9;
|
|
251
549
|
let v0 = 1e9;
|
|
252
550
|
let u1 = -1e9;
|
|
@@ -260,12 +558,18 @@ function createGrid(plan, room, ignored) {
|
|
|
260
558
|
const nx = Math.ceil((u1 - u0) / gs);
|
|
261
559
|
const nz = Math.ceil((v1 - v0) / gs);
|
|
262
560
|
const insideCells = new Uint8Array(nx * nz);
|
|
263
|
-
const insets = usableParts(room, 0.08);
|
|
561
|
+
const insets = room.outdoor ? usableParts(room, 0.08) : room.parts;
|
|
562
|
+
const bodies = room.outdoor ? null : wallBodies(plan, room.floor);
|
|
563
|
+
const tooClose = (u, v) => bodies.some((r) => {
|
|
564
|
+
const dx = Math.max(r[0] - u, 0, u - r[2]);
|
|
565
|
+
const dz = Math.max(r[1] - v, 0, v - r[3]);
|
|
566
|
+
return dx * dx + dz * dz < radius * radius - 1e-9;
|
|
567
|
+
});
|
|
264
568
|
for (let j = 0; j < nz; j++)
|
|
265
569
|
for (let i = 0; i < nx; i++) {
|
|
266
570
|
const u = u0 + (i + 0.5) * gs;
|
|
267
571
|
const v = v0 + (j + 0.5) * gs;
|
|
268
|
-
if (insets.some((r) => u >= r[0] && u <= r[2] && v >= r[1] && v <= r[3])) insideCells[j * nx + i] = 1;
|
|
572
|
+
if (insets.some((r) => u >= r[0] && u <= r[2] && v >= r[1] && v <= r[3]) && (room.outdoor || !tooClose(u, v))) insideCells[j * nx + i] = 1;
|
|
269
573
|
}
|
|
270
574
|
const entries = [];
|
|
271
575
|
for (const ab of openingsOfFloor(plan, room.floor))
|
|
@@ -276,6 +580,22 @@ function createGrid(plan, room, ignored) {
|
|
|
276
580
|
const j = Math.floor((pt[1] - v0) / gs);
|
|
277
581
|
if (i >= 0 && j >= 0 && i < nx && j < nz && insideCells[j * nx + i]) entries.push([i, j]);
|
|
278
582
|
}
|
|
583
|
+
if (!room.outdoor)
|
|
584
|
+
for (const pair of plan.openPairs[room.floor] || []) {
|
|
585
|
+
const otherId = pair[0] === room.id ? pair[1] : pair[1] === room.id ? pair[0] : null;
|
|
586
|
+
const other = otherId && plan.rooms.find((c) => c.id === otherId && c.floor === room.floor);
|
|
587
|
+
if (!other) continue;
|
|
588
|
+
const edge = sharedEdge(room, other, null);
|
|
589
|
+
if (!edge || edge.a1 - edge.a0 > 2) continue;
|
|
590
|
+
const mid = (edge.a0 + edge.a1) / 2;
|
|
591
|
+
for (const offset of [-0.4, 0.4]) {
|
|
592
|
+
const pt = edge.axis === "u" ? [mid, edge.pos + offset] : [edge.pos + offset, mid];
|
|
593
|
+
if (!inRect(room, pt[0], pt[1])) continue;
|
|
594
|
+
const i = Math.floor((pt[0] - u0) / gs);
|
|
595
|
+
const j = Math.floor((pt[1] - v0) / gs);
|
|
596
|
+
if (i >= 0 && j >= 0 && i < nx && j < nz && insideCells[j * nx + i]) entries.push([i, j]);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
279
599
|
const R = inner(room.parts[0]);
|
|
280
600
|
return { gs, u0, v0, nx, nz, inside: insideCells, entries, center: [(R[0] + R[2]) / 2, (R[1] + R[3]) / 2] };
|
|
281
601
|
}
|
|
@@ -335,154 +655,12 @@ function reachable(g, solids, inflation) {
|
|
|
335
655
|
return true;
|
|
336
656
|
}
|
|
337
657
|
|
|
338
|
-
// src/registry.ts
|
|
339
|
-
var CATALOG = {};
|
|
340
|
-
var LIBRARIES = {};
|
|
341
|
-
var LIBRARY_ALIAS = {};
|
|
342
|
-
var TEMPLATES = {};
|
|
343
|
-
var STYLES = {};
|
|
344
|
-
var STYLE_ALIAS = {};
|
|
345
|
-
var PALETTE_STYLES = {};
|
|
346
|
-
var ROOM_COLORS = [];
|
|
347
|
-
var PASSES = [];
|
|
348
|
-
var DECOR_PROFILES = {};
|
|
349
|
-
var GROUND_OVERLAYS = [];
|
|
350
|
-
var STATE_PLUGINS = { normal: { apply: () => [] } };
|
|
351
|
-
var WALL_TOP_HEIGHTS = { none: 0 };
|
|
352
|
-
var SEED_KEYS = { rooms: {}, items: {} };
|
|
353
|
-
var stateDistribution = { weights: null, seedKey: "state" };
|
|
354
|
-
function registerItem(def) {
|
|
355
|
-
const h = def.h;
|
|
356
|
-
const item = Object.assign({ front: "+z", window: h <= 0.95, low: h <= 0.5, flat: false, deck: false, rim: 0 }, def);
|
|
357
|
-
CATALOG[def.type] = item;
|
|
358
|
-
return item;
|
|
359
|
-
}
|
|
360
|
-
function registerItems(list) {
|
|
361
|
-
for (const d of list) registerItem(d);
|
|
362
|
-
}
|
|
363
|
-
function registerLibrary(id, library) {
|
|
364
|
-
LIBRARIES[id] = library;
|
|
365
|
-
}
|
|
366
|
-
function registerLibraryAlias(alias, id) {
|
|
367
|
-
LIBRARY_ALIAS[alias] = id;
|
|
368
|
-
}
|
|
369
|
-
function registerTemplate(t) {
|
|
370
|
-
TEMPLATES[t.id] = t;
|
|
371
|
-
return t;
|
|
372
|
-
}
|
|
373
|
-
function registerStyle(style) {
|
|
374
|
-
STYLES[style.id] = style;
|
|
375
|
-
for (const a of style.aliases || []) STYLE_ALIAS[a] = style.id;
|
|
376
|
-
}
|
|
377
|
-
function registerPaletteStyle(id, def) {
|
|
378
|
-
PALETTE_STYLES[id] = def;
|
|
379
|
-
}
|
|
380
|
-
function registerRoomColors(list) {
|
|
381
|
-
for (const d of list) {
|
|
382
|
-
const i = ROOM_COLORS.findIndex((x) => x.type === d.type);
|
|
383
|
-
if (i >= 0) ROOM_COLORS.splice(i, 1);
|
|
384
|
-
ROOM_COLORS.push(d);
|
|
385
|
-
}
|
|
386
|
-
ROOM_COLORS.sort((a, b) => a.order - b.order);
|
|
387
|
-
}
|
|
388
|
-
function registerDecorProfile(styleId, profile) {
|
|
389
|
-
DECOR_PROFILES[styleId] = profile;
|
|
390
|
-
}
|
|
391
|
-
function registerPass(name, run) {
|
|
392
|
-
const i = PASSES.findIndex((p) => p.name === name);
|
|
393
|
-
if (i >= 0) PASSES[i] = { name, run };
|
|
394
|
-
else PASSES.push({ name, run });
|
|
395
|
-
}
|
|
396
|
-
function registerGroundOverlay(rule) {
|
|
397
|
-
GROUND_OVERLAYS.push(rule);
|
|
398
|
-
}
|
|
399
|
-
function registerWallTop(id, height) {
|
|
400
|
-
WALL_TOP_HEIGHTS[id] = height;
|
|
401
|
-
}
|
|
402
|
-
function registerSeedKeys(keys) {
|
|
403
|
-
Object.assign(SEED_KEYS.rooms, keys.rooms);
|
|
404
|
-
Object.assign(SEED_KEYS.items, keys.items);
|
|
405
|
-
}
|
|
406
|
-
function registerState(name, plugin) {
|
|
407
|
-
STATE_PLUGINS[name] = plugin;
|
|
408
|
-
}
|
|
409
|
-
function setStateDistribution(weights, seedKey = "state") {
|
|
410
|
-
stateDistribution.weights = weights;
|
|
411
|
-
stateDistribution.seedKey = seedKey;
|
|
412
|
-
}
|
|
413
|
-
var listStates = () => Object.keys(STATE_PLUGINS);
|
|
414
|
-
function resolveStyleId(id) {
|
|
415
|
-
const s = id ?? "";
|
|
416
|
-
return STYLE_ALIAS[s] ?? s;
|
|
417
|
-
}
|
|
418
|
-
function styleOf(id) {
|
|
419
|
-
return STYLES[id] || STYLES.budget || Object.values(STYLES)[0];
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
// src/rng.ts
|
|
423
|
-
function mulberry32(a) {
|
|
424
|
-
return function() {
|
|
425
|
-
a |= 0;
|
|
426
|
-
a = a + 1831565813 | 0;
|
|
427
|
-
let t = Math.imul(a ^ a >>> 15, 1 | a);
|
|
428
|
-
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
|
|
429
|
-
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
430
|
-
};
|
|
431
|
-
}
|
|
432
|
-
function hashString(input) {
|
|
433
|
-
const s = String(input);
|
|
434
|
-
let h = 1779033703 ^ s.length;
|
|
435
|
-
for (let i = 0; i < s.length; i++) {
|
|
436
|
-
h = Math.imul(h ^ s.charCodeAt(i), 3432918353);
|
|
437
|
-
h = h << 13 | h >>> 19;
|
|
438
|
-
}
|
|
439
|
-
h = Math.imul(h ^ h >>> 16, 2246822507);
|
|
440
|
-
h = Math.imul(h ^ h >>> 13, 3266489909);
|
|
441
|
-
return (h ^ h >>> 16) >>> 0;
|
|
442
|
-
}
|
|
443
|
-
var createRng = (seed, key) => mulberry32(hashString(seed + "|" + key));
|
|
444
|
-
function pickWeighted(rng, weights) {
|
|
445
|
-
const keys = Object.keys(weights);
|
|
446
|
-
let total = 0;
|
|
447
|
-
for (const k of keys) total += weights[k];
|
|
448
|
-
let x = rng() * total;
|
|
449
|
-
for (const k of keys) {
|
|
450
|
-
x -= weights[k];
|
|
451
|
-
if (x < 0) return k;
|
|
452
|
-
}
|
|
453
|
-
return keys[keys.length - 1];
|
|
454
|
-
}
|
|
455
|
-
function weightedOrder(candidates, rng) {
|
|
456
|
-
const arr = candidates.slice();
|
|
457
|
-
const out = [];
|
|
458
|
-
while (arr.length) {
|
|
459
|
-
let total = 0;
|
|
460
|
-
for (const c of arr) total += c.weight;
|
|
461
|
-
let x = rng() * total;
|
|
462
|
-
let i = 0;
|
|
463
|
-
for (; i < arr.length - 1; i++) {
|
|
464
|
-
x -= arr[i].weight;
|
|
465
|
-
if (x < 0) break;
|
|
466
|
-
}
|
|
467
|
-
out.push(arr.splice(i, 1)[0]);
|
|
468
|
-
}
|
|
469
|
-
return out;
|
|
470
|
-
}
|
|
471
|
-
function shuffle(arr, rng) {
|
|
472
|
-
const a = arr.slice();
|
|
473
|
-
for (let i = a.length - 1; i > 0; i--) {
|
|
474
|
-
const j = Math.floor(rng() * (i + 1));
|
|
475
|
-
[a[i], a[j]] = [a[j], a[i]];
|
|
476
|
-
}
|
|
477
|
-
return a;
|
|
478
|
-
}
|
|
479
|
-
|
|
480
658
|
// src/fill.ts
|
|
481
659
|
function candidatesOf(cats) {
|
|
482
660
|
const out = [];
|
|
483
661
|
for (const [cat, weight] of Object.entries(cats)) {
|
|
484
662
|
const exact = cat[0] === "=";
|
|
485
|
-
const its = Object.values(CATALOG).filter((i) => !i.decor && (exact ? i.type === cat.slice(1) : i.category === cat || i.type === cat));
|
|
663
|
+
const its = Object.values(CATALOG).filter((i) => !i.decor && (exact ? i.type === cat.slice(1) : !i.exactOnly && (i.category === cat || i.type === cat)));
|
|
486
664
|
for (const it of its) out.push({ it, weight: weight / its.length });
|
|
487
665
|
}
|
|
488
666
|
return out;
|
|
@@ -506,6 +684,7 @@ function itemRect(slot, it, shift) {
|
|
|
506
684
|
}
|
|
507
685
|
return [u0, v0, u0 + extentU, v0 + extentV];
|
|
508
686
|
}
|
|
687
|
+
var RELAXED_RADIUS = 0.12;
|
|
509
688
|
var MESH_FRONT = { "+z": [0, 1], "-z": [0, -1], "+x": [1, 0], "-x": [-1, 0] };
|
|
510
689
|
function rotationFor(f, front) {
|
|
511
690
|
const meshFront = MESH_FRONT[front] || [0, 1];
|
|
@@ -523,9 +702,10 @@ function fillRoom(plan, room, slots2, rng) {
|
|
|
523
702
|
const rims = [];
|
|
524
703
|
const inners = usableParts(room, 0);
|
|
525
704
|
const grid = createGrid(plan, room);
|
|
705
|
+
const relaxedGrid = room.outdoor ? grid : createGrid(plan, room, null, RELAXED_RADIUS);
|
|
526
706
|
const zones = zonesOfFloor(plan, room.floor);
|
|
527
707
|
const windowZones = windowsOfRoom(plan, room).map((j) => ({ z: windowZone(j, room.outdoor), j }));
|
|
528
|
-
const inflation = room.outdoor ? 0.15 :
|
|
708
|
+
const inflation = room.outdoor ? 0.15 : plan.playerRadius;
|
|
529
709
|
const units = [];
|
|
530
710
|
const seen = {};
|
|
531
711
|
for (const s of slots2) {
|
|
@@ -544,7 +724,7 @@ function fillRoom(plan, room, slots2, rng) {
|
|
|
544
724
|
const dependencies = (u) => u.members.flatMap((m) => [m.requires, m.on]).filter(Boolean).filter((id) => slots2.some((s) => s.id === id && !u.members.includes(s)));
|
|
545
725
|
const done = /* @__PURE__ */ new Set();
|
|
546
726
|
const ready = (u) => dependencies(u).every((id) => done.has(id));
|
|
547
|
-
const make = (s, it, r, parent) => {
|
|
727
|
+
const make = (s, it, r, parent, overheadOff) => {
|
|
548
728
|
const f = [s.frontU, s.frontV];
|
|
549
729
|
const id = room.id + "." + s.id;
|
|
550
730
|
const m = {
|
|
@@ -573,13 +753,18 @@ function fillRoom(plan, room, slots2, rng) {
|
|
|
573
753
|
_r: r,
|
|
574
754
|
_seedId: SEED_KEYS.items[id] ?? id
|
|
575
755
|
};
|
|
756
|
+
if (it.overhead) m.overhead = !overheadOff;
|
|
757
|
+
if (it.category === "car") {
|
|
758
|
+
m.driver = [f[1], -f[0]];
|
|
759
|
+
m.carClearance = plan.carClearance;
|
|
760
|
+
}
|
|
576
761
|
if (parent && s.anchor === "inside") m.y = r2(parent.y || 0);
|
|
577
762
|
if (room.link) m.link = { from: room.link[0], to: room.link[1] };
|
|
578
763
|
state[s.id] = m;
|
|
579
764
|
items.push(m);
|
|
580
765
|
return m;
|
|
581
766
|
};
|
|
582
|
-
const tryFit = (s, relax) => {
|
|
767
|
+
const tryFit = (s, relax, overheadOff) => {
|
|
583
768
|
const cands = weightedOrder(candidatesOf(s.cats), rng);
|
|
584
769
|
if (s.anchor === "on" || s.anchor === "inside") {
|
|
585
770
|
const parent = state[s.on];
|
|
@@ -602,19 +787,22 @@ function fillRoom(plan, room, slots2, rng) {
|
|
|
602
787
|
const shifts = [0];
|
|
603
788
|
for (let k = 1; k * 0.1 <= slide + 1e-9; k++) shifts.push(k * 0.1, -k * 0.1);
|
|
604
789
|
let found = null;
|
|
605
|
-
|
|
606
|
-
|
|
790
|
+
const car = it.category === "car" && !relax;
|
|
791
|
+
const tries = car ? carCandidates(s, it, plan.carClearance) : shifts.map((shift) => ({ rect: itemRect(s, it, shift) }));
|
|
792
|
+
for (const attempt of tries) {
|
|
793
|
+
const r3 = attempt.rect;
|
|
607
794
|
if (!insideUnion(r3, inners)) continue;
|
|
795
|
+
if (car && (!insideUnion(attempt.clear, inners) || solids.some((o) => overlaps(attempt.clear, o)))) continue;
|
|
608
796
|
if (it.flat) {
|
|
609
797
|
found = { rect: r3 };
|
|
610
798
|
break;
|
|
611
799
|
}
|
|
612
800
|
if (zones.some((z) => overlaps(r3, z))) continue;
|
|
613
|
-
if (windowZones.some(({ z, j }) => blocksWindow(it, j) && overlaps(r3, z))) continue;
|
|
801
|
+
if (windowZones.some(({ z, j }) => blocksWindow(it, j, overheadOff) && overlaps(r3, z))) continue;
|
|
614
802
|
if (solids.some((o) => overlaps(r3, o))) continue;
|
|
615
803
|
if (!it.deck && rims.some((a) => overlaps(r3, a))) continue;
|
|
616
|
-
let cl2 = null;
|
|
617
|
-
if (!relax && s.clearance > 0 && !it.low) {
|
|
804
|
+
let cl2 = car ? attempt.clear : null;
|
|
805
|
+
if (!car && !relax && s.clearance > 0 && !it.low) {
|
|
618
806
|
cl2 = clearRect(r3, f, s.clearance);
|
|
619
807
|
if (!insideUnion(cl2, inners)) continue;
|
|
620
808
|
if (items.some((m2) => !m2.low && !m2.flat && !m2.on && overlaps(cl2, m2._r))) continue;
|
|
@@ -625,14 +813,14 @@ function fillRoom(plan, room, slots2, rng) {
|
|
|
625
813
|
rimRect2 = [r3[0] - it.rim, r3[1] - it.rim, r3[2] + it.rim, r3[3] + it.rim];
|
|
626
814
|
if (!insideUnion(rimRect2, inners) || solids.some((o) => overlaps(rimRect2, o)) || zones.some((z) => overlaps(rimRect2, z))) continue;
|
|
627
815
|
}
|
|
628
|
-
if (!reachable(grid, solids.concat([r3]), inflation)) continue;
|
|
816
|
+
if (!(relax ? reachable(relaxedGrid, solids.concat([r3]), RELAXED_RADIUS) : reachable(grid, solids.concat([r3]), inflation))) continue;
|
|
629
817
|
found = { rect: r3, cl: cl2, rimRect: rimRect2 };
|
|
630
818
|
break;
|
|
631
819
|
}
|
|
632
820
|
if (!found) continue;
|
|
633
821
|
const { rect: r, cl, rimRect } = found;
|
|
634
822
|
if (it.flat) return make(s, it, r, null);
|
|
635
|
-
const m = make(s, it, r, null);
|
|
823
|
+
const m = make(s, it, r, null, overheadOff);
|
|
636
824
|
if (cl) reserves.push(cl);
|
|
637
825
|
if (rimRect) rims.push(rimRect);
|
|
638
826
|
solids.push(r);
|
|
@@ -655,9 +843,16 @@ function fillRoom(plan, room, slots2, rng) {
|
|
|
655
843
|
break;
|
|
656
844
|
}
|
|
657
845
|
}
|
|
846
|
+
if (!ok)
|
|
847
|
+
for (const m of applicable) {
|
|
848
|
+
if (tryFit(m, false, true)) {
|
|
849
|
+
ok = true;
|
|
850
|
+
break;
|
|
851
|
+
}
|
|
852
|
+
}
|
|
658
853
|
if (!ok && u.required)
|
|
659
854
|
for (const m of applicable) {
|
|
660
|
-
if (tryFit(m, true)) {
|
|
855
|
+
if (tryFit(m, true, true)) {
|
|
661
856
|
ok = true;
|
|
662
857
|
break;
|
|
663
858
|
}
|
|
@@ -706,6 +901,8 @@ var placementKit = {
|
|
|
706
901
|
createGrid,
|
|
707
902
|
clearRect,
|
|
708
903
|
pickWeighted,
|
|
904
|
+
createRng,
|
|
905
|
+
roomSeedKey: (room) => room.seedKey ?? SEED_KEYS.rooms[room.id] ?? room.id,
|
|
709
906
|
shuffle,
|
|
710
907
|
facingRotation: (f, type) => rotationFor(f, CATALOG[type].front),
|
|
711
908
|
cutIntervals,
|
|
@@ -769,6 +966,10 @@ function createPlacementContext(plan, items) {
|
|
|
769
966
|
const zones = zonesOfFloor(plan, room.floor);
|
|
770
967
|
const occupied = itemsOfRoom(room);
|
|
771
968
|
const windowZones = windowsOfRoom(plan, room).map((j) => ({ z: windowZone(j, room.outdoor), j }));
|
|
969
|
+
const landings = it0.flat ? [] : items.filter((m) => m.type === "stairs" && m.floor === room.floor && m.aabb).map((m) => {
|
|
970
|
+
const r = m.aabb;
|
|
971
|
+
return m.frontV === 1 ? [r[0], r[3], r[2], r[3] + 1] : m.frontV === -1 ? [r[0], r[1] - 1, r[2], r[1]] : m.frontU === 1 ? [r[2], r[1], r[2] + 1, r[3]] : [r[0] - 1, r[1], r[0], r[3]];
|
|
972
|
+
});
|
|
772
973
|
const cand = opts.cands ? opts.cands.slice() : [];
|
|
773
974
|
if (!opts.cands)
|
|
774
975
|
for (const p of inners) {
|
|
@@ -783,9 +984,9 @@ function createPlacementContext(plan, items) {
|
|
|
783
984
|
cand.push([x, z, x + extentU, z + extentV]);
|
|
784
985
|
}
|
|
785
986
|
}
|
|
786
|
-
for (const r of shuffle(cand, rng2)) {
|
|
987
|
+
for (const r of opts.ordered ? cand : shuffle(cand, rng2)) {
|
|
787
988
|
if (!insideUnion(r, inners)) continue;
|
|
788
|
-
if (zones.some((z) => overlaps(r, z))) continue;
|
|
989
|
+
if (zones.some((z) => overlaps(r, z)) || landings.some((z) => overlaps(r, z))) continue;
|
|
789
990
|
if (windowZones.some(({ z, j }) => blocksWindow(it, j) && overlaps(r, z))) continue;
|
|
790
991
|
if (solidList.some((o) => overlaps(r, o))) continue;
|
|
791
992
|
if (opts.flat) {
|
|
@@ -794,7 +995,7 @@ function createPlacementContext(plan, items) {
|
|
|
794
995
|
}
|
|
795
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;
|
|
796
997
|
if (opts.without && opts.without(r)) continue;
|
|
797
|
-
if (!reachable(opts.railing || gridOf(room), solidList.concat([r]), 0.16)) continue;
|
|
998
|
+
if (!reachable(opts.railing || gridOf(room), solidList.concat([r]), room.outdoor ? 0.16 : plan.playerRadius)) continue;
|
|
798
999
|
return r;
|
|
799
1000
|
}
|
|
800
1001
|
return null;
|
|
@@ -875,6 +1076,129 @@ function translate(ns, key, locale = "en", vars) {
|
|
|
875
1076
|
return vars ? raw.replace(/\{(\w+)\}/g, (m, k) => k in vars ? String(vars[k]) : m) : raw;
|
|
876
1077
|
}
|
|
877
1078
|
|
|
1079
|
+
// src/paths.ts
|
|
1080
|
+
var CLOSE = 1e-6;
|
|
1081
|
+
var rounded = (r) => r.map(r2);
|
|
1082
|
+
var touches = (a, r) => a[0] <= r[2] + CLOSE && a[2] >= r[0] - CLOSE && a[1] <= r[3] + CLOSE && a[3] >= r[1] - CLOSE;
|
|
1083
|
+
function addExteriorDoorPaths(plan, c) {
|
|
1084
|
+
const { gates, paths, zones, setback, marginLeft, marginRight, backyard, w } = c;
|
|
1085
|
+
const social = gates.find((g) => g.type === "pedestrian");
|
|
1086
|
+
const socialPath = social ? paths.find((p) => p.from === social.id && p.type === "pedestrian") : null;
|
|
1087
|
+
const parkingZone = zones.find((z) => z.id === "parking");
|
|
1088
|
+
const parking = parkingZone ? parkingZone.parts[0] : null;
|
|
1089
|
+
const midY = setback > 0 ? -setback / 2 : 0;
|
|
1090
|
+
const joinToSocial = (x, width, rects, points) => {
|
|
1091
|
+
if (!socialPath) return;
|
|
1092
|
+
const h = width / 2;
|
|
1093
|
+
const horizontal = socialPath.rects.find((r) => midY >= r[1] - CLOSE && midY <= r[3] + CLOSE && r[2] - r[0] > r[3] - r[1]) || socialPath.rects[0];
|
|
1094
|
+
const targetX = x < horizontal[0] ? horizontal[0] + h : x > horizontal[2] ? horizontal[2] - h : x;
|
|
1095
|
+
if (Math.abs(targetX - x) > CLOSE) {
|
|
1096
|
+
rects.push([Math.min(x, targetX) - h, midY - h, Math.max(x, targetX) + h, midY + h]);
|
|
1097
|
+
points.push([targetX, midY]);
|
|
1098
|
+
}
|
|
1099
|
+
};
|
|
1100
|
+
for (const door of plan.doors.filter((q) => q.floor === 0 && q.exterior)) {
|
|
1101
|
+
if (paths.some((p) => p.to === door.id)) continue;
|
|
1102
|
+
const half = Math.max(1, Math.min(door.width, 2)) / 2;
|
|
1103
|
+
const rects = [];
|
|
1104
|
+
const points = [];
|
|
1105
|
+
const isTarget = c.target && door.a === c.target.a && door.side === c.target.side;
|
|
1106
|
+
let fromId = social ? social.id : null;
|
|
1107
|
+
let width = 1;
|
|
1108
|
+
const id = isTarget ? "entrance_path" : "path_" + door.id;
|
|
1109
|
+
if (door.side === "front") {
|
|
1110
|
+
if (setback > 0) {
|
|
1111
|
+
const stub = Math.min(1, setback);
|
|
1112
|
+
rects.push([door.u - half, -stub, door.u + half, 0]);
|
|
1113
|
+
points.push([door.u, 0], [door.u, -stub]);
|
|
1114
|
+
if (-stub > midY) {
|
|
1115
|
+
rects.push([door.u - width / 2, midY - width / 2, door.u + width / 2, -stub]);
|
|
1116
|
+
points.push([door.u, midY]);
|
|
1117
|
+
}
|
|
1118
|
+
joinToSocial(door.u, width, rects, points);
|
|
1119
|
+
} else {
|
|
1120
|
+
rects.push([door.u - half, -1.2, door.u + half, 0]);
|
|
1121
|
+
points.push([door.u, 0], [door.u, -1.2]);
|
|
1122
|
+
}
|
|
1123
|
+
} else {
|
|
1124
|
+
const lanes = [];
|
|
1125
|
+
if (marginLeft > 0.5) lanes.push({ x: -marginLeft / 2, width: marginLeft, side: "left" });
|
|
1126
|
+
if (marginRight > 0.5) lanes.push({ x: w + marginRight / 2, width: marginRight, side: "right" });
|
|
1127
|
+
const targets = paths.flatMap((p) => p.rects).concat(parking ? [parking] : []);
|
|
1128
|
+
let best = null;
|
|
1129
|
+
for (const lane of lanes) {
|
|
1130
|
+
if (door.side === "left" && lane.side !== "left" || door.side === "right" && lane.side !== "right") continue;
|
|
1131
|
+
const laneWidth = Math.min(0.9, lane.width - 0.1);
|
|
1132
|
+
const h = laneWidth / 2;
|
|
1133
|
+
const rearStrip = Math.min(0.8, backyard);
|
|
1134
|
+
const rearY = door.v + rearStrip + laneWidth / 2;
|
|
1135
|
+
const overlapsParking = !!parking && lane.x + h > parking[0] && lane.x - h < parking[2];
|
|
1136
|
+
const frontY = overlapsParking || setback <= 0 ? 0 : -0.6;
|
|
1137
|
+
let frontStub = null;
|
|
1138
|
+
if (!overlapsParking && setback > 0) {
|
|
1139
|
+
const dir = social && social.u < lane.x ? -1 : 1;
|
|
1140
|
+
const band = [-0.6 - h, -0.6 + h];
|
|
1141
|
+
let nearestX = social ? social.u : lane.x;
|
|
1142
|
+
let nearestGap = Infinity;
|
|
1143
|
+
for (const r of targets) {
|
|
1144
|
+
if (!(r[1] <= band[1] && r[3] >= band[0])) continue;
|
|
1145
|
+
const edge = dir < 0 ? r[2] : r[0];
|
|
1146
|
+
const gap = Math.abs(edge - lane.x);
|
|
1147
|
+
if ((dir < 0 ? edge <= lane.x : edge >= lane.x) && gap < nearestGap) {
|
|
1148
|
+
nearestGap = gap;
|
|
1149
|
+
nearestX = edge;
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
frontStub = [Math.min(lane.x, nearestX) - (dir < 0 ? 0 : h), -0.6 - h, Math.max(lane.x, nearestX) + (dir < 0 ? h : 0), -0.6 + h];
|
|
1153
|
+
}
|
|
1154
|
+
const vertical = [lane.x - h, frontY - (frontY < 0 ? h : 0), lane.x + h, (door.side === "back" ? rearY : door.v) + laneWidth / 2];
|
|
1155
|
+
const horizontal = door.side === "back" ? [Math.min(door.u, lane.x) - half, rearY - laneWidth / 2, Math.max(door.u, lane.x) + h, rearY + laneWidth / 2] : null;
|
|
1156
|
+
const cost = (horizontal ? horizontal[2] - horizontal[0] : 0) + (frontStub ? frontStub[2] - frontStub[0] : 0);
|
|
1157
|
+
if (!best || cost < best.cost) best = { lane, laneWidth, half: h, vertical, horizontal, frontStub, cost, rearStrip, rearY, frontY, overlapsParking };
|
|
1158
|
+
}
|
|
1159
|
+
if (best) {
|
|
1160
|
+
const { lane, laneWidth, half: h, vertical, horizontal, frontStub, rearStrip, rearY, frontY, overlapsParking } = best;
|
|
1161
|
+
width = laneWidth;
|
|
1162
|
+
if (overlapsParking) fromId = (gates.find((g) => g.type === "garage") || social || {}).id || null;
|
|
1163
|
+
if (door.side === "back") {
|
|
1164
|
+
rects.push([door.u - half, door.v, door.u + half, door.v + rearStrip + laneWidth], horizontal, vertical);
|
|
1165
|
+
points.push([door.u, door.v], [door.u, rearY], [lane.x, rearY], [lane.x, frontY]);
|
|
1166
|
+
} else {
|
|
1167
|
+
const x0 = door.side === "left" ? lane.x - h : w;
|
|
1168
|
+
const x1 = door.side === "left" ? 0 : lane.x + h;
|
|
1169
|
+
rects.push([Math.min(x0, x1), door.v - half, Math.max(x0, x1), door.v + half], vertical);
|
|
1170
|
+
points.push([door.u, door.v], [lane.x, door.v], [lane.x, frontY]);
|
|
1171
|
+
}
|
|
1172
|
+
if (frontStub) {
|
|
1173
|
+
rects.push(frontStub);
|
|
1174
|
+
points.push([frontStub[0] < lane.x ? frontStub[0] : frontStub[2], -0.6]);
|
|
1175
|
+
}
|
|
1176
|
+
} else {
|
|
1177
|
+
const stub = 1;
|
|
1178
|
+
if (door.side === "back") rects.push([door.u - half, door.v, door.u + half, door.v + Math.min(stub, backyard)]);
|
|
1179
|
+
else rects.push(door.side === "left" ? [-Math.min(stub, marginLeft), door.v - half, 0, door.v + half] : [w, door.v - half, w + Math.min(stub, marginRight), door.v + half]);
|
|
1180
|
+
points.push([door.u, door.v]);
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
const reached = rects.slice();
|
|
1184
|
+
const pool = paths.flatMap((p) => p.rects).concat(parking ? [parking] : []);
|
|
1185
|
+
let grows = true;
|
|
1186
|
+
while (grows) {
|
|
1187
|
+
grows = false;
|
|
1188
|
+
for (const r of pool) {
|
|
1189
|
+
if (!reached.includes(r) && reached.some((a) => touches(a, r))) {
|
|
1190
|
+
reached.push(r);
|
|
1191
|
+
grows = true;
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
const inside = (g) => reached.some((r) => g.u >= r[0] - CLOSE && g.u <= r[2] + CLOSE && g.v >= r[1] - CLOSE && g.v <= r[3] + CLOSE);
|
|
1196
|
+
const gate = gates.find((q) => q.type === "pedestrian" && inside(q)) || gates.find((q) => q.type !== "cargo" && inside(q));
|
|
1197
|
+
if (gate) fromId = gate.id;
|
|
1198
|
+
paths.push({ id, type: "pedestrian", from: fromId, to: door.id, width, rects: rects.map(rounded), points: points.map((q) => q.map(r2)) });
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
|
|
878
1202
|
// src/zoning.ts
|
|
879
1203
|
var ZONES = {};
|
|
880
1204
|
var PERMEABLE_GROUNDS = /* @__PURE__ */ new Set();
|
|
@@ -1006,14 +1330,34 @@ function buildPlan(tpl, wIn, dIn, opIn, bc = {}) {
|
|
|
1006
1330
|
const styleId = resolveStyleId(op.style || (typeof tpl.style === "function" ? tpl.style(op) : tpl.style) || "budget");
|
|
1007
1331
|
const FLOOR_DEFS = floorsOf(tpl, op);
|
|
1008
1332
|
const style = styleOf(styleId);
|
|
1009
|
-
const plan = {
|
|
1333
|
+
const plan = {
|
|
1334
|
+
w,
|
|
1335
|
+
d,
|
|
1336
|
+
tpl,
|
|
1337
|
+
floorDefs: FLOOR_DEFS,
|
|
1338
|
+
style: styleId,
|
|
1339
|
+
floors: [],
|
|
1340
|
+
rooms: [],
|
|
1341
|
+
zones: [],
|
|
1342
|
+
doors: [],
|
|
1343
|
+
windows: [],
|
|
1344
|
+
walls: [],
|
|
1345
|
+
gates: [],
|
|
1346
|
+
paths: [],
|
|
1347
|
+
lot: null,
|
|
1348
|
+
openPairs: {},
|
|
1349
|
+
reservations: [],
|
|
1350
|
+
options: op,
|
|
1351
|
+
playerRadius: op.playerRadius ?? PLAYER_RADIUS,
|
|
1352
|
+
carClearance: Object.assign({}, CAR_CLEARANCE, op.carClearance)
|
|
1353
|
+
};
|
|
1010
1354
|
FLOOR_DEFS.forEach((A, floorIndex) => {
|
|
1011
1355
|
const floorNumber = tpl.singleFloor ? op.floor || 3 : floorIndex + 1;
|
|
1012
1356
|
plan.floors.push({
|
|
1013
1357
|
index: floorIndex,
|
|
1014
1358
|
name: tpl.singleFloor ? "floor_n" : A.label,
|
|
1015
1359
|
number: tpl.singleFloor ? floorNumber : void 0,
|
|
1016
|
-
y0: tpl.singleFloor ? (floorNumber - 1) * A.ceiling : floorIndex * A.ceiling,
|
|
1360
|
+
y0: tpl.singleFloor ? (floorNumber - 1) * A.ceiling : tpl.cumulativeCeilings ? FLOOR_DEFS.slice(0, floorIndex).reduce((t, q) => t + q.ceiling, 0) : floorIndex * A.ceiling,
|
|
1017
1361
|
ceiling: A.ceiling
|
|
1018
1362
|
});
|
|
1019
1363
|
plan.openPairs[floorIndex] = A.openPairs || [];
|
|
@@ -1029,7 +1373,8 @@ function buildPlan(tpl, wIn, dIn, opIn, bc = {}) {
|
|
|
1029
1373
|
const a = byId[sp.a];
|
|
1030
1374
|
if (!a) throw new Error("Door references a missing room: " + sp.a);
|
|
1031
1375
|
let edge;
|
|
1032
|
-
if (sp.
|
|
1376
|
+
if (sp.edge) edge = sp.edge;
|
|
1377
|
+
else if (sp.b === "outside") edge = exteriorEdge(a, sp.side, w, d, sp.at);
|
|
1033
1378
|
else {
|
|
1034
1379
|
const b = byId[sp.b];
|
|
1035
1380
|
if (!b) throw new Error("Door references a missing room: " + sp.b);
|
|
@@ -1037,7 +1382,7 @@ function buildPlan(tpl, wIn, dIn, opIn, bc = {}) {
|
|
|
1037
1382
|
}
|
|
1038
1383
|
if (!edge) throw new Error("Door without a wall: " + sp.a + " / " + sp.b);
|
|
1039
1384
|
const width = sp.width;
|
|
1040
|
-
const margin = width / 2 +
|
|
1385
|
+
const margin = width / 2 + (sp.clearance ?? tpl.clearance?.door ?? DOOR_EDGE_CLEARANCE);
|
|
1041
1386
|
if (edge.a1 - edge.a0 < width + 0.24) throw new Error("Wall too short for the door: " + sp.a + " / " + sp.b);
|
|
1042
1387
|
const c = clamp(sp.at != null ? sp.at : (edge.a0 + edge.a1) / 2, edge.a0 + margin, edge.a1 - margin);
|
|
1043
1388
|
const exterior = sp.b === "outside";
|
|
@@ -1059,51 +1404,101 @@ function buildPlan(tpl, wIn, dIn, opIn, bc = {}) {
|
|
|
1059
1404
|
exterior,
|
|
1060
1405
|
side: sp.side || null,
|
|
1061
1406
|
leaves: exterior && sp.steel ? 0 : width > 1.1 ? 2 : 1,
|
|
1062
|
-
model: sp.steel ? "steel" : width > 1.1 ? "double" : "single",
|
|
1407
|
+
model: sp.model || (sp.steel ? "steel" : width > 1.1 ? "double" : "single"),
|
|
1408
|
+
type: sp.type || "door",
|
|
1409
|
+
steps: sp.steps || 0,
|
|
1063
1410
|
hinge: "start",
|
|
1064
1411
|
zones: openingZones(u, v, edge.axis, width, openingDepth(width, leaf))
|
|
1065
1412
|
});
|
|
1066
1413
|
});
|
|
1067
1414
|
plan.doors.push(...floorDoors);
|
|
1068
|
-
const
|
|
1069
|
-
(A.windows(w, d, op) || []
|
|
1070
|
-
const a = byId[windowDef.room];
|
|
1071
|
-
if (!a) throw new Error("Window references a missing room: " + windowDef.room);
|
|
1072
|
-
const edge = exteriorEdge(a, windowDef.side, w, d, windowDef.at);
|
|
1073
|
-
if (!edge) return;
|
|
1074
|
-
const width = windowDef.width;
|
|
1075
|
-
const m0 = edge.a0 + width / 2 + 0.3;
|
|
1076
|
-
const m1 = edge.a1 - width / 2 - 0.3;
|
|
1077
|
-
if (m1 < m0 - TOL) return;
|
|
1078
|
-
const centers = windowDef.n ? Array.from({ length: windowDef.n }, (_, i) => edge.a0 + (edge.a1 - edge.a0) * (i + 1) / (windowDef.n + 1)) : [windowDef.at];
|
|
1079
|
-
for (const c0 of centers) {
|
|
1080
|
-
const c = clamp(c0, m0, m1);
|
|
1081
|
-
const u = edge.axis === "u" ? c : edge.pos;
|
|
1082
|
-
const v = edge.axis === "u" ? edge.pos : c;
|
|
1083
|
-
if (floorDoors.some((p) => p.axis === edge.axis && eq(p.pos, edge.pos) && Math.abs(p.at - c) < (p.width + width) / 2 + 0.15)) continue;
|
|
1084
|
-
if (windowsOut.some((j) => j.axis === edge.axis && eq(j.pos, edge.pos) && Math.abs(j.at - c) < (j.width + width) / 2 + 0.2)) continue;
|
|
1085
|
-
windowsOut.push({
|
|
1086
|
-
id: "w" + floorIndex + "_" + windowsOut.length,
|
|
1087
|
-
floor: floorIndex,
|
|
1088
|
-
u: r2(u),
|
|
1089
|
-
v: r2(v),
|
|
1090
|
-
axis: edge.axis,
|
|
1091
|
-
pos: edge.pos,
|
|
1092
|
-
at: r2(c),
|
|
1093
|
-
width,
|
|
1094
|
-
y0: windowDef.y0 || 1,
|
|
1095
|
-
y1: windowDef.y1 || 2.15,
|
|
1096
|
-
room: windowDef.room,
|
|
1097
|
-
side: windowDef.side
|
|
1098
|
-
});
|
|
1099
|
-
}
|
|
1100
|
-
});
|
|
1415
|
+
const floorWalls = (tpl.noWalls ? [] : buildWalls(list, floorDoors, A.openPairs || [], floorIndex)).map((p) => Object.assign(p, { h: p.h || A.ceiling }));
|
|
1416
|
+
const windowsOut = placeWindows({ list, byId, floorWalls, windowDefs: A.windows(w, d, op) || [], floorIndex, ceiling: A.ceiling, w, d, clearance: tpl.clearance?.window ?? WINDOW_EDGE_CLEARANCE });
|
|
1101
1417
|
plan.windows.push(...windowsOut);
|
|
1102
|
-
plan.walls.push(...
|
|
1418
|
+
plan.walls.push(...floorWalls);
|
|
1103
1419
|
});
|
|
1420
|
+
if (tpl.config) plan.config = tpl.config(op, w, d);
|
|
1421
|
+
if (tpl.reservations) tpl.reservations(plan);
|
|
1104
1422
|
buildLot(plan, tpl, w, d, op, style, styleId, bc);
|
|
1105
1423
|
return plan;
|
|
1106
1424
|
}
|
|
1425
|
+
function placeWindows(params) {
|
|
1426
|
+
const { list, byId, floorWalls, windowDefs, floorIndex, ceiling, w, d, clearance } = params;
|
|
1427
|
+
const stretches = floorWalls.filter((p) => p.exterior && p.type === "wall");
|
|
1428
|
+
const found = [];
|
|
1429
|
+
const groups = [];
|
|
1430
|
+
let order = 0;
|
|
1431
|
+
for (const def of windowDefs) {
|
|
1432
|
+
const room = byId[def.room];
|
|
1433
|
+
if (!room) throw new Error("Window references a missing room: " + def.room);
|
|
1434
|
+
const edge = exteriorEdge(room, def.side, w, d, def.at);
|
|
1435
|
+
if (!edge) continue;
|
|
1436
|
+
const centres = def.n ? Array.from({ length: def.n }, (_, i) => edge.a0 + (edge.a1 - edge.a0) * (i + 1) / (def.n + 1)) : [def.at];
|
|
1437
|
+
for (const centre of centres) {
|
|
1438
|
+
const onEdge = stretches.filter((t) => t.axis === edge.axis && eq(edge.axis === "u" ? t.v0 : t.u0, edge.pos) && t.sides.includes(def.room));
|
|
1439
|
+
const start = (t) => edge.axis === "u" ? t.u0 : t.v0;
|
|
1440
|
+
const end = (t) => edge.axis === "u" ? t.u1 : t.v1;
|
|
1441
|
+
const gap = (t) => Math.min(Math.abs(centre - start(t)), Math.abs(centre - end(t)));
|
|
1442
|
+
const target = onEdge.find((t) => centre >= start(t) - TOL && centre <= end(t) + TOL) || onEdge.slice().sort((x, y) => gap(x) - gap(y))[0];
|
|
1443
|
+
if (!target) continue;
|
|
1444
|
+
let group = groups.find((x) => x.wall === target && x.def.room === def.room);
|
|
1445
|
+
if (!group) groups.push(group = { wall: target, edge, start: start(target), end: end(target), def, entries: [] });
|
|
1446
|
+
group.entries.push({ def, centre, order: order++ });
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
for (const g of groups) {
|
|
1450
|
+
const room = byId[g.def.room];
|
|
1451
|
+
const bath = room.type === "bathroom";
|
|
1452
|
+
const length = g.end - g.start;
|
|
1453
|
+
const its = g.entries.slice().sort((x, y) => x.centre - y.centre).map((x) => ({ def: x.def, order: x.order, width: bath ? Math.min(x.def.width, 0.6) : x.def.width }));
|
|
1454
|
+
const fits = (l) => l.reduce((t, x) => t + x.width, 0) + (l.length + 1) * clearance <= length + TOL;
|
|
1455
|
+
while (its.length && !fits(its)) {
|
|
1456
|
+
const k = its.reduce((m, x, i, arr) => x.width > arr[m].width ? i : m, 0);
|
|
1457
|
+
if (its[k].width > 0.6 + TOL) its[k] = Object.assign({}, its[k], { width: Math.max(0.6, r2(its[k].width - 0.1)) });
|
|
1458
|
+
else its.pop();
|
|
1459
|
+
}
|
|
1460
|
+
if (!its.length) continue;
|
|
1461
|
+
const space = (length - its.reduce((t, x) => t + x.width, 0)) / (its.length + 1);
|
|
1462
|
+
let pos = g.start + space;
|
|
1463
|
+
for (const x of its) {
|
|
1464
|
+
const c = pos + x.width / 2;
|
|
1465
|
+
pos += x.width + space;
|
|
1466
|
+
const u = g.edge.axis === "u" ? c : g.edge.pos;
|
|
1467
|
+
const v = g.edge.axis === "u" ? g.edge.pos : c;
|
|
1468
|
+
let y0 = x.def.y0 || 1;
|
|
1469
|
+
let y1 = x.def.y1 || 2.15;
|
|
1470
|
+
if (bath) {
|
|
1471
|
+
y0 = Math.max(y0, 2.1);
|
|
1472
|
+
y1 = Math.min(Math.max(y1, y0 + 0.4), ceiling - 0.25);
|
|
1473
|
+
}
|
|
1474
|
+
found.push({ id: "", floor: floorIndex, u: r2(u), v: r2(v), axis: g.edge.axis, pos: g.edge.pos, at: r2(c), width: x.width, y0: r2(y0), y1: r2(y1), room: x.def.room, side: x.def.side, _order: x.order });
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
for (const room of list) {
|
|
1478
|
+
if (WINDOWLESS_ROOM_TYPES.has(room.type) || found.some((j) => j.room === room.id)) continue;
|
|
1479
|
+
let best = null;
|
|
1480
|
+
for (const t of stretches) {
|
|
1481
|
+
if (!t.sides.includes(room.id)) continue;
|
|
1482
|
+
const i0 = t.axis === "u" ? t.u0 : t.v0;
|
|
1483
|
+
const i1 = t.axis === "u" ? t.u1 : t.v1;
|
|
1484
|
+
const length = i1 - i0;
|
|
1485
|
+
const width = Math.min(room.type === "bathroom" ? 0.6 : 1.2, length - 2 * clearance);
|
|
1486
|
+
if (width < 0.6 - TOL || best && best.length >= length) continue;
|
|
1487
|
+
const side = t.axis === "u" ? t.sides[0] === "outside" ? "front" : "back" : t.sides[0] === "outside" ? "left" : "right";
|
|
1488
|
+
const c = (i0 + i1) / 2;
|
|
1489
|
+
const pos = t.axis === "u" ? t.v0 : t.u0;
|
|
1490
|
+
const bath = room.type === "bathroom";
|
|
1491
|
+
best = { length, window: { id: "", floor: floorIndex, u: r2(t.axis === "u" ? c : pos), v: r2(t.axis === "u" ? pos : c), axis: t.axis, pos, at: r2(c), width: r2(width), y0: bath ? 2.1 : 1, y1: bath ? Math.min(2.5, ceiling - 0.25) : 2.15, room: room.id, side, _order: 1e6 + found.length } };
|
|
1492
|
+
}
|
|
1493
|
+
if (best) found.push(best.window);
|
|
1494
|
+
}
|
|
1495
|
+
found.sort((x, y) => x._order - y._order);
|
|
1496
|
+
found.forEach((j, i) => {
|
|
1497
|
+
delete j._order;
|
|
1498
|
+
j.id = "w" + floorIndex + "_" + i;
|
|
1499
|
+
});
|
|
1500
|
+
return found;
|
|
1501
|
+
}
|
|
1107
1502
|
function buildLot(plan, tpl, w, d, op, style, styleId, bc) {
|
|
1108
1503
|
const seed = op.seed == null ? 1 : op.seed;
|
|
1109
1504
|
const rFacade = createRng(seed, STREAM.facade);
|
|
@@ -1163,9 +1558,11 @@ function buildLot(plan, tpl, w, d, op, style, styleId, bc) {
|
|
|
1163
1558
|
parts.sort((a, b) => b[2] - b[0] - (a[2] - a[0]));
|
|
1164
1559
|
addZone("garden", "garden", "garden", parts, libs.garden);
|
|
1165
1560
|
}
|
|
1166
|
-
|
|
1167
|
-
|
|
1561
|
+
const yard = cfg.yardZones;
|
|
1562
|
+
if (backDepth > 0 && yard) {
|
|
1563
|
+
for (const q of yard) addZone(q.id, q.type, q.label || q.id, [[-marginL, d + q.v0, w + marginR, d + Math.min(backDepth, q.v1)]], q.library);
|
|
1168
1564
|
} else if (backDepth > 0) addZone("backyard", "backyard", "backyard", [[-marginL, d, w + marginR, d + backDepth]], libs.backyard);
|
|
1565
|
+
for (const q of cfg.extraZones || []) addZone(q.id, q.type, q.label || q.id, q.parts, q.library);
|
|
1169
1566
|
if (marginL > 0.5) addZone("side_left", "side_yard", "side_left", [[-marginL, 0, 0, d]], libs.sideYard);
|
|
1170
1567
|
if (marginR > 0.5) addZone("side_right", "side_yard", "side_right", [[w, 0, w + marginR, d]], libs.sideYard);
|
|
1171
1568
|
const gates = [];
|
|
@@ -1223,12 +1620,14 @@ function buildLot(plan, tpl, w, d, op, style, styleId, bc) {
|
|
|
1223
1620
|
paths.push({ id: "cargo_path", type: "vehicle", from: g.id, to: pt.id, width: hw * 2, rects: [[g.u - hw, d, g.u + hw, d + backDepth].map(r2)], points: [[g.u, d + backDepth], [g.u, d]] });
|
|
1224
1621
|
}
|
|
1225
1622
|
}
|
|
1623
|
+
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 });
|
|
1226
1625
|
plan.paths = paths;
|
|
1227
1626
|
lot.paths = paths;
|
|
1228
1627
|
const boundary = (rule) => {
|
|
1229
1628
|
const type = pickWeighted(rLot, rule.type);
|
|
1230
1629
|
const top = type === "railing" ? "none" : pickWeighted(rLot, rule.top);
|
|
1231
|
-
const wallHeight = type === "railing" ? 1.2 : type === "fence" ? 2 : style.height;
|
|
1630
|
+
const wallHeight = type === "railing" ? 1.2 : type === "fence" ? style.fenceHeight ?? 2 : style.height;
|
|
1232
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 };
|
|
1233
1632
|
};
|
|
1234
1633
|
const A = { front: boundary(style.front), left: boundary(style.sideYard), right: boundary(style.sideYard), back: boundary(style.back) };
|
|
@@ -1498,7 +1897,7 @@ function resolveSlot(plan, room, def, byId, zones) {
|
|
|
1498
1897
|
}
|
|
1499
1898
|
return roomCenter;
|
|
1500
1899
|
};
|
|
1501
|
-
const free = (rect) => !zones.some((z) => overlaps(rect, z, room.type === "stairs" ? -0.011 : EPS));
|
|
1900
|
+
const free = (rect) => !!def.floorFree || !zones.some((z) => overlaps(rect, z, room.type === "stairs" ? -0.011 : EPS));
|
|
1502
1901
|
const rule = def.facing || "center";
|
|
1503
1902
|
let R = inner(room.parts[Math.min(def.part || 0, room.parts.length - 1)]);
|
|
1504
1903
|
if (def.anchor === "wall" || def.anchor === "window") {
|
|
@@ -1744,6 +2143,8 @@ function generate(template, options) {
|
|
|
1744
2143
|
};
|
|
1745
2144
|
if (tpl.singleFloor) result.floorNumber = op.floor || 3;
|
|
1746
2145
|
result.partyWalls = tpl.partyWalls || [];
|
|
2146
|
+
if (plan.config) result.config = plan.config;
|
|
2147
|
+
runResultExtensions(plan, result, seed, op);
|
|
1747
2148
|
if (zone) {
|
|
1748
2149
|
const report = { zone: zone.id, adjustments, violations: checkLot(zone, lot, Object.assign({}, result, { rooms: plan.rooms })) };
|
|
1749
2150
|
result.zoning = report;
|
|
@@ -2013,6 +2414,214 @@ var pt_BR_default = {
|
|
|
2013
2414
|
}
|
|
2014
2415
|
};
|
|
2015
2416
|
|
|
2417
|
+
// src/silhouettes/random.ts
|
|
2418
|
+
function createSilhouetteRandom(seed) {
|
|
2419
|
+
let state = seed;
|
|
2420
|
+
return () => {
|
|
2421
|
+
state |= 0;
|
|
2422
|
+
state = state + 1831565813 | 0;
|
|
2423
|
+
let t = Math.imul(state ^ state >>> 15, 1 | state);
|
|
2424
|
+
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
|
|
2425
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
2426
|
+
};
|
|
2427
|
+
}
|
|
2428
|
+
|
|
2429
|
+
// src/silhouettes/builder.ts
|
|
2430
|
+
var ROOT = -1;
|
|
2431
|
+
var MAX_BOX_RADIUS = 0.15;
|
|
2432
|
+
var BOX_RADIUS_RATIO = 0.22;
|
|
2433
|
+
var PLAIN_BOX_RADIUS = 0.012;
|
|
2434
|
+
function boxRadius(width, height, depth) {
|
|
2435
|
+
const radius = Math.min(Math.min(width, height, depth) * BOX_RADIUS_RATIO, MAX_BOX_RADIUS);
|
|
2436
|
+
return radius < PLAIN_BOX_RADIUS ? 0 : radius;
|
|
2437
|
+
}
|
|
2438
|
+
function createSilhouetteBuilder(random) {
|
|
2439
|
+
const parts = [];
|
|
2440
|
+
const windows = [];
|
|
2441
|
+
const pick = (list) => list[Math.floor(random() * list.length)];
|
|
2442
|
+
const group = (x, y, z, ry = 0, parent = ROOT) => parts.push({ shape: "group", parent, x, y, z, ry }) - 1;
|
|
2443
|
+
const box = (x, y, z, width, height, depth, color, o = {}) => {
|
|
2444
|
+
parts.push({ shape: "box", parent: o.parent ?? ROOT, x, y: y + height / 2, z, width, height, depth, color, emissive: o.emissive ?? false, ry: o.ry ?? 0, rz: o.rz ?? 0, radius: boxRadius(width, height, depth) });
|
|
2445
|
+
};
|
|
2446
|
+
const prism = (x, y, z, width, height, depth, color, parent = ROOT) => {
|
|
2447
|
+
parts.push({ shape: "prism", parent, x, y, z, width, height, depth, color, ry: 0 });
|
|
2448
|
+
};
|
|
2449
|
+
const cylinder = (x, y, z, radiusTop, radiusBottom, height, color, parent = ROOT, segments = 14) => {
|
|
2450
|
+
parts.push({ shape: "cylinder", parent, x, y: y + height / 2, z, radiusTop, radiusBottom, height, color, segments });
|
|
2451
|
+
};
|
|
2452
|
+
const line = (points, color, parent = ROOT) => {
|
|
2453
|
+
parts.push({ shape: "line", parent, points, color });
|
|
2454
|
+
};
|
|
2455
|
+
const sign = (text, x, y, z, width, height, ry, colors, parent = ROOT) => {
|
|
2456
|
+
parts.push({ shape: "sign", parent, x, y, z, width, height, ry, text, ...colors });
|
|
2457
|
+
};
|
|
2458
|
+
const fence = (x0, z0, x1, z1, height, color, parent = ROOT) => {
|
|
2459
|
+
const length = Math.hypot(x1 - x0, z1 - z0);
|
|
2460
|
+
const count = Math.ceil(length / 3);
|
|
2461
|
+
for (let index = 0; index <= count; index++) {
|
|
2462
|
+
const t = index / count;
|
|
2463
|
+
cylinder(x0 + (x1 - x0) * t, 0, z0 + (z1 - z0) * t, 0.05, 0.05, height, color, parent, 5);
|
|
2464
|
+
}
|
|
2465
|
+
for (const level of [height * 0.35, height * 0.65, height * 0.98]) line([[x0, level, z0], [x1, level, z1]], 9079950, parent);
|
|
2466
|
+
};
|
|
2467
|
+
const finish = (footprint) => ({ parts, windows, footprint });
|
|
2468
|
+
return { random, pick, group, box, prism, cylinder, line, sign, fence, windows, finish };
|
|
2469
|
+
}
|
|
2470
|
+
|
|
2471
|
+
// src/silhouettes/building.ts
|
|
2472
|
+
var STOREY_HEIGHT = 3;
|
|
2473
|
+
var SHOP_AWNING_HEIGHT = 4.5;
|
|
2474
|
+
var WINDOW_COLUMN_WIDTH = 2.6;
|
|
2475
|
+
var SHOP_SIGNS = ["FARM\xC1CIA", "MAGAZINE", "LOT\xC9RICA", "PADARIA", "\xD3TICA", "CAL\xC7ADOS", "BAR"];
|
|
2476
|
+
var SIGN_COLORS = ["#ffd27a", "#e8dfcf", "#c4483b"];
|
|
2477
|
+
var DEFAULT_LIT_CHANCE = 0.07;
|
|
2478
|
+
function createCityBuilding(p) {
|
|
2479
|
+
const random = p.random ?? createSilhouetteRandom(p.seed ?? 7);
|
|
2480
|
+
const b = createSilhouetteBuilder(random);
|
|
2481
|
+
const { x, z, width: w, depth: d, height: h } = p;
|
|
2482
|
+
b.box(x, 0, z, w, h, d, p.color);
|
|
2483
|
+
b.box(x, h, z, w + 0.3, 0.35, d + 0.3, 2763308);
|
|
2484
|
+
if (random() < 0.4) b.cylinder(x + (random() - 0.5) * (w - 3), h + 0.35, z + (random() - 0.5) * (d - 3), 0.8, 0.8, 1.4, 4932928);
|
|
2485
|
+
const hasShop = p.shopChance !== void 0 && p.shopChance > 0 && random() < p.shopChance;
|
|
2486
|
+
if (hasShop) {
|
|
2487
|
+
for (const face of [1, -1]) {
|
|
2488
|
+
const zFace = z + face * (d / 2);
|
|
2489
|
+
b.box(x, SHOP_AWNING_HEIGHT, zFace + face * 0.8, w, 0.2, 1.6, 2763308);
|
|
2490
|
+
b.box(x, 0, zFace + face * 0.03, w * 0.6, 3.5, 0.05, 4869198);
|
|
2491
|
+
const text = b.pick(SHOP_SIGNS);
|
|
2492
|
+
b.sign(text, x, 5.3, zFace + face * 0.12, Math.min(w * 0.7, 8), 1, face > 0 ? 0 : Math.PI, { background: "#1a1a1c", foreground: b.pick(SIGN_COLORS) });
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2495
|
+
addWindows(b, p, hasShop ? SHOP_AWNING_HEIGHT : 0);
|
|
2496
|
+
return b.finish({ x, z, width: w, depth: d, height: h + 0.35 });
|
|
2497
|
+
}
|
|
2498
|
+
function addWindows(b, p, base) {
|
|
2499
|
+
const { x, z, width: w, depth: d, height: h } = p;
|
|
2500
|
+
const floors = Math.max(1, Math.floor((h - 1) / STOREY_HEIGHT));
|
|
2501
|
+
const faces = [[0, d / 2 + 0.03, w, 0], [0, -d / 2 - 0.03, w, Math.PI], [w / 2 + 0.03, 0, d, Math.PI / 2], [-w / 2 - 0.03, 0, d, -Math.PI / 2]];
|
|
2502
|
+
for (const [ox, oz, faceWidth, ry] of faces) {
|
|
2503
|
+
const columns = Math.max(1, Math.floor(faceWidth / WINDOW_COLUMN_WIDTH));
|
|
2504
|
+
for (let floor = 0; floor < floors; floor++) {
|
|
2505
|
+
const y = base + 1.8 + floor * STOREY_HEIGHT;
|
|
2506
|
+
for (let column = 0; column < columns; column++) {
|
|
2507
|
+
const u = (column - (columns - 1) / 2) * WINDOW_COLUMN_WIDTH;
|
|
2508
|
+
b.windows.push({ x: ox ? x + ox : x + u, y, z: ox ? z + u : z + oz, ry, width: 1.2, height: 1.4, isLit: b.random() < (p.litChance || DEFAULT_LIT_CHANCE) });
|
|
2509
|
+
}
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2513
|
+
|
|
2514
|
+
// src/silhouettes/houses.ts
|
|
2515
|
+
var LIT_WINDOW = 16757854;
|
|
2516
|
+
var DARK_WINDOW = 1053979;
|
|
2517
|
+
var DEFAULT_ROOF = 5913130;
|
|
2518
|
+
var randomOf = (o) => o.random ?? createSilhouetteRandom(o.seed ?? 7);
|
|
2519
|
+
var pane = (isLit) => ({ color: isLit ? LIT_WINDOW : DARK_WINDOW, emissive: isLit });
|
|
2520
|
+
function createTwoStoreyHouse(p) {
|
|
2521
|
+
const b = createSilhouetteBuilder(randomOf(p));
|
|
2522
|
+
const { width: w, depth: d } = p;
|
|
2523
|
+
const g = b.group(p.x, 0, p.z, p.ry);
|
|
2524
|
+
b.box(0, 0, 0, w, 6, d, p.color, { parent: g });
|
|
2525
|
+
b.prism(0, 6, 0, w + 0.4, 1.4, d + 0.4, p.roofColor ?? DEFAULT_ROOF, g);
|
|
2526
|
+
b.box(0, 3.1, d / 2 + 0.5, w * 0.6, 0.15, 1, 6974054, { parent: g });
|
|
2527
|
+
for (const floor of [0, 1]) {
|
|
2528
|
+
for (const side of [-1, 1]) {
|
|
2529
|
+
const { color, emissive } = pane(b.random() < 0.12);
|
|
2530
|
+
b.box(side * w * 0.26, 1.1 + floor * 3, d / 2 + 0.03, 1.1, 1.2, 0.04, color, { parent: g, emissive });
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2533
|
+
b.box(0, 0, d / 2 + 0.02, 1, 2.1, 0.05, 2759956, { parent: g });
|
|
2534
|
+
return b.finish({ x: p.x, z: p.z, width: w, depth: d, height: 7.4 });
|
|
2535
|
+
}
|
|
2536
|
+
function createSlabHouse(p) {
|
|
2537
|
+
const b = createSilhouetteBuilder(randomOf(p));
|
|
2538
|
+
const { width: w, depth: d, floors } = p;
|
|
2539
|
+
const g = b.group(p.x, 0, p.z, 0);
|
|
2540
|
+
for (let floor = 0; floor < floors; floor++) {
|
|
2541
|
+
const isTop = floor === floors - 1;
|
|
2542
|
+
b.box(0, floor * 2.9, 0, w, 2.9, d, isTop && b.random() < 0.5 ? 10119760 : p.color, { parent: g });
|
|
2543
|
+
const { color, emissive } = pane(b.random() < 0.1);
|
|
2544
|
+
b.box(0, floor * 2.9 + 1.1, d / 2 + 0.03, 1, 1, 0.04, color, { parent: g, emissive });
|
|
2545
|
+
}
|
|
2546
|
+
b.cylinder(w * 0.25, floors * 2.9, -d * 0.2, 0.55, 0.55, 0.9, 2775712, g, 10);
|
|
2547
|
+
for (let level = 0; level < floors; level++) b.box(w / 2 + 0.4, level * 2.9, 0, 0.8, 0.18, 1.2, 8026740, { parent: g });
|
|
2548
|
+
if (b.random() < 0.6) for (let index = 0; index < 4; index++) b.box(-w * 0.3 + index * 0.4, floors * 2.9, 0, 0.02, 1, 0.02, 9079434, { parent: g });
|
|
2549
|
+
return b.finish({ x: p.x, z: p.z, width: w, depth: d, height: floors * 2.9 + 1 });
|
|
2550
|
+
}
|
|
2551
|
+
function createColonialHouse(p) {
|
|
2552
|
+
const b = createSilhouetteBuilder(randomOf(p));
|
|
2553
|
+
const { width: w } = p;
|
|
2554
|
+
const g = b.group(p.x, 0, p.z, 0);
|
|
2555
|
+
b.box(0, 0, 0, w, 4.2, 10, p.color, { parent: g });
|
|
2556
|
+
b.box(0, 4.2, 4.6, w, 0.8, 0.25, p.color, { parent: g });
|
|
2557
|
+
b.prism(0, 4.2, -0.4, w, 1.4, 9.6, 8014386, g);
|
|
2558
|
+
const count = Math.max(1, Math.floor(w / 2.2));
|
|
2559
|
+
for (let index = 0; index < count; index++) {
|
|
2560
|
+
const u = (index - (count - 1) / 2) * 2.2;
|
|
2561
|
+
const isDoor = index === 0 && p.hasDoor;
|
|
2562
|
+
const lit = isDoor ? false : b.random() < 0.15;
|
|
2563
|
+
const color = isDoor ? 3810326 : lit ? LIT_WINDOW : 2767434;
|
|
2564
|
+
b.box(u, 1, 5.02, 0.9, 2, 0.05, color, { parent: g, emissive: lit });
|
|
2565
|
+
}
|
|
2566
|
+
return b.finish({ x: p.x, z: p.z, width: w, depth: 10, height: 6 });
|
|
2567
|
+
}
|
|
2568
|
+
function createChalet(p) {
|
|
2569
|
+
const b = createSilhouetteBuilder(randomOf(p));
|
|
2570
|
+
const g = b.group(p.x, 0, p.z, p.ry);
|
|
2571
|
+
b.box(0, 0, 0, 7, 0.8, 9, 5920336, { parent: g });
|
|
2572
|
+
b.box(0, 0.8, 0, 7, 4.2, 9, p.color ?? 7031344, { parent: g });
|
|
2573
|
+
b.prism(0, 5, 0, 7.6, 4, 9.6, 2894376, g);
|
|
2574
|
+
b.box(2.2, 5, -1, 0.8, 4.2, 0.8, 5920336, { parent: g });
|
|
2575
|
+
const { color, emissive } = pane(b.random() < 0.3);
|
|
2576
|
+
b.box(0, 1.6, 4.52, 1.3, 1.3, 0.04, color, { parent: g, emissive });
|
|
2577
|
+
b.box(0, 6.2, 4.6, 1, 1, 0.04, DARK_WINDOW, { parent: g });
|
|
2578
|
+
return b.finish({ x: p.x, z: p.z, width: 7.6, depth: 9.6, height: 9 });
|
|
2579
|
+
}
|
|
2580
|
+
function createCompanyHouse(p) {
|
|
2581
|
+
const b = createSilhouetteBuilder(randomOf(p));
|
|
2582
|
+
const g = b.group(p.x, 0, p.z, 0);
|
|
2583
|
+
b.box(0, 0, 0, 6, 3, 9, p.color ?? 14209216, { parent: g });
|
|
2584
|
+
b.prism(0, 3, 0, 6.4, 1.6, 9.4, 9062962, g);
|
|
2585
|
+
b.box(0, 0, 4.52, 0.9, 2.1, 0.05, 3811868, { parent: g });
|
|
2586
|
+
b.box(1.8, 1.1, 4.52, 1, 1, 0.04, DARK_WINDOW, { parent: g });
|
|
2587
|
+
b.fence(-3.2, 6.5, 3.2, 6.5, 0.9, 14735560, g);
|
|
2588
|
+
return b.finish({ x: p.x, z: p.z, width: 6.4, depth: 13, height: 4.6 });
|
|
2589
|
+
}
|
|
2590
|
+
function createStiltHouse(p) {
|
|
2591
|
+
const b = createSilhouetteBuilder(randomOf(p));
|
|
2592
|
+
const g = b.group(p.x, 0, p.z, 0);
|
|
2593
|
+
for (const a of [-2, 2]) for (const c of [-3, 3]) b.cylinder(a, -1.5, c, 0.12, 0.12, 3.1, 3811868, g, 6);
|
|
2594
|
+
b.box(0, 1.5, 0, 5, 0.2, 7, 5916208, { parent: g });
|
|
2595
|
+
b.box(0, 1.7, 0, 4.6, 2.6, 6.4, p.color ?? 6969926, { parent: g });
|
|
2596
|
+
b.prism(0, 4.3, 0, 5.2, 1.1, 7, 8026744, g);
|
|
2597
|
+
b.box(0, 1.5, 5, 1.2, 0.15, 4, 5916208, { parent: g });
|
|
2598
|
+
return b.finish({ x: p.x, z: p.z, width: 5.2, depth: 9, height: 5.4 });
|
|
2599
|
+
}
|
|
2600
|
+
function createFarmhouse(p) {
|
|
2601
|
+
const b = createSilhouetteBuilder(randomOf(p));
|
|
2602
|
+
const g = b.group(p.x, 0, p.z, 0);
|
|
2603
|
+
b.box(0, 0, 0, 15, 3.2, 11, 14208952, { parent: g });
|
|
2604
|
+
b.prism(0, 3.2, 0, 17, 2.2, 13, 9062962, g);
|
|
2605
|
+
b.box(0, 0, 6.8, 15, 0.25, 2.6, 6969926, { parent: g });
|
|
2606
|
+
for (let index = 0; index < 6; index++) b.cylinder(-7 + index * 2.8, 0, 7.8, 0.1, 0.1, 3.2, 5916208, g, 6);
|
|
2607
|
+
b.box(-3, 1, 5.52, 1.2, 1.2, 0.04, LIT_WINDOW, { parent: g, emissive: true });
|
|
2608
|
+
return b.finish({ x: p.x, z: p.z, width: 17, depth: 13, height: 5.4 });
|
|
2609
|
+
}
|
|
2610
|
+
function createDetachedHouse(p) {
|
|
2611
|
+
const b = createSilhouetteBuilder(randomOf(p));
|
|
2612
|
+
const { width: w, depth: d } = p;
|
|
2613
|
+
const g = b.group(p.x, 0, p.z, p.ry);
|
|
2614
|
+
b.box(0, 0, 0, w, 3, d, p.color, { parent: g });
|
|
2615
|
+
b.prism(0, 3, 0, w + 0.5, 1.6, d + 0.5, p.roofColor ?? DEFAULT_ROOF, g);
|
|
2616
|
+
b.box(0, 0, d / 2 + 0.02, 1, 2.1, 0.05, 2759956, { parent: g });
|
|
2617
|
+
for (const side of [-1, 1]) {
|
|
2618
|
+
const { color, emissive } = pane(b.random() < 0.15);
|
|
2619
|
+
b.box(side * w * 0.28, 1.1, d / 2 + 0.03, 1.1, 1.1, 0.04, color, { parent: g, emissive });
|
|
2620
|
+
if (p.isBoarded || b.random() < 0.3) for (const k of [-1, 1]) b.box(side * w * 0.28, 1.5 + k * 0.25, d / 2 + 0.08, 1.3, 0.14, 0.04, 7031338, { parent: g, rz: k * 0.15 });
|
|
2621
|
+
}
|
|
2622
|
+
return b.finish({ x: p.x, z: p.z, width: w + 0.5, depth: d + 0.5, height: 4.6 });
|
|
2623
|
+
}
|
|
2624
|
+
|
|
2016
2625
|
// src/index.ts
|
|
2017
2626
|
registerGroundItems();
|
|
2018
2627
|
registerPermeableGround("lawn", "dirt");
|
|
@@ -2045,6 +2654,13 @@ var engine = {
|
|
|
2045
2654
|
registerState,
|
|
2046
2655
|
setStateDistribution,
|
|
2047
2656
|
registerLocale,
|
|
2657
|
+
registerResultExtension,
|
|
2658
|
+
registerFindsProvider,
|
|
2659
|
+
findsFor,
|
|
2660
|
+
findsAnswer,
|
|
2661
|
+
registerViewerLayer,
|
|
2662
|
+
viewerLayers: VIEWER_LAYERS,
|
|
2663
|
+
blocksWindow,
|
|
2048
2664
|
// zoning
|
|
2049
2665
|
defineZone,
|
|
2050
2666
|
getZone,
|
|
@@ -2059,6 +2675,7 @@ var engine = {
|
|
|
2059
2675
|
catalog: CATALOG,
|
|
2060
2676
|
libraries: LIBRARIES,
|
|
2061
2677
|
styles: STYLES,
|
|
2678
|
+
paletteStyles: PALETTE_STYLES,
|
|
2062
2679
|
createRng,
|
|
2063
2680
|
mulberry32,
|
|
2064
2681
|
hashString,
|
|
@@ -2066,13 +2683,26 @@ var engine = {
|
|
|
2066
2683
|
windowZone,
|
|
2067
2684
|
openingZones,
|
|
2068
2685
|
color: { lstar, hex: hexString, hsl: intToHsl, HIGH_SATURATION, MIN_WALL_FLOOR_CONTRAST, MIN_FABRIC_FLOOR_CONTRAST },
|
|
2069
|
-
constants: { WALL_THICKNESS, DOOR_CLEARANCE }
|
|
2686
|
+
constants: { WALL_THICKNESS, DOOR_CLEARANCE, PLAYER_RADIUS, DOOR_EDGE_CLEARANCE, WINDOW_EDGE_CLEARANCE, OVERHEAD_TOP, CAR_CLEARANCE }
|
|
2070
2687
|
};
|
|
2071
2688
|
export {
|
|
2689
|
+
blocksWindow,
|
|
2072
2690
|
checkLot,
|
|
2691
|
+
createChalet,
|
|
2692
|
+
createCityBuilding,
|
|
2693
|
+
createColonialHouse,
|
|
2694
|
+
createCompanyHouse,
|
|
2695
|
+
createDetachedHouse,
|
|
2696
|
+
createFarmhouse,
|
|
2073
2697
|
createRng,
|
|
2698
|
+
createSilhouetteRandom,
|
|
2699
|
+
createSlabHouse,
|
|
2700
|
+
createStiltHouse,
|
|
2701
|
+
createTwoStoreyHouse,
|
|
2074
2702
|
defineZone,
|
|
2075
2703
|
engine,
|
|
2704
|
+
findsAnswer,
|
|
2705
|
+
findsFor,
|
|
2076
2706
|
generate,
|
|
2077
2707
|
getZone,
|
|
2078
2708
|
hashString,
|
|
@@ -2086,6 +2716,7 @@ export {
|
|
|
2086
2716
|
overlaps,
|
|
2087
2717
|
palette,
|
|
2088
2718
|
registerDecorProfile,
|
|
2719
|
+
registerFindsProvider,
|
|
2089
2720
|
registerGroundOverlay,
|
|
2090
2721
|
registerItem,
|
|
2091
2722
|
registerItems,
|
|
@@ -2095,11 +2726,13 @@ export {
|
|
|
2095
2726
|
registerPaletteStyle,
|
|
2096
2727
|
registerPass,
|
|
2097
2728
|
registerPermeableGround,
|
|
2729
|
+
registerResultExtension,
|
|
2098
2730
|
registerRoomColors,
|
|
2099
2731
|
registerSeedKeys,
|
|
2100
2732
|
registerState,
|
|
2101
2733
|
registerStyle,
|
|
2102
2734
|
registerTemplate,
|
|
2735
|
+
registerViewerLayer,
|
|
2103
2736
|
registerWallTop,
|
|
2104
2737
|
setStateDistribution,
|
|
2105
2738
|
slotKit,
|