@adatechnology/game-urban 0.2.8 → 0.3.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.
@@ -25,6 +25,7 @@ var GameUrban = (() => {
25
25
  DEFAULT_CITY_PLANS: () => DEFAULT_CITY_PLANS,
26
26
  DEFAULT_DISTRICT_KINDS: () => DEFAULT_DISTRICT_KINDS,
27
27
  DEFAULT_FACADES: () => DEFAULT_FACADES,
28
+ DEFAULT_LIGHT_ALLOWANCE: () => DEFAULT_LIGHT_ALLOWANCE,
28
29
  DEFAULT_LIGHT_BUDGET: () => DEFAULT_LIGHT_BUDGET,
29
30
  DEFAULT_MAX_FLOORS: () => DEFAULT_MAX_FLOORS,
30
31
  DEFAULT_PATTERNS: () => DEFAULT_PATTERNS,
@@ -44,9 +45,10 @@ var GameUrban = (() => {
44
45
  TOWER_DEFAULTS: () => TOWER_DEFAULTS,
45
46
  TOWER_PATTERNS: () => TOWER_PATTERNS,
46
47
  TOWER_SAMPLE_CELLS: () => TOWER_SAMPLE_CELLS,
47
- clamp: () => clamp,
48
+ clamp: () => clamp2,
48
49
  composePattern: () => composePattern,
49
50
  createCities: () => createCities,
51
+ createLightAllowance: () => createLightAllowance,
50
52
  createLightBudget: () => createLightBudget,
51
53
  createPlantProvider: () => createPlantProvider,
52
54
  createSilhouetteHouses: () => createSilhouetteHouses,
@@ -85,37 +87,2294 @@ var GameUrban = (() => {
85
87
  });
86
88
 
87
89
  // src/budget.ts
88
- function createLightBudget(count) {
89
- const budget = {
90
+ function createLightAllowance(count) {
91
+ const allowance = {
90
92
  left: count,
91
93
  take() {
92
- return budget.left-- > 0;
94
+ return allowance.left-- > 0;
93
95
  }
94
96
  };
95
- return budget;
97
+ return allowance;
96
98
  }
99
+ var createLightBudget = createLightAllowance;
100
+
101
+ // ../game-contracts/dist/index.js
102
+ var DEFAULT_COST_ALLOCATION_BYTES = 1024 * 1024;
103
+ var DEFERRED_QUEUE_ID = {
104
+ DESTRUCTION_EVENTS: "destruction.events",
105
+ BREACH_PAINT: "floorplan.breach-paint",
106
+ AUDIO_VOICES: "audio.voices",
107
+ VEHICLE_CREASE: "vehicles.crease"
108
+ };
109
+ var DEFERRED_PRIORITY = {
110
+ [DEFERRED_QUEUE_ID.DESTRUCTION_EVENTS]: { priority: 300, phase: "present" },
111
+ [DEFERRED_QUEUE_ID.BREACH_PAINT]: { priority: 250, phase: "present" },
112
+ [DEFERRED_QUEUE_ID.AUDIO_VOICES]: { priority: 200, phase: "idle", reserveMs: 1 },
113
+ [DEFERRED_QUEUE_ID.VEHICLE_CREASE]: { priority: 100, phase: "idle" }
114
+ };
115
+ var WARNED_KEY = /* @__PURE__ */ Symbol.for("@adatechnology/game-contracts.warned");
116
+ var warnedKeys = () => {
117
+ const holder = globalThis;
118
+ return holder[WARNED_KEY] ?? (holder[WARNED_KEY] = /* @__PURE__ */ new Set());
119
+ };
120
+ var isProduction = () => {
121
+ try {
122
+ return process.env.NODE_ENV === "production";
123
+ } catch {
124
+ return false;
125
+ }
126
+ };
127
+ var isQuiet = () => globalThis.__GAME_KIT_QUIET__ === true;
128
+ var warnOnce = (key, message) => {
129
+ if (isQuiet() || isProduction()) return;
130
+ const seen = warnedKeys();
131
+ if (seen.has(key)) return;
132
+ seen.add(key);
133
+ console.warn(`[game-kit] ${message}`);
134
+ };
135
+ var UNSAFE_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
136
+ var isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
137
+ var deepMerge = (base, patch) => {
138
+ if (!patch) return base;
139
+ const result = { ...base };
140
+ for (const [key, value] of Object.entries(patch)) {
141
+ if (value === void 0 || UNSAFE_KEYS.has(key)) continue;
142
+ const current = result[key];
143
+ result[key] = isPlainObject(current) && isPlainObject(value) ? deepMerge(current, value) : value;
144
+ }
145
+ return result;
146
+ };
147
+ var mergeById = (shipped, extra) => {
148
+ const entries = /* @__PURE__ */ new Map();
149
+ for (const entry of [...shipped, ...extra ?? []]) entries.set(entry.id, entry);
150
+ return entries;
151
+ };
152
+ var applyPatches = (entries, patches) => {
153
+ for (const [id, patch] of Object.entries(patches ?? {})) {
154
+ const entry = entries.get(id);
155
+ if (entry) entries.set(id, deepMerge(entry, patch));
156
+ else warnOnce(`registry.patch.${id}`, `patch for "${id}" ignored: no entry has that id`);
157
+ }
158
+ };
159
+ var removeIds = (entries, ids) => {
160
+ for (const id of ids ?? []) {
161
+ if (!entries.delete(id)) warnOnce(`registry.remove.${id}`, `remove of "${id}" ignored: no entry has that id`);
162
+ }
163
+ };
164
+ var SURFACE_IDS = ["concrete", "asphalt", "wood", "tile", "dirt", "grass", "gravel", "sand", "metal", "water", "glass", "carpet"];
165
+ var GAIT_IDS = ["walk", "run"];
166
+ var LANDING_IDS = ["light", "heavy"];
167
+ var BODY_SOUND_IDS = [
168
+ "jump",
169
+ "cloth_rustle",
170
+ "body_fall",
171
+ "fall_damage",
172
+ "player_hurt",
173
+ "player_breath_run",
174
+ "player_death",
175
+ "bite",
176
+ "grab",
177
+ "punch_swing",
178
+ "punch_hit_body",
179
+ "punch_hit_face",
180
+ "punch_hit_object",
181
+ "kick_swing",
182
+ "kick_hit",
183
+ "kick_hit_object",
184
+ "shove_two_hands",
185
+ "knife_swing",
186
+ "knife_stab",
187
+ "knife_slash",
188
+ "knife_hit_object",
189
+ "blunt_swing",
190
+ "blunt_hit",
191
+ "blunt_hit_object",
192
+ "axe_swing",
193
+ "axe_chop",
194
+ "axe_hit_object"
195
+ ];
196
+ var footstepSoundId = (surface, gait) => `footstep_${surface}_${gait}`;
197
+ var FOOTSTEP_SOUND_IDS = SURFACE_IDS.flatMap((surface) => GAIT_IDS.map((gait) => footstepSoundId(surface, gait)));
198
+ var LAND_SOUND_IDS = SURFACE_IDS.flatMap((surface) => LANDING_IDS.map((landing) => `land_${surface}_${landing}`));
199
+ var BODY_FOLEY_IDS = [...FOOTSTEP_SOUND_IDS, ...LAND_SOUND_IDS, ...BODY_SOUND_IDS];
97
200
 
98
201
  // ../game-floorplan/dist/index.js
99
- var WALL_THICKNESS = 0.15;
100
- var MARGIN = WALL_THICKNESS / 2;
101
- var CATALOG = {};
202
+ var createRegistries = () => ({
203
+ CATALOG: {},
204
+ LIBRARIES: {},
205
+ LIBRARY_ALIAS: {},
206
+ TEMPLATES: {},
207
+ STYLES: {},
208
+ STYLE_ALIAS: {},
209
+ PALETTE_STYLES: {},
210
+ ROOM_COLORS: [],
211
+ PASSES: [],
212
+ DECOR_PROFILES: {},
213
+ GROUND_OVERLAYS: [],
214
+ STATE_PLUGINS: { normal: { apply: () => [] } },
215
+ WALL_TOP_HEIGHTS: { none: 0 },
216
+ SEED_KEYS: { rooms: {}, items: {} },
217
+ stateDistribution: { weights: null, seedKey: "state" },
218
+ RESULT_EXTENSIONS: [],
219
+ FINDS_PROVIDERS: [],
220
+ VIEWER_LAYERS: [],
221
+ LOCALES: {},
222
+ ZONES: {},
223
+ PERMEABLE_GROUNDS: /* @__PURE__ */ new Set()
224
+ });
225
+ var defaultRegistries;
226
+ var activeRegistries;
227
+ var getDefaultRegistries = () => defaultRegistries ?? (defaultRegistries = createRegistries());
228
+ var registries = () => activeRegistries ?? getDefaultRegistries();
229
+ function withRegistries(scope, run) {
230
+ const previous = activeRegistries;
231
+ activeRegistries = scope;
232
+ try {
233
+ return run();
234
+ } finally {
235
+ activeRegistries = previous;
236
+ }
237
+ }
102
238
  function registerItem(def) {
103
239
  const h = def.h;
104
240
  const item = Object.assign({ front: "+z", window: h <= 0.95, low: h <= 0.5, flat: false, deck: false, rim: 0 }, def);
105
- CATALOG[def.type] = item;
241
+ registries().CATALOG[def.type] = item;
106
242
  return item;
107
243
  }
108
244
  function registerItems(list) {
109
245
  for (const d of list) registerItem(d);
110
246
  }
111
- var locales = {};
247
+ function registerLibrary(id, library) {
248
+ registries().LIBRARIES[id] = library;
249
+ }
250
+ function registerLibraryAlias(alias, id) {
251
+ registries().LIBRARY_ALIAS[alias] = id;
252
+ }
253
+ function registerTemplate(t) {
254
+ registries().TEMPLATES[t.id] = t;
255
+ return t;
256
+ }
257
+ function registerStyle(style) {
258
+ registries().STYLES[style.id] = style;
259
+ for (const a of style.aliases || []) registries().STYLE_ALIAS[a] = style.id;
260
+ }
261
+ function registerPaletteStyle(id, def) {
262
+ registries().PALETTE_STYLES[id] = def;
263
+ }
264
+ function registerRoomColors(list) {
265
+ for (const d of list) {
266
+ const i = registries().ROOM_COLORS.findIndex((x) => x.type === d.type);
267
+ if (i >= 0) registries().ROOM_COLORS.splice(i, 1);
268
+ registries().ROOM_COLORS.push(d);
269
+ }
270
+ registries().ROOM_COLORS.sort((a, b) => a.order - b.order);
271
+ }
272
+ function registerDecorProfile(styleId, profile) {
273
+ registries().DECOR_PROFILES[styleId] = profile;
274
+ }
275
+ function registerPass(name, run) {
276
+ const i = registries().PASSES.findIndex((p) => p.name === name);
277
+ if (i >= 0) registries().PASSES[i] = { name, run };
278
+ else registries().PASSES.push({ name, run });
279
+ }
280
+ function registerGroundOverlay(rule) {
281
+ registries().GROUND_OVERLAYS.push(rule);
282
+ }
283
+ function registerWallTop(id, height) {
284
+ registries().WALL_TOP_HEIGHTS[id] = height;
285
+ }
286
+ function registerSeedKeys(keys) {
287
+ Object.assign(registries().SEED_KEYS.rooms, keys.rooms);
288
+ Object.assign(registries().SEED_KEYS.items, keys.items);
289
+ }
290
+ function registerState(name, plugin) {
291
+ registries().STATE_PLUGINS[name] = plugin;
292
+ }
293
+ function setStateDistribution(weights, seedKey = "state") {
294
+ registries().stateDistribution.weights = weights;
295
+ registries().stateDistribution.seedKey = seedKey;
296
+ }
297
+ var listStates = () => Object.keys(registries().STATE_PLUGINS);
298
+ function resolveStyleId(id) {
299
+ const s = id ?? "";
300
+ return registries().STYLE_ALIAS[s] ?? s;
301
+ }
302
+ function styleOf(id) {
303
+ return registries().STYLES[id] || registries().STYLES.budget || Object.values(registries().STYLES)[0];
304
+ }
305
+ function registerResultExtension(name, run, options = {}) {
306
+ const entry = { name, run, after: options.after || [], seedKey: options.seedKey ?? name };
307
+ const i = registries().RESULT_EXTENSIONS.findIndex((x) => x.name === name);
308
+ if (i >= 0) registries().RESULT_EXTENSIONS[i] = entry;
309
+ else registries().RESULT_EXTENSIONS.push(entry);
310
+ }
311
+ function registerFindsProvider(provider) {
312
+ registries().FINDS_PROVIDERS.push(provider);
313
+ }
314
+ function registerViewerLayer(layer) {
315
+ const i = registries().VIEWER_LAYERS.findIndex((x) => x.id === layer.id);
316
+ if (i >= 0) registries().VIEWER_LAYERS[i] = layer;
317
+ else registries().VIEWER_LAYERS.push(layer);
318
+ }
319
+ function mulberry32(a) {
320
+ return function() {
321
+ a |= 0;
322
+ a = a + 1831565813 | 0;
323
+ let t = Math.imul(a ^ a >>> 15, 1 | a);
324
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
325
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
326
+ };
327
+ }
328
+ function hashString(input) {
329
+ const s = String(input);
330
+ let h = 1779033703 ^ s.length;
331
+ for (let i = 0; i < s.length; i++) {
332
+ h = Math.imul(h ^ s.charCodeAt(i), 3432918353);
333
+ h = h << 13 | h >>> 19;
334
+ }
335
+ h = Math.imul(h ^ h >>> 16, 2246822507);
336
+ h = Math.imul(h ^ h >>> 13, 3266489909);
337
+ return (h ^ h >>> 16) >>> 0;
338
+ }
339
+ var createRng = (seed, key) => mulberry32(hashString(seed + "|" + key));
340
+ function pickWeighted(rng, weights) {
341
+ const keys = Object.keys(weights);
342
+ let total = 0;
343
+ for (const k of keys) total += weights[k];
344
+ let x = rng() * total;
345
+ for (const k of keys) {
346
+ x -= weights[k];
347
+ if (x < 0) return k;
348
+ }
349
+ return keys[keys.length - 1];
350
+ }
351
+ function weightedOrder(candidates, rng) {
352
+ const arr = candidates.slice();
353
+ const out = [];
354
+ while (arr.length) {
355
+ let total = 0;
356
+ for (const c of arr) total += c.weight;
357
+ let x = rng() * total;
358
+ let i = 0;
359
+ for (; i < arr.length - 1; i++) {
360
+ x -= arr[i].weight;
361
+ if (x < 0) break;
362
+ }
363
+ out.push(arr.splice(i, 1)[0]);
364
+ }
365
+ return out;
366
+ }
367
+ function shuffle(arr, rng) {
368
+ const a = arr.slice();
369
+ for (let i = a.length - 1; i > 0; i--) {
370
+ const j = Math.floor(rng() * (i + 1));
371
+ [a[i], a[j]] = [a[j], a[i]];
372
+ }
373
+ return a;
374
+ }
375
+ function orderOf(extensions) {
376
+ const ordered = [];
377
+ const visiting = /* @__PURE__ */ new Set();
378
+ const visit = (extension) => {
379
+ if (ordered.includes(extension)) return;
380
+ if (visiting.has(extension.name)) throw new Error("Result extensions depend on each other: " + extension.name);
381
+ visiting.add(extension.name);
382
+ for (const name of extension.after) {
383
+ const before = extensions.find((x) => x.name === name);
384
+ if (before) visit(before);
385
+ }
386
+ visiting.delete(extension.name);
387
+ ordered.push(extension);
388
+ };
389
+ extensions.forEach(visit);
390
+ return ordered;
391
+ }
392
+ function runResultExtensions(plan, result, seed, options) {
393
+ for (const extension of orderOf(registries().RESULT_EXTENSIONS)) {
394
+ result[extension.name] = extension.run({ plan, result, seed, rng: createRng(seed, extension.seedKey), options });
395
+ }
396
+ }
397
+ function findsFor(query) {
398
+ return answerOf(query) ?? [];
399
+ }
400
+ function findsAnswer(query) {
401
+ return answerOf(query);
402
+ }
403
+ function answerOf(query) {
404
+ for (const provider of registries().FINDS_PROVIDERS) {
405
+ const stacks = provider.findsFor(query);
406
+ if (stacks !== null) return stacks;
407
+ }
408
+ return null;
409
+ }
410
+ var WALL_THICKNESS = 0.15;
411
+ var MARGIN = WALL_THICKNESS / 2;
412
+ var EPS = 1e-6;
413
+ var TOL = 1e-3;
414
+ var DOOR_CLEARANCE = 0.9;
415
+ var PLAYER_RADIUS = 0.24;
416
+ var DOOR_EDGE_CLEARANCE = 0.12;
417
+ var WINDOW_EDGE_CLEARANCE = 0.3;
418
+ var OVERHEAD_TOP = 2.15;
419
+ var WINDOWLESS_ROOM_TYPES = /* @__PURE__ */ new Set([
420
+ "stockroom",
421
+ "kitchen",
422
+ "laundry",
423
+ "bathroom",
424
+ "void",
425
+ "elevator",
426
+ "stairs",
427
+ "pantry",
428
+ "closet",
429
+ "corridor",
430
+ "hall",
431
+ "balcony",
432
+ "terrace",
433
+ "garage",
434
+ "storage",
435
+ "porch",
436
+ "tent",
437
+ "outhouse"
438
+ ]);
439
+ var CAR_CLEARANCE = { driver: 0.8, side: 0.6, end: 0.6 };
440
+ var r2 = (x) => Math.round(x * 100) / 100;
441
+ var clamp = (x, a, b) => Math.max(a, Math.min(b, x));
442
+ var overlaps = (a, b, e = EPS) => a[0] < b[2] - e && a[2] > b[0] + e && a[1] < b[3] - e && a[3] > b[1] + e;
443
+ var inner = (p) => [p[0] + MARGIN, p[1] + MARGIN, p[2] - MARGIN, p[3] - MARGIN];
444
+ var eq = (a, b) => Math.abs(a - b) < TOL;
445
+ var inflate = (r, e) => [r[0] - e, r[1] - e, r[2] + e, r[3] + e];
446
+ var areaOf = (r) => (r[2] - r[0]) * (r[3] - r[1]);
447
+ var topOf = (m) => (m.y || 0) + m.h;
448
+ var extent = (size, f) => Math.abs(f[1]) === 1 ? [size[0], size[1]] : [size[1], size[0]];
449
+ var cardinal = (dx, dz) => Math.abs(dx) > Math.abs(dz) ? [Math.sign(dx), 0] : [0, Math.sign(dz) || 1];
450
+ function clearRect(r, f, clearance) {
451
+ return f[1] === 1 ? [r[0], r[3], r[2], r[3] + clearance] : f[1] === -1 ? [r[0], r[1] - clearance, r[2], r[1]] : f[0] === 1 ? [r[2], r[1], r[2] + clearance, r[3]] : [r[0] - clearance, r[1], r[0], r[3]];
452
+ }
453
+ function insideUnion(r, inners) {
454
+ const e = 1e-4;
455
+ const points = [
456
+ [r[0] + e, r[1] + e],
457
+ [r[2] - e, r[1] + e],
458
+ [r[0] + e, r[3] - e],
459
+ [r[2] - e, r[3] - e],
460
+ [(r[0] + r[2]) / 2, (r[1] + r[3]) / 2]
461
+ ];
462
+ return points.every((p) => inners.some((q) => p[0] >= q[0] - 1e-6 && p[0] <= q[2] + 1e-6 && p[1] >= q[1] - 1e-6 && p[1] <= q[3] + 1e-6));
463
+ }
464
+ function cutIntervals(intervals, a, b) {
465
+ const out = [];
466
+ for (const [x, y] of intervals) {
467
+ if (b <= x || a >= y) {
468
+ out.push([x, y]);
469
+ continue;
470
+ }
471
+ if (a > x) out.push([x, a]);
472
+ if (b < y) out.push([b, y]);
473
+ }
474
+ return out;
475
+ }
476
+ function exteriorEdge(room, side, w, d, c) {
477
+ const cand = [];
478
+ for (const p of room.parts) {
479
+ if (side === "front" && eq(p[1], 0)) cand.push({ axis: "u", pos: 0, a0: p[0], a1: p[2] });
480
+ else if (side === "back" && eq(p[3], d)) cand.push({ axis: "u", pos: d, a0: p[0], a1: p[2] });
481
+ else if (side === "left" && eq(p[0], 0)) cand.push({ axis: "v", pos: 0, a0: p[1], a1: p[3] });
482
+ else if (side === "right" && eq(p[2], w)) cand.push({ axis: "v", pos: w, a0: p[1], a1: p[3] });
483
+ }
484
+ if (!cand.length) return null;
485
+ if (c != null) {
486
+ const k = cand.find((x) => c >= x.a0 - TOL && c <= x.a1 + TOL);
487
+ if (k) return k;
488
+ }
489
+ return cand.sort((x, y) => y.a1 - y.a0 - (x.a1 - x.a0))[0];
490
+ }
491
+ function sharedEdge(A, B, c) {
492
+ const cand = [];
493
+ for (const pa of A.parts)
494
+ for (const pb of B.parts) {
495
+ let o;
496
+ if (eq(pa[2], pb[0]) || eq(pa[0], pb[2])) {
497
+ o = [Math.max(pa[1], pb[1]), Math.min(pa[3], pb[3])];
498
+ if (o[1] - o[0] > TOL) cand.push({ axis: "v", pos: eq(pa[2], pb[0]) ? pa[2] : pa[0], a0: o[0], a1: o[1] });
499
+ }
500
+ if (eq(pa[3], pb[1]) || eq(pa[1], pb[3])) {
501
+ o = [Math.max(pa[0], pb[0]), Math.min(pa[2], pb[2])];
502
+ if (o[1] - o[0] > TOL) cand.push({ axis: "u", pos: eq(pa[3], pb[1]) ? pa[3] : pa[1], a0: o[0], a1: o[1] });
503
+ }
504
+ }
505
+ if (!cand.length) return null;
506
+ if (c != null) {
507
+ const k = cand.find((x) => c >= x.a0 - TOL && c <= x.a1 + TOL);
508
+ if (k) return k;
509
+ }
510
+ return cand.sort((x, y) => y.a1 - y.a0 - (x.a1 - x.a0))[0];
511
+ }
512
+ function openingZones(u, v, axis, width, depth) {
513
+ const h = width / 2;
514
+ return axis === "u" ? [[u - h, v - depth, u + h, v], [u - h, v, u + h, v + depth]] : [[u - depth, v - h, u, v + h], [u, v - h, u + depth, v + h]];
515
+ }
516
+ var openingDepth = (width, leaf) => Math.max(0.9, leaf) + MARGIN;
517
+ function entryPoints(ab) {
518
+ return ab.axis === "u" ? [[ab.u, ab.v - 0.55], [ab.u, ab.v + 0.55]] : [[ab.u - 0.55, ab.v], [ab.u + 0.55, ab.v]];
519
+ }
520
+ function normalizeRoom(c, floorIndex) {
521
+ const parts = [c.rect].concat(c.parts || []).map((p) => p.map(r2));
522
+ return {
523
+ id: c.id,
524
+ type: c.type,
525
+ label: c.label,
526
+ floor: floorIndex,
527
+ parts,
528
+ library: c.library || null,
529
+ without: c.without || [],
530
+ overrides: c.overrides || {},
531
+ extras: c.extras || [],
532
+ slots: c.slots || null,
533
+ link: c.link || null,
534
+ outdoor: !!c.outdoor,
535
+ ceiling: c.ceiling || null,
536
+ seedKey: c.seedKey
537
+ };
538
+ }
539
+ function usableParts(room, extra) {
540
+ const m = MARGIN + (extra || 0);
541
+ const out = [];
542
+ for (const p of room.parts) {
543
+ out.push([p[0] + m, p[1] + m, p[2] - m, p[3] - m]);
544
+ for (let k = 0; k < 4; k++) {
545
+ const spans = [];
546
+ for (const q of room.parts) {
547
+ if (q === p) continue;
548
+ if (k === 0 && eq(q[2], p[0])) spans.push([Math.max(q[1], p[1]), Math.min(q[3], p[3])]);
549
+ else if (k === 2 && eq(q[0], p[2])) spans.push([Math.max(q[1], p[1]), Math.min(q[3], p[3])]);
550
+ else if (k === 1 && eq(q[3], p[1])) spans.push([Math.max(q[0], p[0]), Math.min(q[2], p[2])]);
551
+ else if (k === 3 && eq(q[1], p[3])) spans.push([Math.max(q[0], p[0]), Math.min(q[2], p[2])]);
552
+ }
553
+ spans.sort((x, y) => x[0] - y[0]);
554
+ const merged = [];
555
+ for (const span of spans) {
556
+ const last = merged[merged.length - 1];
557
+ if (last && span[0] <= last[1] + TOL) last[1] = Math.max(last[1], span[1]);
558
+ else merged.push([span[0], span[1]]);
559
+ }
560
+ for (const [a, b] of merged) {
561
+ if (b - a <= 0.3) continue;
562
+ const x0 = a + m;
563
+ const x1 = b - m;
564
+ if (x1 <= x0) continue;
565
+ if (k === 0) out.push([p[0], x0, p[0] + m, x1]);
566
+ else if (k === 2) out.push([p[2] - m, x0, p[2], x1]);
567
+ else if (k === 1) out.push([x0, p[1], x1, p[1] + m]);
568
+ else out.push([x0, p[3] - m, x1, p[3]]);
569
+ }
570
+ }
571
+ }
572
+ return out;
573
+ }
574
+ 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);
575
+ function buildWalls(rooms, doors, openPairs, floorIndex) {
576
+ const groups = {};
577
+ const add = (axis, pos, a, b, id, side) => {
578
+ const k = axis + "|" + pos.toFixed(2);
579
+ (groups[k] = groups[k] || { axis, pos, es: [] }).es.push({ a, b, id, side });
580
+ };
581
+ for (const c of rooms)
582
+ for (const p of c.parts) {
583
+ add("u", p[1], p[0], p[2], c.id, 1);
584
+ add("u", p[3], p[0], p[2], c.id, -1);
585
+ add("v", p[0], p[1], p[3], c.id, 1);
586
+ add("v", p[2], p[1], p[3], c.id, -1);
587
+ }
588
+ const par = (x, y) => openPairs.some((o) => o[0] === x && o[1] === y || o[0] === y && o[1] === x);
589
+ const typeOf = {};
590
+ for (const c of rooms) typeOf[c.id] = c.type;
591
+ const out = [];
592
+ for (const g of Object.values(groups)) {
593
+ const breaks = [...new Set(g.es.flatMap((e) => [r2(e.a), r2(e.b)]))].sort((x, y) => x - y);
594
+ const segs = [];
595
+ for (let i = 0; i < breaks.length - 1; i++) {
596
+ const p = breaks[i];
597
+ const q = breaks[i + 1];
598
+ const m = (p + q) / 2;
599
+ const after = g.es.find((e) => e.side === 1 && m > e.a && m < e.b);
600
+ const before = g.es.find((e) => e.side === -1 && m > e.a && m < e.b);
601
+ if (!after && !before) continue;
602
+ const A = before ? before.id : "outside";
603
+ const B = after ? after.id : "outside";
604
+ if (A === B || A !== "outside" && B !== "outside" && par(A, B)) continue;
605
+ const last = segs[segs.length - 1];
606
+ if (last && last.A === A && last.B === B && eq(last.q, p)) last.q = q;
607
+ else segs.push({ A, B, p, q });
608
+ }
609
+ for (const s of segs) {
610
+ let pieces = [[s.p, s.q]];
611
+ for (const pt of doors) {
612
+ if (pt.axis !== g.axis || !eq(pt.pos, g.pos)) continue;
613
+ const c0 = pt.at - pt.width / 2;
614
+ const c1 = pt.at + pt.width / 2;
615
+ const n = [];
616
+ for (const [x, y] of pieces) {
617
+ if (c1 <= x + TOL || c0 >= y - TOL) {
618
+ n.push([x, y]);
619
+ continue;
620
+ }
621
+ if (c0 > x + TOL) n.push([x, c0]);
622
+ if (c1 < y - TOL) n.push([c1, y]);
623
+ }
624
+ pieces = n;
625
+ }
626
+ for (const [x, y] of pieces) {
627
+ const isOpen = (t) => t === "balcony" || t === "terrace" || t === "porch";
628
+ const isVoid = typeOf[s.A] === "void" || typeOf[s.B] === "void";
629
+ if (isVoid && (s.A === "outside" || s.B === "outside")) continue;
630
+ const railing = isVoid || isOpen(typeOf[s.A]) && s.B === "outside" || isOpen(typeOf[s.B]) && s.A === "outside";
631
+ out.push({
632
+ floor: floorIndex,
633
+ axis: g.axis,
634
+ u0: g.axis === "u" ? x : g.pos,
635
+ v0: g.axis === "u" ? g.pos : x,
636
+ u1: g.axis === "u" ? y : g.pos,
637
+ v1: g.axis === "u" ? g.pos : y,
638
+ thickness: WALL_THICKNESS,
639
+ sides: [s.A, s.B],
640
+ exterior: s.A === "outside" || s.B === "outside",
641
+ type: railing ? "railing" : "wall",
642
+ h: railing ? 1.1 : 0
643
+ });
644
+ }
645
+ }
646
+ }
647
+ return out;
648
+ }
649
+ var STEP = 0.05;
650
+ var MAX_CANDIDATES = 80;
651
+ function carCandidates(slot, item, car) {
652
+ const front = [slot.frontU, slot.frontV];
653
+ const zone = slot._cell.R;
654
+ const alongV = Math.abs(front[1]) === 1;
655
+ const [extentU, extentV] = extent([item.w, item.d], front);
656
+ const across = alongV ? extentU : extentV;
657
+ const along = alongV ? extentV : extentU;
658
+ const a = alongV ? 0 : 1;
659
+ const b = alongV ? 1 : 0;
660
+ const driverSign = alongV ? front[1] : -front[0];
661
+ const frontSign = alongV ? front[1] : front[0];
662
+ const lowClear = driverSign < 0 ? car.driver : car.side;
663
+ const highClear = driverSign < 0 ? car.side : car.driver;
664
+ const lo = zone[a] + lowClear;
665
+ const hi = zone[a + 2] - highClear - across;
666
+ const alongLo = zone[b] + car.end;
667
+ const alongHi = zone[b + 2] - car.end - along;
668
+ if (hi < lo - 1e-6 || alongHi < alongLo - 1e-6) return [];
669
+ const preferred = (alongV ? slot.x : slot.z) - across / 2;
670
+ const alongStart = frontSign < 0 ? alongHi : alongLo;
671
+ const positions = [];
672
+ for (let p = lo; p <= hi + 1e-6; p += STEP) positions.push(Math.min(p, hi));
673
+ positions.sort((x, y) => Math.abs(x - preferred) - Math.abs(y - preferred));
674
+ return positions.slice(0, MAX_CANDIDATES).map((p) => {
675
+ const rect = alongV ? [p, alongStart, p + across, alongStart + along] : [alongStart, p, alongStart + along, p + across];
676
+ 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];
677
+ return { rect, clear };
678
+ });
679
+ }
680
+ function zonesOfFloor(plan, floorIndex) {
681
+ const out = [];
682
+ if (floorIndex >= 0) {
683
+ for (const p of plan.doors) if (p.floor === floorIndex) out.push(...p.zones);
684
+ } else {
685
+ for (const p of plan.doors) if (p.floor === 0 && p.exterior) out.push(...p.zones);
686
+ for (const g of plan.gates) out.push(...g.zones);
687
+ for (const c of plan.paths) out.push(...c.rects);
688
+ out.push(...plan.reservations);
689
+ }
690
+ return out;
691
+ }
692
+ function openingsOfFloor(plan, floorIndex) {
693
+ if (floorIndex >= 0) return plan.doors.filter((p) => p.floor === floorIndex);
694
+ return plan.doors.filter((p) => p.floor === 0 && p.exterior).concat(plan.gates);
695
+ }
696
+ function windowZone(j, outside) {
697
+ const h = j.width / 2 + 0.1;
698
+ const depth = 0.5;
699
+ let n;
700
+ if (j.side === "front") n = [0, 1];
701
+ else if (j.side === "back") n = [0, -1];
702
+ else if (j.side === "left") n = [1, 0];
703
+ else n = [-1, 0];
704
+ if (outside) n = [-n[0], -n[1]];
705
+ const u = j.u;
706
+ const v = j.v;
707
+ if (j.axis === "u") return n[1] > 0 ? [u - h, v - MARGIN, u + h, v + depth] : [u - h, v - depth, u + h, v + MARGIN];
708
+ return n[0] > 0 ? [u - MARGIN, v - h, u + depth, v + h] : [u - depth, v - h, u + MARGIN, v + h];
709
+ }
710
+ function blocksWindow(it, win, overheadOff) {
711
+ const height = Math.max(it.h, it.windowHeight || 0, it.overhead && !overheadOff ? OVERHEAD_TOP : 0);
712
+ return !it.window || it.h > 1 + EPS || height > win.y0 - 0.05 + EPS;
713
+ }
714
+ function windowsOfRoom(plan, room) {
715
+ if (!room.outdoor) return plan.windows.filter((j) => j.room === room.id);
716
+ return plan.windows.filter(
717
+ (j) => j.floor === 0 && room.parts.some((p) => {
718
+ if (j.side === "front") return eq(p[3], j.pos) && j.u >= p[0] && j.u <= p[2];
719
+ if (j.side === "back") return eq(p[1], j.pos) && j.u >= p[0] && j.u <= p[2];
720
+ if (j.side === "left") return eq(p[2], j.pos) && j.v >= p[1] && j.v <= p[3];
721
+ return eq(p[0], j.pos) && j.v >= p[1] && j.v <= p[3];
722
+ })
723
+ );
724
+ }
725
+ function roomAt(plan, u, v, floorIndex) {
726
+ const list = floorIndex < 0 ? plan.zones : plan.rooms;
727
+ return list.find((c) => c.floor === floorIndex && inRect(c, u, v)) || null;
728
+ }
729
+ function isOpenSide(plan, room, pu, pv) {
730
+ const o = roomAt(plan, pu, pv, room.floor);
731
+ if (!o) return false;
732
+ if (o.id === room.id) return true;
733
+ if (room.outdoor) return true;
734
+ return (plan.openPairs[room.floor] || []).some((x) => x[0] === room.id && x[1] === o.id || x[1] === room.id && x[0] === o.id);
735
+ }
736
+ function wallBodies(plan, floor) {
737
+ const cache = plan.wallBodies = plan.wallBodies || {};
738
+ if (cache[floor]) return cache[floor];
739
+ const doors = plan.doors.filter((p) => p.floor === floor);
740
+ return cache[floor] = plan.walls.filter((p) => p.floor === floor && (p.type === "wall" || p.type === "railing")).map((p) => {
741
+ const a0 = p.axis === "u" ? p.u0 : p.v0;
742
+ const a1 = p.axis === "u" ? p.u1 : p.v1;
743
+ const pos = p.axis === "u" ? p.v0 : p.u0;
744
+ 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)));
745
+ const e0 = atDoor(a0) ? 0 : MARGIN;
746
+ const e1 = atDoor(a1) ? 0 : MARGIN;
747
+ return p.axis === "u" ? [a0 - e0, pos - MARGIN, a1 + e1, pos + MARGIN] : [pos - MARGIN, a0 - e0, pos + MARGIN, a1 + e1];
748
+ });
749
+ }
750
+ function createGrid(plan, room, ignored, radius = plan.playerRadius) {
751
+ const gs = room.outdoor ? 0.3 : 0.1;
752
+ let u0 = 1e9;
753
+ let v0 = 1e9;
754
+ let u1 = -1e9;
755
+ let v1 = -1e9;
756
+ for (const p of room.parts) {
757
+ u0 = Math.min(u0, p[0]);
758
+ v0 = Math.min(v0, p[1]);
759
+ u1 = Math.max(u1, p[2]);
760
+ v1 = Math.max(v1, p[3]);
761
+ }
762
+ const nx = Math.ceil((u1 - u0) / gs);
763
+ const nz = Math.ceil((v1 - v0) / gs);
764
+ const insideCells = new Uint8Array(nx * nz);
765
+ const insets = room.outdoor ? usableParts(room, 0.08) : room.parts;
766
+ const bodies = room.outdoor ? null : wallBodies(plan, room.floor).filter((r) => r[0] - radius <= u1 && r[2] + radius >= u0 && r[1] - radius <= v1 && r[3] + radius >= v0);
767
+ const tooClose = (u, v) => bodies.some((r) => {
768
+ const dx = Math.max(r[0] - u, 0, u - r[2]);
769
+ const dz = Math.max(r[1] - v, 0, v - r[3]);
770
+ return dx * dx + dz * dz < radius * radius - 1e-9;
771
+ });
772
+ for (let j = 0; j < nz; j++)
773
+ for (let i = 0; i < nx; i++) {
774
+ const u = u0 + (i + 0.5) * gs;
775
+ const v = v0 + (j + 0.5) * gs;
776
+ 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;
777
+ }
778
+ const entries = [];
779
+ for (const ab of openingsOfFloor(plan, room.floor))
780
+ if (!(ignored && ignored.has(ab.id)))
781
+ for (const pt of entryPoints(ab)) {
782
+ if (!inRect(room, pt[0], pt[1])) continue;
783
+ const i = Math.floor((pt[0] - u0) / gs);
784
+ const j = Math.floor((pt[1] - v0) / gs);
785
+ if (i >= 0 && j >= 0 && i < nx && j < nz && insideCells[j * nx + i]) entries.push([i, j]);
786
+ }
787
+ if (!room.outdoor)
788
+ for (const pair of plan.openPairs[room.floor] || []) {
789
+ const otherId = pair[0] === room.id ? pair[1] : pair[1] === room.id ? pair[0] : null;
790
+ const other = otherId && plan.rooms.find((c) => c.id === otherId && c.floor === room.floor);
791
+ if (!other) continue;
792
+ const edge = sharedEdge(room, other, null);
793
+ if (!edge || edge.a1 - edge.a0 > 2) continue;
794
+ const mid = (edge.a0 + edge.a1) / 2;
795
+ for (const offset of [-0.4, 0.4]) {
796
+ const pt = edge.axis === "u" ? [mid, edge.pos + offset] : [edge.pos + offset, mid];
797
+ if (!inRect(room, pt[0], pt[1])) continue;
798
+ const i = Math.floor((pt[0] - u0) / gs);
799
+ const j = Math.floor((pt[1] - v0) / gs);
800
+ if (i >= 0 && j >= 0 && i < nx && j < nz && insideCells[j * nx + i]) entries.push([i, j]);
801
+ }
802
+ }
803
+ const R = inner(room.parts[0]);
804
+ return { gs, u0, v0, nx, nz, inside: insideCells, entries, center: [(R[0] + R[2]) / 2, (R[1] + R[3]) / 2] };
805
+ }
806
+ function reachable(g, solids, inflation) {
807
+ const { gs, u0, v0, nx, nz, inside } = g;
808
+ const blocked = new Uint8Array(nx * nz);
809
+ for (const r of solids) {
810
+ const i0 = Math.max(0, Math.floor((r[0] - inflation - u0) / gs));
811
+ const i1 = Math.min(nx - 1, Math.floor((r[2] + inflation - u0) / gs));
812
+ const j0 = Math.max(0, Math.floor((r[1] - inflation - v0) / gs));
813
+ const j1 = Math.min(nz - 1, Math.floor((r[3] + inflation - v0) / gs));
814
+ for (let j = j0; j <= j1; j++)
815
+ for (let i = i0; i <= i1; i++) {
816
+ const u = u0 + (i + 0.5) * gs;
817
+ const v = v0 + (j + 0.5) * gs;
818
+ if (u > r[0] - inflation && u < r[2] + inflation && v > r[1] - inflation && v < r[3] + inflation) blocked[j * nx + i] = 1;
819
+ }
820
+ }
821
+ let best = -1;
822
+ let bestDist = 1e9;
823
+ for (let j = 0; j < nz; j++)
824
+ for (let i = 0; i < nx; i++) {
825
+ const k = j * nx + i;
826
+ if (!inside[k] || blocked[k]) continue;
827
+ const dist = Math.hypot(u0 + (i + 0.5) * gs - g.center[0], v0 + (j + 0.5) * gs - g.center[1]);
828
+ if (dist < bestDist) {
829
+ bestDist = dist;
830
+ best = k;
831
+ }
832
+ }
833
+ if (best < 0) return false;
834
+ if (!g.entries.length) return true;
835
+ for (const [i, j] of g.entries) if (blocked[j * nx + i]) return false;
836
+ const visited = new Uint8Array(nx * nz);
837
+ const queue = new Int32Array(nx * nz);
838
+ let h = 0;
839
+ let t = 0;
840
+ const s = g.entries[0][1] * nx + g.entries[0][0];
841
+ visited[s] = 1;
842
+ queue[t++] = s;
843
+ while (h < t) {
844
+ const k = queue[h++];
845
+ const i = k % nx;
846
+ const j = (k - i) / nx;
847
+ for (const [di, dj] of [[1, 0], [-1, 0], [0, 1], [0, -1]]) {
848
+ const a = i + di;
849
+ const b = j + dj;
850
+ if (a < 0 || b < 0 || a >= nx || b >= nz) continue;
851
+ const q = b * nx + a;
852
+ if (visited[q] || !inside[q] || blocked[q]) continue;
853
+ visited[q] = 1;
854
+ queue[t++] = q;
855
+ }
856
+ }
857
+ if (!visited[best]) return false;
858
+ for (const [i, j] of g.entries) if (!visited[j * nx + i]) return false;
859
+ return true;
860
+ }
861
+ function candidatesOf(cats) {
862
+ const out = [];
863
+ for (const [cat, weight] of Object.entries(cats)) {
864
+ const exact = cat[0] === "=";
865
+ const its = Object.values(registries().CATALOG).filter((i) => !i.decor && (exact ? i.type === cat.slice(1) : !i.exactOnly && (i.category === cat || i.type === cat)));
866
+ for (const it of its) out.push({ it, weight: weight / its.length });
867
+ }
868
+ return out;
869
+ }
870
+ function itemRect(slot, it, shift) {
871
+ const c = slot._cell;
872
+ const f = c.f;
873
+ const [extentU, extentV] = extent([it.w, it.d], f);
874
+ const R = c.R;
875
+ let u0 = (c.rect ? (c.rect[0] + c.rect[2]) / 2 : slot.x) - extentU / 2;
876
+ let v0 = (c.rect ? (c.rect[1] + c.rect[3]) / 2 : slot.z) - extentV / 2;
877
+ if (shift) {
878
+ if (slot.side === "left" || slot.side === "right") v0 += shift;
879
+ else u0 += shift;
880
+ }
881
+ for (const s of c.enclosed) {
882
+ if (s === "left") u0 = R[0];
883
+ else if (s === "right") u0 = R[2] - extentU;
884
+ else if (s === "front") v0 = R[1];
885
+ else if (s === "back") v0 = R[3] - extentV;
886
+ }
887
+ return [u0, v0, u0 + extentU, v0 + extentV];
888
+ }
889
+ var RELAXED_RADIUS = 0.12;
890
+ var MESH_FRONT = { "+z": [0, 1], "-z": [0, -1], "+x": [1, 0], "-x": [-1, 0] };
891
+ function rotationFor(f, front) {
892
+ const meshFront = MESH_FRONT[front] || [0, 1];
893
+ let ry = Math.atan2(f[0], f[1]) - Math.atan2(meshFront[0], meshFront[1]);
894
+ if (ry > Math.PI + EPS) ry -= 2 * Math.PI;
895
+ if (ry < -Math.PI - EPS) ry += 2 * Math.PI;
896
+ return r2(ry);
897
+ }
898
+ function fillRoom(plan, room, slots2, rng) {
899
+ const items = [];
900
+ const missing = [];
901
+ const state = {};
902
+ const solids = [];
903
+ const reserves = [];
904
+ const rims = [];
905
+ const inners = usableParts(room, 0);
906
+ const grid = createGrid(plan, room);
907
+ const relaxedGrid = room.outdoor ? grid : createGrid(plan, room, null, RELAXED_RADIUS);
908
+ const zones = zonesOfFloor(plan, room.floor);
909
+ const windowZones = windowsOfRoom(plan, room).map((j) => ({ z: windowZone(j, room.outdoor), j }));
910
+ const inflation = room.outdoor ? 0.15 : plan.playerRadius;
911
+ const units = [];
912
+ const seen = {};
913
+ for (const s of slots2) {
914
+ if (s.group) {
915
+ if (seen[s.group]) {
916
+ seen[s.group].members.push(s);
917
+ continue;
918
+ }
919
+ const u = { group: s.group, members: [s] };
920
+ seen[s.group] = u;
921
+ units.push(u);
922
+ } else units.push({ group: null, members: [s] });
923
+ }
924
+ for (const u of units) u.required = u.members.some((m) => m.required);
925
+ const queue = units.filter((u) => u.required).concat(units.filter((u) => !u.required));
926
+ 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)));
927
+ const done = /* @__PURE__ */ new Set();
928
+ const ready = (u) => dependencies(u).every((id) => done.has(id));
929
+ const make = (s, it, r, parent, overheadOff) => {
930
+ const f = [s.frontU, s.frontV];
931
+ const id = room.id + "." + s.id;
932
+ const m = {
933
+ id,
934
+ type: it.type,
935
+ category: it.category,
936
+ x: r2((r[0] + r[2]) / 2),
937
+ z: r2((r[1] + r[3]) / 2),
938
+ ry: rotationFor(f, it.front),
939
+ w: it.w,
940
+ d: it.d,
941
+ h: it.h,
942
+ y: parent ? r2((parent.y || 0) + parent.h) : 0,
943
+ frontU: f[0],
944
+ frontV: f[1],
945
+ room: room.id,
946
+ floor: room.floor,
947
+ outdoor: !!room.outdoor,
948
+ slot: s.id,
949
+ on: parent ? s.on : null,
950
+ mode: parent ? s.anchor === "inside" ? "inside" : "on" : null,
951
+ clearance: s.clearance,
952
+ flat: it.flat,
953
+ low: it.low,
954
+ aabb: r.map(r2),
955
+ _r: r,
956
+ _seedId: registries().SEED_KEYS.items[id] ?? id
957
+ };
958
+ if (it.overhead) m.overhead = !overheadOff;
959
+ if (it.category === "car") {
960
+ m.driver = [f[1], -f[0]];
961
+ m.carClearance = plan.carClearance;
962
+ }
963
+ if (parent && s.anchor === "inside") m.y = r2(parent.y || 0);
964
+ if (room.link) m.link = { from: room.link[0], to: room.link[1] };
965
+ state[s.id] = m;
966
+ items.push(m);
967
+ return m;
968
+ };
969
+ const tryFit = (s, relax, overheadOff) => {
970
+ const cands = weightedOrder(candidatesOf(s.cats), rng);
971
+ if (s.anchor === "on" || s.anchor === "inside") {
972
+ const parent = state[s.on];
973
+ if (!parent) return null;
974
+ for (const { it } of cands) {
975
+ if (it.w > parent.w + EPS || it.d > parent.d + EPS) continue;
976
+ const f = [parent.frontU, parent.frontV];
977
+ const r = parent.aabb;
978
+ const [extentU, extentV] = extent([it.w, it.d], f);
979
+ const cu = (r[0] + r[2]) / 2 - f[0] * (parent.d / 2 - it.d / 2);
980
+ const cv = (r[1] + r[3]) / 2 - f[1] * (parent.d / 2 - it.d / 2);
981
+ return make(s, it, [cu - extentU / 2, cv - extentV / 2, cu + extentU / 2, cv + extentV / 2], parent);
982
+ }
983
+ return null;
984
+ }
985
+ for (const { it } of cands) {
986
+ if (it.w > s.w + EPS || it.d > s.d + EPS) continue;
987
+ const f = [s.frontU, s.frontV];
988
+ const slide = s.anchor === "wall" ? s.slide == null ? 2 : s.slide : 0;
989
+ const shifts = [0];
990
+ for (let k = 1; k * 0.1 <= slide + 1e-9; k++) shifts.push(k * 0.1, -k * 0.1);
991
+ let found = null;
992
+ const car = it.category === "car" && !relax;
993
+ const tries = car ? carCandidates(s, it, plan.carClearance) : shifts.map((shift) => ({ rect: itemRect(s, it, shift) }));
994
+ for (const attempt of tries) {
995
+ const r3 = attempt.rect;
996
+ if (!insideUnion(r3, inners)) continue;
997
+ if (car && (!insideUnion(attempt.clear, inners) || solids.some((o) => overlaps(attempt.clear, o)))) continue;
998
+ if (it.flat) {
999
+ found = { rect: r3 };
1000
+ break;
1001
+ }
1002
+ if (zones.some((z) => overlaps(r3, z))) continue;
1003
+ if (windowZones.some(({ z, j }) => blocksWindow(it, j, overheadOff) && overlaps(r3, z))) continue;
1004
+ if (solids.some((o) => overlaps(r3, o))) continue;
1005
+ if (!it.deck && rims.some((a) => overlaps(r3, a))) continue;
1006
+ let cl2 = car ? attempt.clear : null;
1007
+ if (!car && !relax && s.clearance > 0 && !it.low) {
1008
+ cl2 = clearRect(r3, f, s.clearance);
1009
+ if (!insideUnion(cl2, inners)) continue;
1010
+ if (items.some((m2) => !m2.low && !m2.flat && !m2.on && overlaps(cl2, m2._r))) continue;
1011
+ }
1012
+ if (!it.low && reserves.some((q) => overlaps(r3, q))) continue;
1013
+ let rimRect2 = null;
1014
+ if (it.rim) {
1015
+ rimRect2 = [r3[0] - it.rim, r3[1] - it.rim, r3[2] + it.rim, r3[3] + it.rim];
1016
+ if (!insideUnion(rimRect2, inners) || solids.some((o) => overlaps(rimRect2, o)) || zones.some((z) => overlaps(rimRect2, z))) continue;
1017
+ }
1018
+ if (!(relax ? reachable(relaxedGrid, solids.concat([r3]), RELAXED_RADIUS) : reachable(grid, solids.concat([r3]), inflation))) continue;
1019
+ found = { rect: r3, cl: cl2, rimRect: rimRect2 };
1020
+ break;
1021
+ }
1022
+ if (!found) continue;
1023
+ const { rect: r, cl, rimRect } = found;
1024
+ if (it.flat) return make(s, it, r, null);
1025
+ const m = make(s, it, r, null, overheadOff);
1026
+ if (cl) reserves.push(cl);
1027
+ if (rimRect) rims.push(rimRect);
1028
+ solids.push(r);
1029
+ return m;
1030
+ }
1031
+ return null;
1032
+ };
1033
+ const process2 = (u) => {
1034
+ const pr = u.members[0].chance;
1035
+ const x = rng();
1036
+ const arr = u.members.slice();
1037
+ const members = [];
1038
+ while (arr.length) members.push(arr.splice(arr.length > 1 ? Math.floor(rng() * arr.length) : 0, 1)[0]);
1039
+ const applicable = members.filter((m) => m.valid && (!m.requires || state[m.requires]) && (!m.on || state[m.on]));
1040
+ if (!u.required && x >= pr) return;
1041
+ let ok = false;
1042
+ for (const m of applicable) {
1043
+ if (tryFit(m, false)) {
1044
+ ok = true;
1045
+ break;
1046
+ }
1047
+ }
1048
+ if (!ok)
1049
+ for (const m of applicable) {
1050
+ if (tryFit(m, false, true)) {
1051
+ ok = true;
1052
+ break;
1053
+ }
1054
+ }
1055
+ if (!ok && u.required)
1056
+ for (const m of applicable) {
1057
+ if (tryFit(m, true, true)) {
1058
+ ok = true;
1059
+ break;
1060
+ }
1061
+ }
1062
+ if (!ok && u.required && (applicable.length || !u.members.some((m) => m.requires))) missing.push({ room: room.id, slot: u.group || u.members[0].id });
1063
+ };
1064
+ const pending = queue.slice();
1065
+ while (pending.length) {
1066
+ let i = pending.findIndex(ready);
1067
+ if (i < 0) i = 0;
1068
+ const u = pending.splice(i, 1)[0];
1069
+ process2(u);
1070
+ for (const m of u.members) done.add(m.id);
1071
+ }
1072
+ return { items, missing };
1073
+ }
1074
+ var STREAM = {
1075
+ facade: "fachada",
1076
+ lot: "lote",
1077
+ decor: "decor",
1078
+ roomPrefix: "c:",
1079
+ palettePrefix: "paleta:"
1080
+ };
1081
+ var placementKit = {
1082
+ MARGIN,
1083
+ EPS,
1084
+ r2,
1085
+ clamp,
1086
+ overlaps,
1087
+ inner,
1088
+ inflate,
1089
+ areaOf,
1090
+ topOf,
1091
+ extent,
1092
+ insideUnion,
1093
+ usableParts,
1094
+ zonesOfFloor,
1095
+ windowsOfRoom,
1096
+ windowZone,
1097
+ roomAt,
1098
+ reachable,
1099
+ createGrid,
1100
+ clearRect,
1101
+ pickWeighted,
1102
+ createRng,
1103
+ roomSeedKey: (room) => room.seedKey ?? registries().SEED_KEYS.rooms[room.id] ?? room.id,
1104
+ shuffle,
1105
+ facingRotation: (f, type) => rotationFor(f, registries().CATALOG[type].front),
1106
+ cutIntervals,
1107
+ blocksWindow
1108
+ };
1109
+ function createPlacementContext(plan, items) {
1110
+ const added = [];
1111
+ const counters = {};
1112
+ const make = (room, type, r, f, o) => {
1113
+ const it = registries().CATALOG[type];
1114
+ const n = counters[room.id + type] = (counters[room.id + type] || 0) + 1;
1115
+ return Object.assign(
1116
+ {
1117
+ id: room.id + ".dec_" + type + n,
1118
+ type,
1119
+ category: it.category,
1120
+ x: r2((r[0] + r[2]) / 2),
1121
+ z: r2((r[1] + r[3]) / 2),
1122
+ ry: r2(Math.atan2(f[0], f[1])),
1123
+ w: it.w,
1124
+ d: it.d,
1125
+ h: it.h,
1126
+ y: 0,
1127
+ frontU: f[0],
1128
+ frontV: f[1],
1129
+ room: room.id,
1130
+ floor: room.floor,
1131
+ outdoor: !!room.outdoor,
1132
+ slot: "dec_" + type + n,
1133
+ on: null,
1134
+ mode: null,
1135
+ clearance: 0,
1136
+ flat: !!it.flat,
1137
+ low: it.low,
1138
+ aabb: r.map(r2),
1139
+ _r: r,
1140
+ decor: true,
1141
+ mount: it.mount
1142
+ },
1143
+ o || {}
1144
+ );
1145
+ };
1146
+ const add = (m) => {
1147
+ added.push(m);
1148
+ items.push(m);
1149
+ return m;
1150
+ };
1151
+ const itemsOfRoom = (c) => items.filter((m) => m.room === c.id && m.floor === c.floor);
1152
+ const extraSolids = {};
1153
+ const solidsOfRoom = (c) => itemsOfRoom(c).filter((m) => !m.flat && !m.on && (!m.decor || m.mount === "floor")).map((m) => m._r).concat(extraSolids[c.id] || []);
1154
+ const gridCache = {};
1155
+ let ignored = null;
1156
+ const gridOf = (c) => gridCache[c.id] || (gridCache[c.id] = createGrid(plan, c, ignored));
1157
+ function placeOnFloor(room, type, rng2, optsIn) {
1158
+ const opts = Object.assign({}, optsIn || {});
1159
+ const it0 = registries().CATALOG[type];
1160
+ if (it0.flat) opts.flat = true;
1161
+ const it = registries().CATALOG[type];
1162
+ const inners = usableParts(room, 0);
1163
+ const solidList = solidsOfRoom(room);
1164
+ const zones = zonesOfFloor(plan, room.floor);
1165
+ const occupied = itemsOfRoom(room);
1166
+ const windowZones = windowsOfRoom(plan, room).map((j) => ({ z: windowZone(j, room.outdoor), j }));
1167
+ const landings = it0.flat ? [] : items.filter((m) => m.type === "stairs" && m.floor === room.floor && m.aabb).map((m) => {
1168
+ const r = m.aabb;
1169
+ 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]];
1170
+ });
1171
+ const cand = opts.cands ? opts.cands.slice() : [];
1172
+ if (!opts.cands)
1173
+ for (const p of inners) {
1174
+ const extentU = opts.dim ? opts.dim[0] : it.w;
1175
+ const extentV = opts.dim ? opts.dim[1] : it.d;
1176
+ for (const [x, z] of [[p[0], p[1]], [p[2] - extentU, p[1]], [p[0], p[3] - extentV], [p[2] - extentU, p[3] - extentV]]) cand.push([x, z, x + extentU, z + extentV]);
1177
+ for (let k = 0; k < 6; k++) {
1178
+ const t = rng2();
1179
+ const side = Math.floor(rng2() * 4);
1180
+ const x = side < 2 ? p[0] + t * Math.max(0, p[2] - p[0] - extentU) : side === 2 ? p[0] : p[2] - extentU;
1181
+ const z = side >= 2 ? p[1] + t * Math.max(0, p[3] - p[1] - extentV) : side === 0 ? p[1] : p[3] - extentV;
1182
+ cand.push([x, z, x + extentU, z + extentV]);
1183
+ }
1184
+ }
1185
+ for (const r of opts.ordered ? cand : shuffle(cand, rng2)) {
1186
+ if (!insideUnion(r, inners)) continue;
1187
+ if (zones.some((z) => overlaps(r, z)) || landings.some((z) => overlaps(r, z))) continue;
1188
+ if (windowZones.some(({ z, j }) => blocksWindow(it, j) && overlaps(r, z))) continue;
1189
+ if (solidList.some((o) => overlaps(r, o))) continue;
1190
+ if (opts.flat) {
1191
+ if (!(opts.without && opts.without(r))) return r;
1192
+ continue;
1193
+ }
1194
+ 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;
1195
+ if (opts.without && opts.without(r)) continue;
1196
+ if (!reachable(opts.railing || gridOf(room), solidList.concat([r]), room.outdoor ? 0.16 : plan.playerRadius)) continue;
1197
+ return r;
1198
+ }
1199
+ return null;
1200
+ }
1201
+ const roomById = (id) => plan.rooms.find((c) => c.id === id) || plan.zones.find((c) => c.id === id);
1202
+ return {
1203
+ make,
1204
+ add,
1205
+ placeOnFloor,
1206
+ itemsOfRoom,
1207
+ solidsOfRoom,
1208
+ gridOf,
1209
+ extraSolids,
1210
+ roomById,
1211
+ setIgnored: (s) => {
1212
+ ignored = s;
1213
+ for (const k of Object.keys(gridCache)) delete gridCache[k];
1214
+ },
1215
+ added
1216
+ };
1217
+ }
1218
+ function groundCover(plan, items, ctx) {
1219
+ if (!plan.lot) return;
1220
+ const polygonOf = (r) => [[r[0], r[1]], [r[2], r[1]], [r[2], r[3]], [r[0], r[3]]].map((p) => [r2(p[0]), r2(p[1])]);
1221
+ const groundPiece = (z, type, r, layer, i) => {
1222
+ const m = ctx.make(z, type, r, [0, 1], {
1223
+ id: z.id + ".ground" + (layer ? "_overlay" : "") + i,
1224
+ slot: "ground" + (layer ? "_overlay" : "") + i,
1225
+ w: r2(r[2] - r[0]),
1226
+ d: r2(r[3] - r[1]),
1227
+ h: 0,
1228
+ polygon: polygonOf(r),
1229
+ layer,
1230
+ zone: z.id,
1231
+ outdoor: true,
1232
+ floor: -1
1233
+ });
1234
+ return ctx.add(m);
1235
+ };
1236
+ for (const z of plan.zones) {
1237
+ z.parts.forEach((p, i) => groundPiece(z, z.ground, p, 0, i));
1238
+ let k = 0;
1239
+ 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];
1240
+ for (const rule of registries().GROUND_OVERLAYS) {
1241
+ const triggers = items.filter((m) => m.room === z.id && m.category === rule.category);
1242
+ if (rule.merge) {
1243
+ if (triggers.length && z.ground !== rule.unlessGround) {
1244
+ const u = triggers.reduce((a, m) => [Math.min(a[0], m._r[0]), Math.min(a[1], m._r[1]), Math.max(a[2], m._r[2]), Math.max(a[3], m._r[3])], [1e9, 1e9, -1e9, -1e9]);
1245
+ const q = clip(inflate(u, rule.inflate));
1246
+ if (q) groundPiece(z, rule.ground, q, 1, k++);
1247
+ }
1248
+ } else {
1249
+ for (const m of triggers) {
1250
+ const q = clip(inflate(m._r, rule.inflate));
1251
+ if (q) groundPiece(z, rule.ground, q, 1, k++);
1252
+ }
1253
+ }
1254
+ }
1255
+ }
1256
+ }
1257
+ function runPostProcess(plan, items, seed) {
1258
+ const ctx = createPlacementContext(plan, items);
1259
+ const rng = createRng(seed, STREAM.decor);
1260
+ for (const pass of registries().PASSES) pass.run({ plan, items, seed, rng, ctx, kit: placementKit, catalog: registries().CATALOG, profiles: registries().DECOR_PROFILES });
1261
+ groundCover(plan, items, ctx);
1262
+ return ctx;
1263
+ }
112
1264
  function registerLocale(code, table) {
1265
+ const { LOCALES: locales } = registries();
113
1266
  const cur = locales[code] = locales[code] || {};
114
1267
  for (const ns of Object.keys(table)) cur[ns] = Object.assign(cur[ns] || {}, table[ns]);
115
1268
  }
116
- var PERMEABLE_GROUNDS = /* @__PURE__ */ new Set();
1269
+ var listLocales = () => Object.keys(registries().LOCALES);
1270
+ function translate(ns, key, locale = "en", vars) {
1271
+ const { LOCALES: locales } = registries();
1272
+ const raw = locales[locale]?.[ns]?.[key] ?? locales.en?.[ns]?.[key] ?? key;
1273
+ return vars ? raw.replace(/\{(\w+)\}/g, (m, k) => k in vars ? String(vars[k]) : m) : raw;
1274
+ }
1275
+ var CLOSE = 1e-6;
1276
+ var rounded = (r) => r.map(r2);
1277
+ var touches = (a, r) => a[0] <= r[2] + CLOSE && a[2] >= r[0] - CLOSE && a[1] <= r[3] + CLOSE && a[3] >= r[1] - CLOSE;
1278
+ function addExteriorDoorPaths(plan, c) {
1279
+ const { gates, paths, zones, setback, marginLeft, marginRight, backyard, w } = c;
1280
+ const social = gates.find((g) => g.type === "pedestrian");
1281
+ const socialPath = social ? paths.find((p) => p.from === social.id && p.type === "pedestrian") : null;
1282
+ const parkingZone = zones.find((z) => z.id === "parking");
1283
+ const parking = parkingZone ? parkingZone.parts[0] : null;
1284
+ const midY = setback > 0 ? -setback / 2 : 0;
1285
+ const joinToSocial = (x, width, rects, points) => {
1286
+ if (!socialPath) return;
1287
+ const h = width / 2;
1288
+ 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];
1289
+ const targetX = x < horizontal[0] ? horizontal[0] + h : x > horizontal[2] ? horizontal[2] - h : x;
1290
+ if (Math.abs(targetX - x) > CLOSE) {
1291
+ rects.push([Math.min(x, targetX) - h, midY - h, Math.max(x, targetX) + h, midY + h]);
1292
+ points.push([targetX, midY]);
1293
+ }
1294
+ };
1295
+ for (const door of plan.doors.filter((q) => q.floor === 0 && q.exterior)) {
1296
+ if (paths.some((p) => p.to === door.id)) continue;
1297
+ const half = Math.max(1, Math.min(door.width, 2)) / 2;
1298
+ const rects = [];
1299
+ const points = [];
1300
+ const isTarget = c.target && door.a === c.target.a && door.side === c.target.side;
1301
+ let fromId = social ? social.id : null;
1302
+ let width = 1;
1303
+ const id = isTarget ? "entrance_path" : "path_" + door.id;
1304
+ if (door.side === "front") {
1305
+ if (setback > 0) {
1306
+ const stub = Math.min(1, setback);
1307
+ rects.push([door.u - half, -stub, door.u + half, 0]);
1308
+ points.push([door.u, 0], [door.u, -stub]);
1309
+ if (-stub > midY) {
1310
+ rects.push([door.u - width / 2, midY - width / 2, door.u + width / 2, -stub]);
1311
+ points.push([door.u, midY]);
1312
+ }
1313
+ joinToSocial(door.u, width, rects, points);
1314
+ } else {
1315
+ rects.push([door.u - half, -1.2, door.u + half, 0]);
1316
+ points.push([door.u, 0], [door.u, -1.2]);
1317
+ }
1318
+ } else {
1319
+ const lanes = [];
1320
+ if (marginLeft > 0.5) lanes.push({ x: -marginLeft / 2, width: marginLeft, side: "left" });
1321
+ if (marginRight > 0.5) lanes.push({ x: w + marginRight / 2, width: marginRight, side: "right" });
1322
+ const targets = paths.flatMap((p) => p.rects).concat(parking ? [parking] : []);
1323
+ let best = null;
1324
+ for (const lane of lanes) {
1325
+ if (door.side === "left" && lane.side !== "left" || door.side === "right" && lane.side !== "right") continue;
1326
+ const laneWidth = Math.min(0.9, lane.width - 0.1);
1327
+ const h = laneWidth / 2;
1328
+ const rearStrip = Math.min(0.8, backyard);
1329
+ const rearY = door.v + rearStrip + laneWidth / 2;
1330
+ const overlapsParking = !!parking && lane.x + h > parking[0] && lane.x - h < parking[2];
1331
+ const frontY = overlapsParking || setback <= 0 ? 0 : -0.6;
1332
+ let frontStub = null;
1333
+ if (!overlapsParking && setback > 0) {
1334
+ const dir = social && social.u < lane.x ? -1 : 1;
1335
+ const band = [-0.6 - h, -0.6 + h];
1336
+ let nearestX = social ? social.u : lane.x;
1337
+ let nearestGap = Infinity;
1338
+ for (const r of targets) {
1339
+ if (!(r[1] <= band[1] && r[3] >= band[0])) continue;
1340
+ const edge = dir < 0 ? r[2] : r[0];
1341
+ const gap = Math.abs(edge - lane.x);
1342
+ if ((dir < 0 ? edge <= lane.x : edge >= lane.x) && gap < nearestGap) {
1343
+ nearestGap = gap;
1344
+ nearestX = edge;
1345
+ }
1346
+ }
1347
+ 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];
1348
+ }
1349
+ const vertical = [lane.x - h, frontY - (frontY < 0 ? h : 0), lane.x + h, (door.side === "back" ? rearY : door.v) + laneWidth / 2];
1350
+ 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;
1351
+ const cost = (horizontal ? horizontal[2] - horizontal[0] : 0) + (frontStub ? frontStub[2] - frontStub[0] : 0);
1352
+ if (!best || cost < best.cost) best = { lane, laneWidth, half: h, vertical, horizontal, frontStub, cost, rearStrip, rearY, frontY, overlapsParking };
1353
+ }
1354
+ if (best) {
1355
+ const { lane, laneWidth, half: h, vertical, horizontal, frontStub, rearStrip, rearY, frontY, overlapsParking } = best;
1356
+ width = laneWidth;
1357
+ if (overlapsParking) fromId = (gates.find((g) => g.type === "garage") || social || {}).id || null;
1358
+ if (door.side === "back") {
1359
+ rects.push([door.u - half, door.v, door.u + half, door.v + rearStrip + laneWidth], horizontal, vertical);
1360
+ points.push([door.u, door.v], [door.u, rearY], [lane.x, rearY], [lane.x, frontY]);
1361
+ } else {
1362
+ const x0 = door.side === "left" ? lane.x - h : w;
1363
+ const x1 = door.side === "left" ? 0 : lane.x + h;
1364
+ rects.push([Math.min(x0, x1), door.v - half, Math.max(x0, x1), door.v + half], vertical);
1365
+ points.push([door.u, door.v], [lane.x, door.v], [lane.x, frontY]);
1366
+ }
1367
+ if (frontStub) {
1368
+ rects.push(frontStub);
1369
+ points.push([frontStub[0] < lane.x ? frontStub[0] : frontStub[2], -0.6]);
1370
+ }
1371
+ } else {
1372
+ const stub = 1;
1373
+ if (door.side === "back") rects.push([door.u - half, door.v, door.u + half, door.v + Math.min(stub, backyard)]);
1374
+ 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]);
1375
+ points.push([door.u, door.v]);
1376
+ }
1377
+ }
1378
+ const reached = rects.slice();
1379
+ const pool = paths.flatMap((p) => p.rects).concat(parking ? [parking] : []);
1380
+ let grows = true;
1381
+ while (grows) {
1382
+ grows = false;
1383
+ for (const r of pool) {
1384
+ if (!reached.includes(r) && reached.some((a) => touches(a, r))) {
1385
+ reached.push(r);
1386
+ grows = true;
1387
+ }
1388
+ }
1389
+ }
1390
+ 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);
1391
+ const gate = gates.find((q) => q.type === "pedestrian" && inside(q)) || gates.find((q) => q.type !== "cargo" && inside(q));
1392
+ if (gate) fromId = gate.id;
1393
+ paths.push({ id, type: "pedestrian", from: fromId, to: door.id, width, rects: rects.map(rounded), points: points.map((q) => q.map(r2)) });
1394
+ }
1395
+ }
1396
+ function defineZone(zone) {
1397
+ if (!zone || !zone.id) throw new Error("defineZone: id is required");
1398
+ registries().ZONES[zone.id] = zone;
1399
+ return zone;
1400
+ }
1401
+ var getZone = (id) => registries().ZONES[id];
1402
+ var listZones = () => Object.values(registries().ZONES);
117
1403
  function registerPermeableGround(...types) {
118
- for (const t of types) PERMEABLE_GROUNDS.add(t);
1404
+ for (const t of types) registries().PERMEABLE_GROUNDS.add(t);
1405
+ }
1406
+ function resolveZone(z) {
1407
+ if (!z) return null;
1408
+ if (typeof z === "string") {
1409
+ const found = registries().ZONES[z];
1410
+ if (!found) throw new Error("Unknown zone: " + z);
1411
+ return found;
1412
+ }
1413
+ return z;
1414
+ }
1415
+ var floorsOfTemplate = (tpl, op) => (tpl.floorsFn ? tpl.floorsFn(op) : tpl.floors || []).length;
1416
+ var useOf = (tpl) => tpl.use || "residential_single";
1417
+ function measure(plan) {
1418
+ const floors = plan.floors;
1419
+ const top = floors.reduce((m, f) => Math.max(m, f.y0 + f.ceiling), 0);
1420
+ const number = plan.floorNumber != null ? plan.floorNumber : floors.length;
1421
+ const rooms = (plan.rooms || []).filter((r) => r.floor >= 0);
1422
+ const area = (r) => r.parts.reduce((s, p) => s + (p[2] - p[0]) * (p[3] - p[1]), 0);
1423
+ const footprint = plan.w * plan.d;
1424
+ const built = rooms.reduce((s, r) => s + area(r), 0);
1425
+ return { storeys: number, height: r2(top), footprint, built };
1426
+ }
1427
+ function checkLot(zoneIn, lot, plan) {
1428
+ const zone = resolveZone(zoneIn);
1429
+ const out = [];
1430
+ const m = measure(plan);
1431
+ const use = plan.use || "residential_single";
1432
+ if (!zone.allowedUses.includes(use)) out.push({ rule: "use", actual: use, limit: zone.allowedUses.join(",") });
1433
+ if (m.storeys > zone.maxFloors) out.push({ rule: "floors", actual: m.storeys, limit: zone.maxFloors });
1434
+ if (m.height > zone.maxHeight + 1e-9) out.push({ rule: "height", actual: m.height, limit: zone.maxHeight });
1435
+ if (!lot) return out;
1436
+ const [u0, v0, u1, v1] = lot.rect;
1437
+ const width = r2(u1 - u0);
1438
+ const depth = r2(v1 - v0);
1439
+ const area = width * depth;
1440
+ const party = plan.partyWalls || [];
1441
+ const sides = [party.includes("left") ? Infinity : lot.sideYard.left, party.includes("right") ? Infinity : lot.sideYard.right];
1442
+ const left = sides[0];
1443
+ const right = sides[1];
1444
+ if (lot.setback < zone.setbacks.front - 1e-9) out.push({ rule: "setback_front", actual: lot.setback, limit: zone.setbacks.front });
1445
+ if (Math.min(left, right) < zone.setbacks.side - 1e-9) out.push({ rule: "setback_side", actual: Math.min(left, right), limit: zone.setbacks.side });
1446
+ if (lot.backyard < zone.setbacks.back - 1e-9) out.push({ rule: "setback_back", actual: lot.backyard, limit: zone.setbacks.back });
1447
+ if (area < zone.minLot - 1e-9) out.push({ rule: "min_lot", actual: r2(area), limit: zone.minLot });
1448
+ if (width < zone.minFrontage - 1e-9) out.push({ rule: "min_frontage", actual: width, limit: zone.minFrontage });
1449
+ const ground = m.footprint;
1450
+ const coverage = ground / area;
1451
+ if (coverage > zone.maxCoverage + 1e-9) out.push({ rule: "coverage", actual: r2(coverage), limit: zone.maxCoverage });
1452
+ const far = m.built / area;
1453
+ if (far > zone.maxFar + 1e-9) out.push({ rule: "far", actual: r2(far), limit: zone.maxFar });
1454
+ let permeable = 0;
1455
+ for (const z of lot.zones) if (registries().PERMEABLE_GROUNDS.has(z.ground)) for (const p of z.parts) permeable += (p[2] - p[0]) * (p[3] - p[1]);
1456
+ const permeability = permeable / area;
1457
+ if (permeability < zone.permeableMin - 1e-9) out.push({ rule: "permeable", actual: r2(permeability), limit: zone.permeableMin });
1458
+ return out;
1459
+ }
1460
+ function clampOptionsToZone(tpl, op, zone, adjustments) {
1461
+ if (tpl.singleFloor) {
1462
+ const ceiling2 = (tpl.floors || tpl.floorsFn?.(op) || [])[0]?.ceiling ?? 2.8;
1463
+ const current2 = op.floor || 3;
1464
+ const limit2 = Math.max(1, Math.min(zone.maxFloors, Math.floor(zone.maxHeight / ceiling2 + 1e-9)));
1465
+ if (current2 <= limit2) return op;
1466
+ adjustments.push({ field: "floor", from: current2, to: limit2, reason: current2 > zone.maxFloors ? "max_floors" : "max_height" });
1467
+ return Object.assign({}, op, { floor: limit2 });
1468
+ }
1469
+ if (!tpl.floorRange || !tpl.floorsFn) return op;
1470
+ const current = floorsOfTemplate(tpl, op);
1471
+ const floors = tpl.floorsFn(op);
1472
+ const ceiling = floors.reduce((m, f) => Math.max(m, f.ceiling), 0) || 2.8;
1473
+ const byHeight = Math.max(1, Math.floor(zone.maxHeight / ceiling + 1e-9));
1474
+ const limit = Math.max(tpl.floorRange[0], Math.min(zone.maxFloors, byHeight, tpl.floorRange[1]));
1475
+ if (current <= limit) return op;
1476
+ adjustments.push({ field: "floors", from: current, to: limit, reason: current > zone.maxFloors ? "max_floors" : "max_height" });
1477
+ return Object.assign({}, op, { floors: limit });
1478
+ }
1479
+ function clampLotToZone(cfg, zone, adjustments, partyWalls = []) {
1480
+ const raise = (field, key, min, reason) => {
1481
+ if (cfg[key] < min) {
1482
+ adjustments.push({ field, from: cfg[key], to: min, reason });
1483
+ cfg[key] = min;
1484
+ }
1485
+ };
1486
+ raise("frontSetback", "frontSetback", zone.setbacks.front, "setback_front");
1487
+ if (!partyWalls.includes("left")) raise("marginLeft", "marginLeft", zone.setbacks.side, "setback_side");
1488
+ if (!partyWalls.includes("right")) raise("marginRight", "marginRight", zone.setbacks.side, "setback_side");
1489
+ raise("backyardDepth", "backyardDepth", zone.setbacks.back, "setback_back");
1490
+ }
1491
+ var ADJUSTABLE_ZONES = /* @__PURE__ */ new Set(["garden", "backyard", "side_yard"]);
1492
+ function enforcePermeability(lot, zones, zone, adjustments) {
1493
+ const target = [...registries().PERMEABLE_GROUNDS][0];
1494
+ if (!target) return;
1495
+ const [u0, v0, u1, v1] = lot.rect;
1496
+ const area = (u1 - u0) * (v1 - v0);
1497
+ const areaOfZone = (z) => z.parts.reduce((s, p) => s + (p[2] - p[0]) * (p[3] - p[1]), 0);
1498
+ let permeable = zones.filter((z) => registries().PERMEABLE_GROUNDS.has(z.ground)).reduce((s, z) => s + areaOfZone(z), 0);
1499
+ if (permeable / area >= zone.permeableMin) return;
1500
+ const candidates = zones.filter((z) => ADJUSTABLE_ZONES.has(z.type) && !registries().PERMEABLE_GROUNDS.has(z.ground)).sort((a, b) => areaOfZone(b) - areaOfZone(a));
1501
+ for (const z of candidates) {
1502
+ if (permeable / area >= zone.permeableMin) break;
1503
+ adjustments.push({ field: "ground:" + z.id, from: z.ground, to: target, reason: "permeable_min" });
1504
+ z.ground = target;
1505
+ permeable += areaOfZone(z);
1506
+ }
1507
+ }
1508
+ var floorsOf = (tpl, op) => tpl.floorsFn ? tpl.floorsFn(op || {}) : tpl.floors;
1509
+ function clampSize(tpl, w, d, op) {
1510
+ const L = tpl.limitsFn ? tpl.limitsFn(op || {}) : tpl.limits;
1511
+ const P0 = tpl.defaultsFn ? tpl.defaultsFn(op || {}) : tpl.defaults;
1512
+ const ww = w == null ? P0.w : clamp(w, L.w[0], L.w[1]);
1513
+ const dd = d == null ? P0.d : clamp(d, L.d[0], L.d[1]);
1514
+ return { w: r2(ww), d: r2(dd) };
1515
+ }
1516
+ function buildPlan(tpl, wIn, dIn, opIn, bc = {}) {
1517
+ const op = opIn || {};
1518
+ const { w, d } = clampSize(tpl, wIn, dIn, op);
1519
+ const styleId = resolveStyleId(op.style || (typeof tpl.style === "function" ? tpl.style(op) : tpl.style) || "budget");
1520
+ const FLOOR_DEFS = floorsOf(tpl, op);
1521
+ const style = styleOf(styleId);
1522
+ const plan = {
1523
+ w,
1524
+ d,
1525
+ tpl,
1526
+ floorDefs: FLOOR_DEFS,
1527
+ style: styleId,
1528
+ floors: [],
1529
+ rooms: [],
1530
+ zones: [],
1531
+ doors: [],
1532
+ windows: [],
1533
+ walls: [],
1534
+ gates: [],
1535
+ paths: [],
1536
+ lot: null,
1537
+ openPairs: {},
1538
+ reservations: [],
1539
+ options: op,
1540
+ playerRadius: op.playerRadius ?? PLAYER_RADIUS,
1541
+ carClearance: Object.assign({}, CAR_CLEARANCE, op.carClearance)
1542
+ };
1543
+ FLOOR_DEFS.forEach((A, floorIndex) => {
1544
+ const floorNumber = tpl.singleFloor ? op.floor || 3 : floorIndex + 1;
1545
+ plan.floors.push({
1546
+ index: floorIndex,
1547
+ name: tpl.singleFloor ? "floor_n" : A.label,
1548
+ number: tpl.singleFloor ? floorNumber : void 0,
1549
+ y0: tpl.singleFloor ? (floorNumber - 1) * A.ceiling : tpl.cumulativeCeilings ? FLOOR_DEFS.slice(0, floorIndex).reduce((t, q) => t + q.ceiling, 0) : floorIndex * A.ceiling,
1550
+ ceiling: A.ceiling
1551
+ });
1552
+ plan.openPairs[floorIndex] = A.openPairs || [];
1553
+ const list = A.rooms(w, d, op).map((c) => normalizeRoom(c, floorIndex));
1554
+ for (const c of list) {
1555
+ if (plan.rooms.some((x) => x.id === c.id)) throw new Error("Duplicate room: " + c.id);
1556
+ plan.rooms.push(c);
1557
+ }
1558
+ const byId2 = {};
1559
+ for (const c of list) byId2[c.id] = c;
1560
+ const floorDoors = [];
1561
+ (A.doors(w, d, op) || []).forEach((sp, i) => {
1562
+ const a = byId2[sp.a];
1563
+ if (!a) throw new Error("Door references a missing room: " + sp.a);
1564
+ let edge;
1565
+ if (sp.edge) edge = sp.edge;
1566
+ else if (sp.b === "outside") edge = exteriorEdge(a, sp.side, w, d, sp.at);
1567
+ else {
1568
+ const b = byId2[sp.b];
1569
+ if (!b) throw new Error("Door references a missing room: " + sp.b);
1570
+ edge = sharedEdge(a, b, sp.at);
1571
+ }
1572
+ if (!edge) throw new Error("Door without a wall: " + sp.a + " / " + sp.b);
1573
+ const width = sp.width;
1574
+ const margin = width / 2 + (sp.clearance ?? tpl.clearance?.door ?? DOOR_EDGE_CLEARANCE);
1575
+ if (edge.a1 - edge.a0 < width + 0.24) throw new Error("Wall too short for the door: " + sp.a + " / " + sp.b);
1576
+ const c = clamp(sp.at != null ? sp.at : (edge.a0 + edge.a1) / 2, edge.a0 + margin, edge.a1 - margin);
1577
+ const exterior = sp.b === "outside";
1578
+ const leaf = exterior && sp.steel ? 0 : width > 1.1 ? width / 2 : width;
1579
+ const u = edge.axis === "u" ? c : edge.pos;
1580
+ const v = edge.axis === "u" ? edge.pos : c;
1581
+ floorDoors.push({
1582
+ id: "d" + floorIndex + "_" + i,
1583
+ floor: floorIndex,
1584
+ u: r2(u),
1585
+ v: r2(v),
1586
+ axis: edge.axis,
1587
+ pos: edge.pos,
1588
+ at: r2(c),
1589
+ width,
1590
+ a: sp.a,
1591
+ b: sp.b,
1592
+ opensTo: sp.opensTo || sp.a,
1593
+ exterior,
1594
+ side: sp.side || null,
1595
+ leaves: exterior && sp.steel ? 0 : width > 1.1 ? 2 : 1,
1596
+ model: sp.model || (sp.steel ? "steel" : width > 1.1 ? "double" : "single"),
1597
+ type: sp.type || "door",
1598
+ steps: sp.steps || 0,
1599
+ hinge: "start",
1600
+ zones: openingZones(u, v, edge.axis, width, openingDepth(width, leaf))
1601
+ });
1602
+ });
1603
+ plan.doors.push(...floorDoors);
1604
+ const floorWalls = (tpl.noWalls ? [] : buildWalls(list, floorDoors, A.openPairs || [], floorIndex)).map((p) => Object.assign(p, { h: p.h || A.ceiling }));
1605
+ const windowsOut = placeWindows({ list, byId: byId2, floorWalls, windowDefs: A.windows(w, d, op) || [], floorIndex, ceiling: A.ceiling, w, d, clearance: tpl.clearance?.window ?? WINDOW_EDGE_CLEARANCE });
1606
+ plan.windows.push(...windowsOut);
1607
+ plan.walls.push(...floorWalls);
1608
+ });
1609
+ if (tpl.config) plan.config = tpl.config(op, w, d);
1610
+ if (tpl.reservations) tpl.reservations(plan);
1611
+ buildLot(plan, tpl, w, d, op, style, styleId, bc);
1612
+ return plan;
1613
+ }
1614
+ function placeWindows(params) {
1615
+ const { list, byId: byId2, floorWalls, windowDefs, floorIndex, ceiling, w, d, clearance } = params;
1616
+ const stretches = floorWalls.filter((p) => p.exterior && p.type === "wall");
1617
+ const found = [];
1618
+ const groups = [];
1619
+ let order = 0;
1620
+ for (const def of windowDefs) {
1621
+ const room = byId2[def.room];
1622
+ if (!room) throw new Error("Window references a missing room: " + def.room);
1623
+ const edge = exteriorEdge(room, def.side, w, d, def.at);
1624
+ if (!edge) continue;
1625
+ const centres = def.n ? Array.from({ length: def.n }, (_, i) => edge.a0 + (edge.a1 - edge.a0) * (i + 1) / (def.n + 1)) : [def.at];
1626
+ for (const centre of centres) {
1627
+ const onEdge = stretches.filter((t) => t.axis === edge.axis && eq(edge.axis === "u" ? t.v0 : t.u0, edge.pos) && t.sides.includes(def.room));
1628
+ const start = (t) => edge.axis === "u" ? t.u0 : t.v0;
1629
+ const end = (t) => edge.axis === "u" ? t.u1 : t.v1;
1630
+ const gap = (t) => Math.min(Math.abs(centre - start(t)), Math.abs(centre - end(t)));
1631
+ const target = onEdge.find((t) => centre >= start(t) - TOL && centre <= end(t) + TOL) || onEdge.slice().sort((x, y) => gap(x) - gap(y))[0];
1632
+ if (!target) continue;
1633
+ let group = groups.find((x) => x.wall === target && x.def.room === def.room);
1634
+ if (!group) groups.push(group = { wall: target, edge, start: start(target), end: end(target), def, entries: [] });
1635
+ group.entries.push({ def, centre, order: order++ });
1636
+ }
1637
+ }
1638
+ for (const g of groups) {
1639
+ const room = byId2[g.def.room];
1640
+ const bath = room.type === "bathroom";
1641
+ const length = g.end - g.start;
1642
+ 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 }));
1643
+ const fits = (l) => l.reduce((t, x) => t + x.width, 0) + (l.length + 1) * clearance <= length + TOL;
1644
+ while (its.length && !fits(its)) {
1645
+ const k = its.reduce((m, x, i, arr) => x.width > arr[m].width ? i : m, 0);
1646
+ if (its[k].width > 0.6 + TOL) its[k] = Object.assign({}, its[k], { width: Math.max(0.6, r2(its[k].width - 0.1)) });
1647
+ else its.pop();
1648
+ }
1649
+ if (!its.length) continue;
1650
+ const space = (length - its.reduce((t, x) => t + x.width, 0)) / (its.length + 1);
1651
+ let pos = g.start + space;
1652
+ for (const x of its) {
1653
+ const c = pos + x.width / 2;
1654
+ pos += x.width + space;
1655
+ const u = g.edge.axis === "u" ? c : g.edge.pos;
1656
+ const v = g.edge.axis === "u" ? g.edge.pos : c;
1657
+ let y0 = x.def.y0 || 1;
1658
+ let y1 = x.def.y1 || 2.15;
1659
+ if (bath) {
1660
+ y0 = Math.max(y0, 2.1);
1661
+ y1 = Math.min(Math.max(y1, y0 + 0.4), ceiling - 0.25);
1662
+ }
1663
+ 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 });
1664
+ }
1665
+ }
1666
+ for (const room of list) {
1667
+ if (WINDOWLESS_ROOM_TYPES.has(room.type) || found.some((j) => j.room === room.id)) continue;
1668
+ let best = null;
1669
+ for (const t of stretches) {
1670
+ if (!t.sides.includes(room.id)) continue;
1671
+ const i0 = t.axis === "u" ? t.u0 : t.v0;
1672
+ const i1 = t.axis === "u" ? t.u1 : t.v1;
1673
+ const length = i1 - i0;
1674
+ const width = Math.min(room.type === "bathroom" ? 0.6 : 1.2, length - 2 * clearance);
1675
+ if (width < 0.6 - TOL || best && best.length >= length) continue;
1676
+ const side = t.axis === "u" ? t.sides[0] === "outside" ? "front" : "back" : t.sides[0] === "outside" ? "left" : "right";
1677
+ const c = (i0 + i1) / 2;
1678
+ const pos = t.axis === "u" ? t.v0 : t.u0;
1679
+ const bath = room.type === "bathroom";
1680
+ 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 } };
1681
+ }
1682
+ if (best) found.push(best.window);
1683
+ }
1684
+ found.sort((x, y) => x._order - y._order);
1685
+ found.forEach((j, i) => {
1686
+ delete j._order;
1687
+ j.id = "w" + floorIndex + "_" + i;
1688
+ });
1689
+ return found;
1690
+ }
1691
+ function buildLot(plan, tpl, w, d, op, style, styleId, bc) {
1692
+ const seed = op.seed == null ? 1 : op.seed;
1693
+ const rFacade = createRng(seed, STREAM.facade);
1694
+ const facadeStyle = style.facade;
1695
+ const palette2 = style.swatches;
1696
+ const color = palette2[Math.floor(rFacade() * palette2.length)];
1697
+ const extra = tpl.facade || {};
1698
+ plan.facade = {
1699
+ style: styleId,
1700
+ color: { name: color[0], hex: color[1] },
1701
+ finish: pickWeighted(rFacade, facadeStyle.finish),
1702
+ grilles: rFacade() < facadeStyle.grilles,
1703
+ roof: pickWeighted(rFacade, facadeStyle.roof),
1704
+ balcony: !!extra.balcony,
1705
+ veranda: !!extra.veranda,
1706
+ steelDoor: !!extra.steelDoor,
1707
+ floors: plan.floorDefs.length
1708
+ };
1709
+ if (tpl.facadeExtra) tpl.facadeExtra(plan.facade, rFacade, w, d, op);
1710
+ if (plan.floorDefs.length > 1 && plan.facade.roof === "fiber_cement") plan.facade.roof = "colonial";
1711
+ if (!tpl.lot) return;
1712
+ const cfg = tpl.lot(w, d, style, op);
1713
+ if (bc.zone) clampLotToZone(cfg, bc.zone, bc.adjustments || [], tpl.partyWalls);
1714
+ const rLot = createRng(seed, STREAM.lot);
1715
+ const setback = cfg.frontSetback;
1716
+ const marginL = cfg.marginLeft;
1717
+ const marginR = cfg.marginRight;
1718
+ const backDepth = cfg.backyardDepth;
1719
+ const lot = {
1720
+ rect: [-marginL, -setback, w + marginR, d + backDepth],
1721
+ setback,
1722
+ sideYard: { left: marginL, right: marginR },
1723
+ backyard: backDepth,
1724
+ style: styleId,
1725
+ zones: [],
1726
+ perimeter: [],
1727
+ gates: [],
1728
+ paths: [],
1729
+ pool: !!cfg.pool
1730
+ };
1731
+ const ZONES = [];
1732
+ const libs = Object.assign({ garden: "garden", backyard: "backyard", parking: "parking", sideYard: "side_yard" }, cfg.libraries || {});
1733
+ 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 }));
1734
+ if (setback > 0) {
1735
+ let parts = [[-marginL, -setback, w + marginR, 0]];
1736
+ if (cfg.parking) {
1737
+ const garageU = cfg.garage.u;
1738
+ const vw = cfg.parking.width;
1739
+ const x0 = Math.max(-marginL, garageU - vw / 2);
1740
+ const x1 = Math.min(w + marginR, garageU + vw / 2);
1741
+ parts = [];
1742
+ if (x0 + marginL > 0.8) parts.push([-marginL, -setback, x0, 0]);
1743
+ if (w + marginR - x1 > 0.8) parts.push([x1, -setback, w + marginR, 0]);
1744
+ addZone("parking", "parking", "parking", [[x0, -setback, x1, 0]], libs.parking);
1745
+ if (!parts.length) parts = [[x1, -setback, w + marginR, 0]];
1746
+ }
1747
+ parts.sort((a, b) => b[2] - b[0] - (a[2] - a[0]));
1748
+ addZone("garden", "garden", "garden", parts, libs.garden);
1749
+ }
1750
+ const yard = cfg.yardZones;
1751
+ if (backDepth > 0 && yard) {
1752
+ 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);
1753
+ } else if (backDepth > 0) addZone("backyard", "backyard", "backyard", [[-marginL, d, w + marginR, d + backDepth]], libs.backyard);
1754
+ for (const q of cfg.extraZones || []) addZone(q.id, q.type, q.label || q.id, q.parts, q.library);
1755
+ if (marginL > 0.5) addZone("side_left", "side_yard", "side_left", [[-marginL, 0, 0, d]], libs.sideYard);
1756
+ if (marginR > 0.5) addZone("side_right", "side_yard", "side_right", [[w, 0, w + marginR, d]], libs.sideYard);
1757
+ const gates = [];
1758
+ const addGate = (type, model, u, v, width, side, toTarget) => {
1759
+ const g = {
1760
+ id: "g" + gates.length,
1761
+ type,
1762
+ model,
1763
+ u: r2(u),
1764
+ v: r2(v),
1765
+ axis: "u",
1766
+ width,
1767
+ height: type === "pedestrian" ? style.height : Math.min(style.height, 2.2),
1768
+ side,
1769
+ to: toTarget || null,
1770
+ material: "iron"
1771
+ };
1772
+ g.zones = openingZones(u, v, "u", width, openingDepth(width, type === "pedestrian" ? width : 0));
1773
+ gates.push(g);
1774
+ return g;
1775
+ };
1776
+ if (cfg.pedestrian && setback > 0) addGate("pedestrian", "pedestrian", cfg.pedestrian.u, -setback, 1, "front", "door");
1777
+ if (cfg.garage && setback > 0) addGate("garage", pickWeighted(rLot, style.gate.garage), cfg.garage.u, -setback, cfg.garage.width, "front", cfg.garage.to);
1778
+ if (cfg.cargo) addGate("cargo", "sliding", cfg.cargo.u, d + backDepth, cfg.cargo.width, "back", "stockroom");
1779
+ plan.gates = gates;
1780
+ lot.gates = gates;
1781
+ const paths = [];
1782
+ const target = (a, side) => plan.doors.find((p) => p.exterior && p.a === a && p.side === side);
1783
+ for (const g of gates) {
1784
+ if (g.type === "pedestrian" && cfg.doorTarget) {
1785
+ const pt = target(cfg.doorTarget.a, cfg.doorTarget.side);
1786
+ if (!pt) continue;
1787
+ const gateU = g.u;
1788
+ const dx = pt.u;
1789
+ const ym = -setback / 2;
1790
+ const L = 1;
1791
+ const h = L / 2;
1792
+ const rs = [];
1793
+ if (Math.abs(gateU - dx) < 0.05) rs.push([dx - h, -setback, dx + h, 0]);
1794
+ else {
1795
+ rs.push([gateU - h, -setback, gateU + h, ym + h]);
1796
+ rs.push([Math.min(gateU, dx) - h, ym - h, Math.max(gateU, dx) + h, ym + h]);
1797
+ rs.push([dx - h, ym - h, dx + h, 0]);
1798
+ }
1799
+ paths.push({ id: "pedestrian_path", type: "pedestrian", from: g.id, to: pt.id, width: L, rects: rs.map((r) => r.map(r2)), points: [[gateU, -setback], [gateU, ym], [dx, ym], [dx, 0]] });
1800
+ } else if (g.type === "garage" && g.to === "inner_garage") {
1801
+ const pt = plan.doors.find((p) => p.exterior && p.side === "front" && Math.abs(p.u - g.u) < 0.6 && p.width >= 2.5);
1802
+ if (!pt) continue;
1803
+ const hw = Math.max(g.width, pt.width) / 2;
1804
+ paths.push({ id: "garage_path", type: "vehicle", from: g.id, to: pt.id, width: hw * 2, rects: [[g.u - hw, -setback, g.u + hw, 0].map(r2)], points: [[g.u, -setback], [g.u, 0]] });
1805
+ } else if (g.type === "cargo") {
1806
+ const pt = target("stockroom", "back");
1807
+ if (!pt) continue;
1808
+ const hw = Math.max(g.width, pt.width) / 2;
1809
+ 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]] });
1810
+ }
1811
+ }
1812
+ if (tpl.paths) paths.push(...tpl.paths(plan, { gates, setback, marginLeft: marginL, marginRight: marginR, backyard: backDepth, w, d }));
1813
+ else addExteriorDoorPaths(plan, { gates, paths, zones: ZONES, setback, marginLeft: marginL, marginRight: marginR, backyard: backDepth, w, target: cfg.doorTarget });
1814
+ plan.paths = paths;
1815
+ lot.paths = paths;
1816
+ const boundary = (rule) => {
1817
+ const type = pickWeighted(rLot, rule.type);
1818
+ const top = type === "railing" ? "none" : pickWeighted(rLot, rule.top);
1819
+ const wallHeight = type === "railing" ? 1.2 : type === "fence" ? style.fenceHeight ?? 2 : style.height;
1820
+ 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 };
1821
+ };
1822
+ const A = { front: boundary(style.front), left: boundary(style.sideYard), right: boundary(style.sideYard), back: boundary(style.back) };
1823
+ const seg = (side, u0, v0, u1, v1) => {
1824
+ if (Math.abs(u1 - u0) + Math.abs(v1 - v0) < 0.05) return;
1825
+ lot.perimeter.push(Object.assign({ id: "m" + lot.perimeter.length, side, u0: r2(u0), v0: r2(v0), u1: r2(u1), v1: r2(v1) }, A[side]));
1826
+ };
1827
+ const cutRun = (side, de, to, vfix, gs) => {
1828
+ let pieces = [[de, to]];
1829
+ for (const g of gs) {
1830
+ const g0 = g.u - g.width / 2;
1831
+ const g1 = g.u + g.width / 2;
1832
+ const n = [];
1833
+ for (const [x, y] of pieces) {
1834
+ if (g1 <= x || g0 >= y) {
1835
+ n.push([x, y]);
1836
+ continue;
1837
+ }
1838
+ if (g0 > x) n.push([x, g0]);
1839
+ if (g1 < y) n.push([g1, y]);
1840
+ }
1841
+ pieces = n;
1842
+ }
1843
+ for (const [x, y] of pieces) seg(side, x, vfix, y, vfix);
1844
+ };
1845
+ if (setback > 0)
1846
+ cutRun("front", -marginL, w + marginR, -setback, gates.filter((g) => g.side === "front"));
1847
+ cutRun("back", -marginL, w + marginR, d + backDepth, gates.filter((g) => g.side === "back"));
1848
+ const vi = setback > 0 ? -setback : 0;
1849
+ if (marginL > 0.5) seg("left", -marginL, vi, -marginL, d + backDepth);
1850
+ else if (backDepth > 0) seg("left", 0, d, 0, d + backDepth);
1851
+ if (marginR > 0.5) seg("right", w + marginR, vi, w + marginR, d + backDepth);
1852
+ else if (backDepth > 0) seg("right", w, d, w, d + backDepth);
1853
+ for (const z of ZONES) z.ground = (cfg.grounds || {})[z.id] || (cfg.grounds || {})[z.type] || pickWeighted(rLot, style.grounds[z.type] || { concrete: 1 });
1854
+ if (bc.zone) enforcePermeability(lot, ZONES, bc.zone, bc.adjustments || []);
1855
+ plan.zones = ZONES;
1856
+ lot.zones = ZONES;
1857
+ plan.lot = lot;
1858
+ }
1859
+ var HIGH_SATURATION = 0.4;
1860
+ var MIN_WALL_FLOOR_CONTRAST = 20;
1861
+ var MIN_FABRIC_FLOOR_CONTRAST = 15;
1862
+ var SCHEMES = ["analogous", "complementary", "split_complementary", "triad"];
1863
+ var HARMONY_OFFSETS = { analogous: [-30, 30], complementary: [180, 165], split_complementary: [150, 210], triad: [120, 240] };
1864
+ var ROLE_FABRIC_MAIN = "fabric_main";
1865
+ var ROLE_FABRIC_ACCENT = "fabric_accent";
1866
+ var ROLE_FLOOR_REFERENCE = "floor_living";
1867
+ var hslToInt = (h, s, l) => {
1868
+ h = (h % 360 + 360) % 360;
1869
+ s = clamp(s, 0, 1);
1870
+ l = clamp(l, 0, 1);
1871
+ const a = s * Math.min(l, 1 - l);
1872
+ const f = (n) => {
1873
+ const k = (n + h / 30) % 12;
1874
+ return l - a * Math.max(-1, Math.min(k - 3, 9 - k, 1));
1875
+ };
1876
+ return Math.round(f(0) * 255) << 16 | Math.round(f(8) * 255) << 8 | Math.round(f(4) * 255);
1877
+ };
1878
+ var intToHsl = (n) => {
1879
+ const r = (n >> 16 & 255) / 255;
1880
+ const g = (n >> 8 & 255) / 255;
1881
+ const b = (n & 255) / 255;
1882
+ const mx = Math.max(r, g, b);
1883
+ const mn = Math.min(r, g, b);
1884
+ const l = (mx + mn) / 2;
1885
+ const d = mx - mn;
1886
+ let h = 0;
1887
+ let s = 0;
1888
+ if (d > 1e-9) {
1889
+ s = d / (1 - Math.abs(2 * l - 1));
1890
+ h = mx === r ? (g - b) / d + (g < b ? 6 : 0) : mx === g ? (b - r) / d + 2 : (r - g) / d + 4;
1891
+ h *= 60;
1892
+ }
1893
+ return { h, s, l };
1894
+ };
1895
+ var lstar = (n) => {
1896
+ const linear = (c) => (c /= 255) <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
1897
+ const Y = 0.2126 * linear(n >> 16 & 255) + 0.7152 * linear(n >> 8 & 255) + 0.0722 * linear(n & 255);
1898
+ return Y > 216 / 24389 ? 116 * Math.cbrt(Y) - 16 : Y * 24389 / 27;
1899
+ };
1900
+ var hexString = (n) => "#" + (n >>> 0).toString(16).padStart(6, "0");
1901
+ function palette(o) {
1902
+ const opt = o || {};
1903
+ let style = resolveStyleId(opt.style || "budget");
1904
+ if (!registries().PALETTE_STYLES[style]) style = registries().PALETTE_STYLES.mid ? "mid" : Object.keys(registries().PALETTE_STYLES)[0];
1905
+ const def = registries().PALETTE_STYLES[style];
1906
+ const state = opt.state || "normal";
1907
+ const seed = opt.seed == null ? 1 : opt.seed;
1908
+ const rng = createRng(seed, STREAM.palettePrefix + (def.seedKey ?? style));
1909
+ const table = def.facadeColors;
1910
+ const base = table[Math.floor(rng() * table.length)];
1911
+ const scheme = SCHEMES[Math.floor(rng() * 4)];
1912
+ const offsets = HARMONY_OFFSETS[scheme];
1913
+ const jit = (v, a) => v + (rng() - 0.5) * a;
1914
+ const hsl = (h, s, l) => ({ h: (h % 360 + 360) % 360, s: clamp(s, 0, 1), l: clamp(l, 0, 1) });
1915
+ const H0 = base[1];
1916
+ const H1 = H0 + offsets[0];
1917
+ const H2 = H0 + offsets[1];
1918
+ const draw = { rng, base, H0, H1, H2, jit, hsl };
1919
+ const early = def.early(draw);
1920
+ const { wallMode, sMax } = early;
1921
+ const R = Object.assign({}, early.roles);
1922
+ const ctx = Object.assign({ style, sMax, wallMode, generalFloor: early.generalFloor }, draw);
1923
+ for (const c of registries().ROOM_COLORS) {
1924
+ const [wh, wl] = c.wall(ctx);
1925
+ 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));
1926
+ const pi = c.floor || early.generalFloor;
1927
+ R["floor_" + c.type] = hsl(pi[0], Math.min(pi[1], wallMode ? 0.18 : 0.3), jit(pi[2], 0.04));
1928
+ }
1929
+ Object.assign(R, def.late(draw, early));
1930
+ registries().STATE_PLUGINS[state]?.adjustPalette?.(R);
1931
+ const adjust = (color, ref, min, preferredDir) => {
1932
+ for (let i = 0; i < 60; i++) {
1933
+ if (Math.abs(lstar(hslToInt(color.h, color.s, color.l)) - lstar(hslToInt(ref.h, ref.s, ref.l))) >= min) return;
1934
+ const refL = lstar(hslToInt(ref.h, ref.s, ref.l));
1935
+ let dir = preferredDir || (refL > 50 ? -1 : 1);
1936
+ if (dir < 0 && color.l <= 0.06 || dir > 0 && color.l >= 0.94) dir = -dir;
1937
+ color.l = clamp(color.l + dir * 0.015, 0.04, 0.96);
1938
+ }
1939
+ };
1940
+ const types = registries().ROOM_COLORS.map((c) => c.type);
1941
+ for (const c of types) {
1942
+ const w = R["wall_" + c];
1943
+ const p = R["floor_" + c];
1944
+ if (w.s > 0.3) p.s = Math.min(p.s, 0.2);
1945
+ if (p.s > HIGH_SATURATION) p.s = HIGH_SATURATION - 0.05;
1946
+ adjust(p, w, MIN_WALL_FLOOR_CONTRAST + 2, lstar(hslToInt(w.h, w.s, w.l)) > 50 ? -1 : 1);
1947
+ }
1948
+ const floorRef = R[ROLE_FLOOR_REFERENCE] || R["floor_" + types[0]];
1949
+ const fabric = R[ROLE_FABRIC_MAIN];
1950
+ adjust(fabric, floorRef, MIN_FABRIC_FLOOR_CONTRAST + 3);
1951
+ adjust(R[ROLE_FABRIC_ACCENT], floorRef, MIN_FABRIC_FLOOR_CONTRAST + 3);
1952
+ for (const c of types) {
1953
+ const p = R["floor_" + c];
1954
+ if (Math.abs(lstar(hslToInt(fabric.h, fabric.s, fabric.l)) - lstar(hslToInt(p.h, p.s, p.l))) < MIN_FABRIC_FLOOR_CONTRAST)
1955
+ adjust(p, fabric, MIN_FABRIC_FLOOR_CONTRAST + 2, lstar(hslToInt(fabric.h, fabric.s, fabric.l)) > 50 ? -1 : 1);
1956
+ }
1957
+ const out = { meta: { seed, style, state, scheme, base: base[0], accentMode: wallMode ? "wall" : "object" } };
1958
+ for (const k of Object.keys(R)) out[k] = hslToInt(R[k].h, R[k].s, R[k].l);
1959
+ const num = (k) => out[k];
1960
+ const pairOk = (w, p) => Math.abs(lstar(w) - lstar(p)) >= MIN_WALL_FLOOR_CONTRAST + 1 && Math.abs(lstar(num(ROLE_FABRIC_MAIN)) - lstar(p)) >= MIN_FABRIC_FLOOR_CONTRAST + 1;
1961
+ const search = (color, ok) => {
1962
+ const h = intToHsl(color);
1963
+ let bestValue = null;
1964
+ let md = 9;
1965
+ for (let l = 0.02; l <= 0.98; l += 5e-3) {
1966
+ const n = hslToInt(h.h, h.s, l);
1967
+ if (ok(n) && Math.abs(l - h.l) < md) {
1968
+ md = Math.abs(l - h.l);
1969
+ bestValue = n;
1970
+ }
1971
+ }
1972
+ return bestValue;
1973
+ };
1974
+ for (const c of types) {
1975
+ if (pairOk(num("wall_" + c), num("floor_" + c))) continue;
1976
+ const n = search(num("floor_" + c), (p) => pairOk(num("wall_" + c), p));
1977
+ if (n != null) {
1978
+ out["floor_" + c] = n;
1979
+ continue;
1980
+ }
1981
+ const w = search(num("wall_" + c), (w2) => pairOk(w2, num("floor_" + c)));
1982
+ if (w != null) out["wall_" + c] = w;
1983
+ }
1984
+ return out;
1985
+ }
1986
+ var SIDE_NORMAL = { left: [1, 0], right: [-1, 0], front: [0, 1], back: [0, -1] };
1987
+ var FIXED_FACING = { "u+": [1, 0], "u-": [-1, 0], "v+": [0, 1], "v-": [0, -1] };
1988
+ var SIDE_OPPOSITE = { front: "back", back: "front", left: "right", right: "left" };
1989
+ var WINDOW_SIDE = { front: "front", back: "back", left: "left", right: "right" };
1990
+ function slotDefsOf(plan, room) {
1991
+ const name = room.library || room.type;
1992
+ const key = registries().LIBRARY_ALIAS[name] || name;
1993
+ const library = room.slots ? room.slots : registries().LIBRARIES[key];
1994
+ if (!library) return [];
1995
+ const R = inner(room.parts[0]);
1996
+ let defs = typeof library === "function" ? library({ w: R[2] - R[0], d: R[3] - R[1] }) : library.slice();
1997
+ defs = defs.map((x) => Object.assign({}, x));
1998
+ for (const id of room.without || []) defs = defs.filter((x) => x.id !== id);
1999
+ for (const [id, aj] of Object.entries(room.overrides || {})) {
2000
+ const i = defs.findIndex((x) => x.id === id);
2001
+ if (i < 0) continue;
2002
+ if (aj === null) defs.splice(i, 1);
2003
+ else defs[i] = Object.assign(defs[i], aj);
2004
+ }
2005
+ defs.push(...(room.extras || []).map((x) => Object.assign({}, x)));
2006
+ const out = [];
2007
+ for (const def of defs) {
2008
+ def.parent = def.on;
2009
+ if (def.anchor === "wall" && def.side === "any") for (const l of ["left", "right", "front", "back"]) out.push(Object.assign({}, def, { id: def.id + "@" + l, side: l, group: def.group || def.id }));
2010
+ else if (def.anchor === "corner" && def.corner === "any") for (const l of ["front-left", "front-right", "back-left", "back-right"]) out.push(Object.assign({}, def, { id: def.id + "@" + l, corner: l, group: def.group || def.id }));
2011
+ else if (def.anchor === "window" && def.n == null) {
2012
+ const windowList = windowsOfRoom(plan, room);
2013
+ windowList.forEach((_j, i) => out.push(Object.assign({}, def, { id: def.id + "@w" + i, n: i, group: def.group || def.id })));
2014
+ } else out.push(def);
2015
+ }
2016
+ return out;
2017
+ }
2018
+ function resolveSlots(plan) {
2019
+ const all = [];
2020
+ for (const room of plan.rooms.concat(plan.zones)) {
2021
+ const defs = slotDefsOf(plan, room);
2022
+ const byId2 = {};
2023
+ const zones = room.type === "stairs" ? plan.floors.flatMap((_, floorIndex) => zonesOfFloor(plan, floorIndex)) : zonesOfFloor(plan, room.floor);
2024
+ for (const def of defs) {
2025
+ const s = resolveSlot(plan, room, def, byId2, zones);
2026
+ byId2[def.id] = s;
2027
+ all.push(s);
2028
+ }
2029
+ }
2030
+ return all;
2031
+ }
2032
+ function resolveSlot(plan, room, def, byId2, zones) {
2033
+ const base = {
2034
+ id: def.id,
2035
+ room: room.id,
2036
+ floor: room.floor,
2037
+ outdoor: room.outdoor,
2038
+ anchor: def.anchor,
2039
+ side: def.side || def.corner || null,
2040
+ cats: def.cats,
2041
+ slide: def.slide,
2042
+ required: !!def.required,
2043
+ chance: def.required ? 1 : def.chance == null ? 1 : def.chance,
2044
+ clearance: def.clearance == null ? 0.5 : def.clearance,
2045
+ front: def.facing || "center",
2046
+ group: def.group || null,
2047
+ requires: def.requires || null,
2048
+ on: def.parent || null,
2049
+ target: def.target || null,
2050
+ valid: true,
2051
+ reason: null
2052
+ };
2053
+ 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 });
2054
+ if (def.anchor === "on" || def.anchor === "inside") {
2055
+ const parent = byId2[def.parent];
2056
+ if (!parent || !parent.valid) return invalid("invalid_parent");
2057
+ 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 });
2058
+ }
2059
+ const size = def.size;
2060
+ const roomCenter = (() => {
2061
+ const R0 = inner(room.parts[0]);
2062
+ return [(R0[0] + R0[2]) / 2, (R0[1] + R0[3]) / 2];
2063
+ })();
2064
+ const openings = openingsOfFloor(plan, room.floor);
2065
+ let point = [0, 0];
2066
+ const targetPoint = (rule2) => {
2067
+ if (rule2 === "door") {
2068
+ let best = null;
2069
+ let bestDist = 1e9;
2070
+ for (const ab of openings) {
2071
+ const dist = Math.hypot(ab.u - point[0], ab.v - point[1]);
2072
+ if (dist < bestDist) {
2073
+ bestDist = dist;
2074
+ best = ab;
2075
+ }
2076
+ }
2077
+ return best ? [best.u, best.v] : roomCenter;
2078
+ }
2079
+ if (rule2 === "target") {
2080
+ const t = byId2[def.target];
2081
+ return t && t.valid ? [t.x, t.z] : roomCenter;
2082
+ }
2083
+ return roomCenter;
2084
+ };
2085
+ const free = (rect) => !!def.floorFree || !zones.some((z) => overlaps(rect, z, room.type === "stairs" ? -0.011 : EPS));
2086
+ const rule = def.facing || "center";
2087
+ let R = inner(room.parts[Math.min(def.part || 0, room.parts.length - 1)]);
2088
+ if (def.anchor === "wall" || def.anchor === "window") {
2089
+ let side = def.side;
2090
+ let a = null;
2091
+ let win = null;
2092
+ if (def.anchor === "window") {
2093
+ const windowList = windowsOfRoom(plan, room);
2094
+ win = windowList[def.n];
2095
+ if (!win) return invalid("no_window");
2096
+ side = room.outdoor ? SIDE_OPPOSITE[win.side] : WINDOW_SIDE[win.side];
2097
+ const cand = room.parts.map(inner).find(
2098
+ (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))
2099
+ );
2100
+ if (!cand) return invalid("window_outside_room");
2101
+ R = cand;
2102
+ a = win.axis === "u" ? win.u : win.v;
2103
+ }
2104
+ const n = SIDE_NORMAL[side];
2105
+ if (!n) return invalid("invalid_side");
2106
+ const horizontal = side === "front" || side === "back";
2107
+ const a0 = horizontal ? R[0] : R[1];
2108
+ const a1 = horizontal ? R[2] : R[3];
2109
+ const len = a1 - a0;
2110
+ const depth = size[1];
2111
+ if (depth > (horizontal ? R[3] - R[1] : R[2] - R[0]) + EPS) return invalid("larger_than_room");
2112
+ const hw = Math.min(size[0], len) / 2;
2113
+ const cell = (ac) => side === "left" ? [R[0], ac - hw, R[0] + depth, ac + hw] : side === "right" ? [R[2] - depth, ac - hw, R[2], ac + hw] : side === "front" ? [ac - hw, R[1], ac + hw, R[1] + depth] : [ac - hw, R[3] - depth, ac + hw, R[3]];
2114
+ const nominal = a != null ? a : a0 + hw + (def.pos == null ? 0.5 : def.pos) * Math.max(0, len - 2 * hw) + (def.shift || 0);
2115
+ const wp = side === "left" ? R[0] - MARGIN : side === "right" ? R[2] + MARGIN : side === "front" ? R[1] - MARGIN : R[3] + MARGIN;
2116
+ const pa = clamp(nominal, a0 + hw, a1 - hw);
2117
+ let found = null;
2118
+ const steps = [0];
2119
+ if (def.anchor !== "window") for (let k = 1; k <= 60; k++) steps.push(k, -k);
2120
+ for (const k of steps) {
2121
+ const ac = pa + k * 0.05;
2122
+ if (ac - hw < a0 - EPS || ac + hw > a1 + EPS) continue;
2123
+ if (free(cell(ac))) {
2124
+ found = ac;
2125
+ break;
2126
+ }
2127
+ }
2128
+ if (found == null) return invalid("blocked_by_door");
2129
+ const sp = horizontal ? [found, wp - n[1] * 0.1] : [wp - n[0] * 0.1, found];
2130
+ if (isOpenSide(plan, room, sp[0], sp[1])) return invalid("open_wall");
2131
+ const rect = cell(found);
2132
+ return Object.assign(base, { side, x: r2((rect[0] + rect[2]) / 2), z: r2((rect[1] + rect[3]) / 2), frontU: n[0], frontV: n[1], ry: r2(Math.atan2(n[0], n[1])), w: size[0], d: size[1], aabb: rect.map(r2), _cell: { rect, f: n, enclosed: [side], R, size } });
2133
+ }
2134
+ if (def.anchor === "corner") {
2135
+ const [cornerV, cornerU] = def.corner.split("-");
2136
+ const normalV = SIDE_NORMAL[cornerV];
2137
+ const normalU = SIDE_NORMAL[cornerU];
2138
+ if (!normalV || !normalU) return invalid("invalid_corner");
2139
+ const kx = cornerU === "left" ? R[0] : R[2];
2140
+ const kz = cornerV === "front" ? R[1] : R[3];
2141
+ point = [kx, kz];
2142
+ let f;
2143
+ if (FIXED_FACING[rule]) {
2144
+ f = FIXED_FACING[rule];
2145
+ if (!(f[0] === normalU[0] && f[1] === normalU[1] || f[0] === normalV[0] && f[1] === normalV[1])) f = normalU;
2146
+ } else {
2147
+ const t = targetPoint(rule);
2148
+ f = Math.abs(t[0] - kx) >= Math.abs(t[1] - kz) ? normalU : normalV;
2149
+ }
2150
+ const [extentU, extentV] = extent(size, f);
2151
+ if (extentU > R[2] - R[0] + EPS || extentV > R[3] - R[1] + EPS) return invalid("larger_than_room");
2152
+ const rect = [cornerU === "left" ? R[0] : R[2] - extentU, cornerV === "front" ? R[1] : R[3] - extentV, cornerU === "left" ? R[0] + extentU : R[2], cornerV === "front" ? R[1] + extentV : R[3]];
2153
+ if (!free(rect)) return invalid("blocked_by_door");
2154
+ for (const sp of [[kx - normalU[0] * (MARGIN + 0.1), (rect[1] + rect[3]) / 2], [(rect[0] + rect[2]) / 2, kz - normalV[1] * (MARGIN + 0.1)]]) if (isOpenSide(plan, room, sp[0], sp[1])) return invalid("open_wall");
2155
+ return Object.assign(base, { x: r2((rect[0] + rect[2]) / 2), z: r2((rect[1] + rect[3]) / 2), frontU: f[0], frontV: f[1], ry: r2(Math.atan2(f[0], f[1])), w: size[0], d: size[1], aabb: rect.map(r2), _cell: { rect, f, enclosed: [cornerU, cornerV], R, size } });
2156
+ }
2157
+ if (def.anchor === "center") {
2158
+ let cx = R[0] + (def.fx == null ? 0.5 : def.fx) * (R[2] - R[0]) + (def.dx || 0);
2159
+ let cz = R[1] + (def.fz == null ? 0.5 : def.fz) * (R[3] - R[1]) + (def.dz || 0);
2160
+ point = [cx, cz];
2161
+ let f;
2162
+ if (FIXED_FACING[rule]) f = FIXED_FACING[rule];
2163
+ else {
2164
+ const t = targetPoint(rule);
2165
+ const dx = t[0] - cx;
2166
+ const dz = t[1] - cz;
2167
+ f = Math.hypot(dx, dz) < 0.05 ? [0, 1] : cardinal(dx, dz);
2168
+ }
2169
+ const [extentU, extentV] = extent(size, f);
2170
+ if (extentU > R[2] - R[0] + EPS || extentV > R[3] - R[1] + EPS) return invalid("larger_than_room");
2171
+ cx = clamp(cx, R[0] + extentU / 2, R[2] - extentU / 2);
2172
+ cz = clamp(cz, R[1] + extentV / 2, R[3] - extentV / 2);
2173
+ const rect = [cx - extentU / 2, cz - extentV / 2, cx + extentU / 2, cz + extentV / 2];
2174
+ if (!free(rect)) return invalid("blocked_by_door");
2175
+ return Object.assign(base, { x: r2(cx), z: r2(cz), frontU: f[0], frontV: f[1], ry: r2(Math.atan2(f[0], f[1])), w: size[0], d: size[1], aabb: rect.map(r2), _cell: { rect, f, enclosed: [], R, size } });
2176
+ }
2177
+ return invalid("unknown_anchor");
2178
+ }
2179
+ function templateOf(t) {
2180
+ if (typeof t === "string") {
2181
+ if (!registries().TEMPLATES[t]) throw new Error("Unknown template: " + t);
2182
+ return registries().TEMPLATES[t];
2183
+ }
2184
+ return t;
2185
+ }
2186
+ var cleanSlot = (s) => {
2187
+ const o = {};
2188
+ for (const k of Object.keys(s)) if (k[0] !== "_") o[k] = s[k];
2189
+ o.cats = Object.keys(s.cats || {});
2190
+ return o;
2191
+ };
2192
+ function slots(template, options) {
2193
+ const tpl = templateOf(template);
2194
+ const o = options || {};
2195
+ const plan = buildPlan(tpl, o.w, o.d, o);
2196
+ const locale = o.locale || "en";
2197
+ return resolveSlots(plan).map(cleanSlot).map((s) => s.reason ? Object.assign(s, { reason: translate("reasons", s.reason, locale) }) : s);
2198
+ }
2199
+ function pickState(requested, seed) {
2200
+ if (requested && registries().STATE_PLUGINS[requested]) return requested;
2201
+ const dist = registries().stateDistribution.weights;
2202
+ if (!dist) return "normal";
2203
+ const x = createRng(seed, registries().stateDistribution.seedKey)();
2204
+ let acc = 0;
2205
+ const keys = Object.keys(dist);
2206
+ for (const k of keys) {
2207
+ acc += dist[k];
2208
+ if (x < acc) return k;
2209
+ }
2210
+ return keys[keys.length - 1];
2211
+ }
2212
+ function colorOf(m, seed) {
2213
+ const spec = m.colorSpec || registries().CATALOG[m.type]?.color || { role: "wood" };
2214
+ if ("role" in spec) return spec.role;
2215
+ if ("hex" in spec) return spec.hex;
2216
+ return spec.pick[hashString(seed + "|" + (m._seedId ?? m.id)) % spec.pick.length];
2217
+ }
2218
+ function applyState(plan, items, ctx, seed, state) {
2219
+ const plugin = registries().STATE_PLUGINS[state];
2220
+ if (!plugin || state === "normal") return [];
2221
+ const c = {
2222
+ plan,
2223
+ items,
2224
+ seed,
2225
+ state,
2226
+ rng: createRng(seed, plugin.seedKey ?? "state:" + state),
2227
+ ctx,
2228
+ kit: placementKit,
2229
+ catalog: registries().CATALOG,
2230
+ wallTopHeights: registries().WALL_TOP_HEIGHTS
2231
+ };
2232
+ return plugin.apply(c);
2233
+ }
2234
+ var loc = (key, locale, ns = "labels", vars) => key ? translate(ns, key, locale, vars) : key;
2235
+ function generate(template, options) {
2236
+ const tpl = templateOf(template);
2237
+ const o = options || {};
2238
+ const seed = o.seed == null ? 1 : o.seed;
2239
+ const locale = o.locale || "en";
2240
+ let op = Object.assign({}, o, { seed });
2241
+ const zone = resolveZone(o.zone);
2242
+ const adjustments = [];
2243
+ if (zone) op = clampOptionsToZone(tpl, op, zone, adjustments);
2244
+ const plan = buildPlan(tpl, op.w, op.d, op, { zone, adjustments });
2245
+ const all = resolveSlots(plan);
2246
+ const items = [];
2247
+ const missing = [];
2248
+ for (const room of plan.rooms.concat(plan.zones)) {
2249
+ const sl = all.filter((s) => s.room === room.id && s.floor === room.floor);
2250
+ const r = fillRoom(plan, room, sl, createRng(seed, STREAM.roomPrefix + (room.seedKey ?? registries().SEED_KEYS.rooms[room.id] ?? room.id)));
2251
+ items.push(...r.items);
2252
+ missing.push(...r.missing);
2253
+ }
2254
+ const ctx = runPostProcess(plan, items, seed);
2255
+ const state = pickState(o.state, seed);
2256
+ const stateItemsAll = applyState(plan, items, ctx, seed, state);
2257
+ const colors2 = palette({ seed, style: plan.style, state });
2258
+ const strip = (m) => {
2259
+ const x = {};
2260
+ for (const k of Object.keys(m)) if (k[0] !== "_" && k !== "colorSpec") x[k] = m[k];
2261
+ return x;
2262
+ };
2263
+ const L = plan.lot;
2264
+ const lot = L ? {
2265
+ rect: L.rect.map(r2),
2266
+ setback: r2(L.setback),
2267
+ backyard: r2(L.backyard),
2268
+ sideYard: L.sideYard,
2269
+ style: L.style,
2270
+ pool: L.pool,
2271
+ zones: plan.zones.map((z) => ({ id: z.id, type: z.type, label: z.label, name: loc(z.label, locale), parts: z.parts, ground: z.ground })),
2272
+ perimeter: L.perimeter.map((s) => Object.assign({ color: "wall" }, s)),
2273
+ gates: plan.gates.map((g) => ({ id: g.id, type: g.type, model: g.model, u: g.u, v: g.v, axis: g.axis, width: g.width, height: g.height, side: g.side, to: g.to, material: g.material, open: !!g.open, color: "gate" })),
2274
+ paths: plan.paths.map((c) => ({ id: c.id, type: c.type, from: c.from, to: c.to, width: c.width, rects: c.rects, points: c.points }))
2275
+ } : null;
2276
+ const facade = Object.assign({}, plan.facade, {
2277
+ color: { name: loc(colors2.meta.base, locale, "facadeColors"), key: colors2.meta.base, hex: hexString(colors2.facade) },
2278
+ roles: { facade: "facade", detail: "facade_detail", roof: "roof", wall: "wall", gate: "gate" }
2279
+ });
2280
+ const itemsOut = items.map((m) => {
2281
+ const out = strip(m);
2282
+ out.color = colorOf(m, seed);
2283
+ return out;
2284
+ });
2285
+ const stateItemsOut = stateItemsAll.map((m) => {
2286
+ const out = strip(m);
2287
+ if (m.category) out.color = colorOf(m, seed);
2288
+ return out;
2289
+ });
2290
+ const result = {
2291
+ template: tpl.id,
2292
+ name: translate("templates", tpl.id, locale),
2293
+ use: useOf(tpl),
2294
+ seed,
2295
+ style: plan.style,
2296
+ state,
2297
+ w: plan.w,
2298
+ d: plan.d,
2299
+ floors: plan.floors.map((f) => ({ index: f.index, label: f.name, name: f.number != null ? translate("labels", "floor_n", locale, { n: f.number }) : loc(f.name, locale), number: f.number, y0: f.y0, ceiling: f.ceiling })),
2300
+ rooms: plan.rooms.map((c) => ({
2301
+ id: c.id,
2302
+ type: c.type,
2303
+ label: c.label,
2304
+ name: loc(c.label, locale),
2305
+ floor: c.floor,
2306
+ u0: c.parts[0][0],
2307
+ v0: c.parts[0][1],
2308
+ u1: c.parts[0][2],
2309
+ v1: c.parts[0][3],
2310
+ parts: c.parts,
2311
+ link: c.link,
2312
+ ceiling: c.ceiling || plan.floors[c.floor].ceiling,
2313
+ color: { wall: "wall_" + c.type, floor: "floor_" + c.type }
2314
+ })),
2315
+ walls: plan.walls,
2316
+ doors: plan.doors,
2317
+ windows: plan.windows,
2318
+ openPairs: [].concat(...Object.values(plan.openPairs)),
2319
+ facade,
2320
+ lot,
2321
+ items: itemsOut,
2322
+ stateItems: stateItemsOut,
2323
+ colors: colors2,
2324
+ building: tpl.building ? tpl.building(plan.w, plan.d, op) : null,
2325
+ missing
2326
+ };
2327
+ if (tpl.singleFloor) result.floorNumber = op.floor || 3;
2328
+ result.partyWalls = tpl.partyWalls || [];
2329
+ if (plan.config) result.config = plan.config;
2330
+ runResultExtensions(plan, result, seed, op);
2331
+ if (zone) {
2332
+ const report = { zone: zone.id, adjustments, violations: checkLot(zone, lot, Object.assign({}, result, { rooms: plan.rooms })) };
2333
+ result.zoning = report;
2334
+ }
2335
+ return result;
2336
+ }
2337
+ var isSolid = (m) => !m.flat && !m.on && (!m.decor || m.mount === "floor");
2338
+ function validate(result, options) {
2339
+ const locale = options?.locale || result.locale || "en";
2340
+ const t = (key, vars) => translate("messages", key, locale, vars);
2341
+ const errors = [];
2342
+ const solids = result.items.filter(isSolid);
2343
+ for (let i = 0; i < solids.length; i++)
2344
+ for (let j = i + 1; j < solids.length; j++)
2345
+ if (solids[i].floor === solids[j].floor && !solids[i].barricade && !solids[j].barricade && overlaps(solids[i].aabb, solids[j].aabb, 0.011)) errors.push(t("overlap", { a: solids[i].id, b: solids[j].id }));
2346
+ for (const m of solids) {
2347
+ if (m.barricade) continue;
2348
+ for (const p of result.doors) if (p.floor === (m.outdoor ? 0 : m.floor) && p.zones.some((z) => overlaps(m.aabb, z, 0.011))) errors.push(t("door_blocked", { id: m.id }));
2349
+ if (result.lot) {
2350
+ for (const g of result.lot.gates) if (m.outdoor && openingZones(g.u, g.v, "u", g.width, DOOR_CLEARANCE).some((z) => overlaps(m.aabb, z, 0.011))) errors.push(t("gate_blocked", { id: m.id }));
2351
+ }
2352
+ }
2353
+ for (const f of result.missing) errors.push(t("missing_required", { room: f.room, slot: f.slot }));
2354
+ if (result.zoning) for (const v of result.zoning.violations) errors.push(t("zone_violation", { zone: result.zoning.zone, rule: v.rule, actual: v.actual, limit: v.limit }));
2355
+ return errors;
2356
+ }
2357
+ function limitsOf(template, options) {
2358
+ const tpl = templateOf(template);
2359
+ const op = options || {};
2360
+ return { limits: tpl.limitsFn ? tpl.limitsFn(op) : tpl.limits, defaults: tpl.defaultsFn ? tpl.defaultsFn(op) : tpl.defaults };
2361
+ }
2362
+ function listTemplates(locale = "en") {
2363
+ return Object.values(registries().TEMPLATES).map((t) => {
2364
+ const a = floorsOf(t, {});
2365
+ return {
2366
+ id: t.id,
2367
+ name: translate("templates", t.id, locale),
2368
+ use: useOf(t),
2369
+ style: typeof t.style === "function" ? t.style({}) : t.style,
2370
+ limits: t.limits,
2371
+ defaults: t.defaults,
2372
+ floors: a.length,
2373
+ floorRange: t.floorRange || null,
2374
+ lot: !!t.lot,
2375
+ options: t.options || {}
2376
+ };
2377
+ });
119
2378
  }
120
2379
  function registerGroundItems() {
121
2380
  const flat = { category: "ground_cover", w: 1, d: 1, h: 0, decor: true, mount: "ground", flat: true };
@@ -127,6 +2386,14 @@ var GameUrban = (() => {
127
2386
  { type: "dirt", ...flat, color: { role: "dirt" } }
128
2387
  ]);
129
2388
  }
2389
+ var slotKit = {
2390
+ wall: (id, side, pos, size, cats, o) => Object.assign({ id, anchor: "wall", side, pos, size, cats }, o),
2391
+ corner: (id, corner, size, cats, o) => Object.assign({ id, anchor: "corner", corner, size, cats }, o),
2392
+ center: (id, fx, fz, size, cats, o) => Object.assign({ id, anchor: "center", fx, fz, size, cats }, o),
2393
+ windowSlot: (id, size, cats, o) => Object.assign({ id, anchor: "window", size, cats }, o),
2394
+ on: (id, parent, cats, o) => Object.assign({ id, anchor: "on", on: parent, cats }, o),
2395
+ inside: (id, parent, cats, o) => Object.assign({ id, anchor: "inside", on: parent, cats }, o)
2396
+ };
130
2397
  var en_default = {
131
2398
  labels: {
132
2399
  floor_n: "Floor {n}",
@@ -321,6 +2588,82 @@ var GameUrban = (() => {
321
2588
  reason_permeable_min: "\xC1rea perme\xE1vel da zona"
322
2589
  }
323
2590
  };
2591
+ var version = "0.1.0";
2592
+ function buildEngine(scope) {
2593
+ const scoped = (fn) => ((...args) => withRegistries(scope, () => fn(...args)));
2594
+ return {
2595
+ version,
2596
+ generate: scoped(generate),
2597
+ slots: scoped(slots),
2598
+ validate: scoped(validate),
2599
+ palette: scoped(palette),
2600
+ limitsOf: scoped(limitsOf),
2601
+ listTemplates: scoped(listTemplates),
2602
+ listStates: scoped(listStates),
2603
+ // content registration
2604
+ registerItem: scoped(registerItem),
2605
+ registerItems: scoped(registerItems),
2606
+ registerLibrary: scoped(registerLibrary),
2607
+ registerLibraryAlias: scoped(registerLibraryAlias),
2608
+ registerTemplate: scoped(registerTemplate),
2609
+ registerStyle: scoped(registerStyle),
2610
+ registerPaletteStyle: scoped(registerPaletteStyle),
2611
+ registerRoomColors: scoped(registerRoomColors),
2612
+ registerDecorProfile: scoped(registerDecorProfile),
2613
+ registerPass: scoped(registerPass),
2614
+ registerGroundOverlay: scoped(registerGroundOverlay),
2615
+ registerWallTop: scoped(registerWallTop),
2616
+ registerSeedKeys: scoped(registerSeedKeys),
2617
+ registerState: scoped(registerState),
2618
+ setStateDistribution: scoped(setStateDistribution),
2619
+ registerLocale: scoped(registerLocale),
2620
+ registerResultExtension: scoped(registerResultExtension),
2621
+ registerFindsProvider: scoped(registerFindsProvider),
2622
+ findsFor: scoped(findsFor),
2623
+ findsAnswer: scoped(findsAnswer),
2624
+ registerViewerLayer: scoped(registerViewerLayer),
2625
+ viewerLayers: scope.VIEWER_LAYERS,
2626
+ blocksWindow: scoped(blocksWindow),
2627
+ // zoning
2628
+ defineZone: scoped(defineZone),
2629
+ getZone: scoped(getZone),
2630
+ listZones: scoped(listZones),
2631
+ checkLot: scoped(checkLot),
2632
+ registerPermeableGround: scoped(registerPermeableGround),
2633
+ // kits and data
2634
+ slotKit,
2635
+ translate: scoped(translate),
2636
+ listLocales: scoped(listLocales),
2637
+ templates: scope.TEMPLATES,
2638
+ catalog: scope.CATALOG,
2639
+ libraries: scope.LIBRARIES,
2640
+ styles: scope.STYLES,
2641
+ paletteStyles: scope.PALETTE_STYLES,
2642
+ createRng,
2643
+ mulberry32,
2644
+ hashString,
2645
+ overlaps,
2646
+ windowZone,
2647
+ openingZones,
2648
+ color: { lstar, hex: hexString, hsl: intToHsl, HIGH_SATURATION, MIN_WALL_FLOOR_CONTRAST, MIN_FABRIC_FLOOR_CONTRAST },
2649
+ constants: { WALL_THICKNESS, DOOR_CLEARANCE, PLAYER_RADIUS, DOOR_EDGE_CLEARANCE, WINDOW_EDGE_CLEARANCE, OVERHEAD_TOP, CAR_CLEARANCE }
2650
+ };
2651
+ }
2652
+ function seedBase(scope) {
2653
+ withRegistries(scope, () => {
2654
+ registerGroundItems();
2655
+ registerPermeableGround("lawn", "dirt");
2656
+ registerLocale("en", en_default);
2657
+ registerLocale("pt-BR", pt_BR_default);
2658
+ });
2659
+ }
2660
+ var defaultEngine;
2661
+ function defaultFloorplan() {
2662
+ if (defaultEngine) return defaultEngine;
2663
+ seedBase(getDefaultRegistries());
2664
+ defaultEngine = buildEngine(getDefaultRegistries());
2665
+ return defaultEngine;
2666
+ }
324
2667
  function createSilhouetteRandom(seed) {
325
2668
  let state = seed;
326
2669
  return () => {
@@ -521,10 +2864,7 @@ var GameUrban = (() => {
521
2864
  }
522
2865
  return b.finish({ x: p.x, z: p.z, width: w + 0.5, depth: d + 0.5, height: 4.6 });
523
2866
  }
524
- registerGroundItems();
525
- registerPermeableGround("lawn", "dirt");
526
- registerLocale("en", en_default);
527
- registerLocale("pt-BR", pt_BR_default);
2867
+ var engine = defaultFloorplan();
528
2868
 
529
2869
  // src/scene.ts
530
2870
  var ROOT2 = -1;
@@ -537,7 +2877,7 @@ var GameUrban = (() => {
537
2877
  const parts = [];
538
2878
  const items = [];
539
2879
  const windows = { lit: [], dark: [] };
540
- const lights = options.budget ?? createLightBudget(Number.POSITIVE_INFINITY);
2880
+ const lights = options.budget ?? createLightAllowance(Number.POSITIVE_INFINITY);
541
2881
  const pick = (list) => list[Math.floor(random() * list.length)];
542
2882
  const group = (x, y, z, ry = 0, parent = ROOT2) => parts.push({ shape: "group", parent, x, y, z, ry }) - 1;
543
2883
  const box = (x, y, z, width, height, depth, color, o = {}) => {
@@ -838,7 +3178,7 @@ var GameUrban = (() => {
838
3178
  var lerp = (a, b, t) => a + (b - a) * t;
839
3179
  function fillBlock(b, cx, cz, size, minHeight, maxHeight, options = {}) {
840
3180
  const placed = [];
841
- const palette = options.palette ?? DEFAULT_FACADES;
3181
+ const palette2 = options.palette ?? DEFAULT_FACADES;
842
3182
  const half = (size - STREET_MARGIN) / 2;
843
3183
  const k = options.k || DEFAULT_PARTITIONS;
844
3184
  const lot = (size - STREET_MARGIN) / k;
@@ -864,7 +3204,7 @@ var GameUrban = (() => {
864
3204
  }
865
3205
  const x = cx - half + lot * (i + 0.5);
866
3206
  const z = cz - half + lot * (j + 0.5);
867
- const color = b.pick(palette);
3207
+ const color = b.pick(palette2);
868
3208
  placed.push((options.draw ?? drawBuilding)(b, x, z, width, depth, height, color, extra ? { ...options, ...extra } : options));
869
3209
  }
870
3210
  }
@@ -894,16 +3234,16 @@ var GameUrban = (() => {
894
3234
  b.prism(x, 0, z, w, w * 0.7, d, color ?? colors(c).tent, -1, ry);
895
3235
  }
896
3236
  function drawPine(b, x, z, scale = 1, y = 0, c) {
897
- const palette = colors(c);
898
- b.cylinder(x, y, z, 0.12 * scale, 0.16 * scale, 1 * scale, palette.trunk);
899
- const green = b.pick(palette.pines);
3237
+ const palette2 = colors(c);
3238
+ b.cylinder(x, y, z, 0.12 * scale, 0.16 * scale, 1 * scale, palette2.trunk);
3239
+ const green = b.pick(palette2.pines);
900
3240
  b.cone(x, y + 0.8 * scale, z, 1.1 * scale, 2.2 * scale, green);
901
3241
  b.cone(x, y + 2.1 * scale, z, 0.8 * scale, 1.8 * scale, green);
902
3242
  }
903
3243
  function drawTree(b, x, z, scale = 1, c) {
904
- const palette = colors(c);
905
- b.cylinder(x, 0, z, 0.12 * scale, 0.18 * scale, 1.8 * scale, palette.trunk);
906
- b.crown(x, 2.6 * scale, z, 1.3 * scale, b.pick(palette.crowns));
3244
+ const palette2 = colors(c);
3245
+ b.cylinder(x, 0, z, 0.12 * scale, 0.18 * scale, 1.8 * scale, palette2.trunk);
3246
+ b.crown(x, 2.6 * scale, z, 1.3 * scale, b.pick(palette2.crowns));
907
3247
  }
908
3248
  function drawContainer(b, x, y, z, ry, color) {
909
3249
  const group = b.group(x, y, z, ry);
@@ -911,11 +3251,11 @@ var GameUrban = (() => {
911
3251
  for (let k = -2; k <= 2; k++) b.box(k * 0.5, 0, 3.04, 0.06, 2.6, 0.04, 1710618, { parent: group });
912
3252
  }
913
3253
  function drawWatchtower(b, x, z, searchlight = false, c) {
914
- const palette = colors(c).watchtower;
915
- for (const a of [-1, 1]) for (const d of [-1, 1]) b.box(x + a * 1.2, 0, z + d * 1.2, 0.25, 7, 0.25, palette.leg);
916
- b.box(x, 7, z, 3.2, 1.2, 3.2, palette.cabin);
917
- b.box(x, 8.2, z, 3.6, 0.15, 3.6, palette.deck);
918
- for (const side of [-1.5, 1.5]) b.box(x + side, 8.2, z, 0.15, 1.6, 0.15, palette.deck);
3254
+ const palette2 = colors(c).watchtower;
3255
+ for (const a of [-1, 1]) for (const d of [-1, 1]) b.box(x + a * 1.2, 0, z + d * 1.2, 0.25, 7, 0.25, palette2.leg);
3256
+ b.box(x, 7, z, 3.2, 1.2, 3.2, palette2.cabin);
3257
+ b.box(x, 8.2, z, 3.6, 0.15, 3.6, palette2.deck);
3258
+ for (const side of [-1.5, 1.5]) b.box(x + side, 8.2, z, 0.15, 1.6, 0.15, palette2.deck);
919
3259
  if (searchlight) b.spot({ x, y: 9, z, color: 15134463, intensity: 2.4, distance: 70, decay: 1.2, angle: 0.35, penumbra: 0.5, target: { x: x + 20, y: 0, z: z + 20 }, sweep: { speed: 0.5, radius: 25 } });
920
3260
  }
921
3261
  function drawSandbags(b, x, z, length, along, rows = 3, c) {
@@ -935,34 +3275,6 @@ var GameUrban = (() => {
935
3275
  b.line(points.map(([px, pz]) => [px, y, pz]), color ?? colors(c).wire);
936
3276
  }
937
3277
 
938
- // ../game-contracts/dist/index.js
939
- var UNSAFE_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
940
- var isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
941
- var deepMerge = (base, patch) => {
942
- if (!patch) return base;
943
- const result = { ...base };
944
- for (const [key, value] of Object.entries(patch)) {
945
- if (value === void 0 || UNSAFE_KEYS.has(key)) continue;
946
- const current = result[key];
947
- result[key] = isPlainObject(current) && isPlainObject(value) ? deepMerge(current, value) : value;
948
- }
949
- return result;
950
- };
951
- var mergeById = (shipped, extra) => {
952
- const entries = /* @__PURE__ */ new Map();
953
- for (const entry of [...shipped, ...extra ?? []]) entries.set(entry.id, entry);
954
- return entries;
955
- };
956
- var applyPatches = (entries, patches) => {
957
- for (const [id, patch] of Object.entries(patches ?? {})) {
958
- const entry = entries.get(id);
959
- if (entry) entries.set(id, deepMerge(entry, patch));
960
- }
961
- };
962
- var removeIds = (entries, ids) => {
963
- for (const id of ids ?? []) entries.delete(id);
964
- };
965
-
966
3278
  // src/neighbourhoods/houses.ts
967
3279
  var DEFAULT_WIDTH = 8;
968
3280
  var DEFAULT_DEPTH = 10;
@@ -1701,7 +4013,7 @@ var GameUrban = (() => {
1701
4013
  const plan = typeof planOrId === "string" ? plans.resolve(planOrId) : resolvePlan(planOrId);
1702
4014
  const locale = options.locale ?? DEFAULT_LOCALE2;
1703
4015
  const cap = Math.max(0, options.maxFloors ?? maxFloors);
1704
- const clamp2 = (n) => Math.min(cap, Math.max(0, n));
4016
+ const clamp3 = (n) => Math.min(cap, Math.max(0, n));
1705
4017
  const districts = plan.districts.map((d) => {
1706
4018
  const kind = kinds.get(d.kind);
1707
4019
  const kindText = text(kind.text, locale);
@@ -1715,7 +4027,7 @@ var GameUrban = (() => {
1715
4027
  color: kind.color,
1716
4028
  zone: kind.zone,
1717
4029
  pattern: kind.pattern,
1718
- floors: [clamp2(lo), clamp2(hi)],
4030
+ floors: [clamp3(lo), clamp3(hi)],
1719
4031
  uses: d.uses ?? kind.uses,
1720
4032
  block: kind.block
1721
4033
  };
@@ -1798,10 +4110,10 @@ var GameUrban = (() => {
1798
4110
  }
1799
4111
 
1800
4112
  // src/terrain/math.ts
1801
- var clamp = (v, a, b) => v < a ? a : v > b ? b : v;
4113
+ var clamp2 = (v, a, b) => v < a ? a : v > b ? b : v;
1802
4114
  var lerp2 = (a, b, t) => a + (b - a) * t;
1803
4115
  var smoothstep = (a, b, x) => {
1804
- const t = clamp((x - a) / (b - a), 0, 1);
4116
+ const t = clamp2((x - a) / (b - a), 0, 1);
1805
4117
  return t * t * (3 - 2 * t);
1806
4118
  };
1807
4119
  var hash2 = (a, b) => {
@@ -1815,7 +4127,7 @@ var GameUrban = (() => {
1815
4127
  const [bx, by] = points[index + 1];
1816
4128
  const dx = bx - ax;
1817
4129
  const dy = by - ay;
1818
- const t = clamp(((px - ax) * dx + (py - ay) * dy) / (dx * dx + dy * dy), 0, 1);
4130
+ const t = clamp2(((px - ax) * dx + (py - ay) * dy) / (dx * dx + dy * dy), 0, 1);
1819
4131
  const d = Math.hypot(px - ax - t * dx, py - ay - t * dy);
1820
4132
  if (d < best) best = d;
1821
4133
  }
@@ -1924,7 +4236,8 @@ var GameUrban = (() => {
1924
4236
  }
1925
4237
 
1926
4238
  // src/kit.ts
1927
- var DEFAULT_LIGHT_BUDGET = 16;
4239
+ var DEFAULT_LIGHT_ALLOWANCE = 16;
4240
+ var DEFAULT_LIGHT_BUDGET = DEFAULT_LIGHT_ALLOWANCE;
1928
4241
  var DEFAULT_URBAN_SEED = 7;
1929
4242
  function createUrban(options = {}) {
1930
4243
  const entries = mergeById(DEFAULT_POLES, options.packs?.poles);
@@ -1935,7 +4248,7 @@ var GameUrban = (() => {
1935
4248
  applyPatches(patterns, options.patch?.neighbourhoods);
1936
4249
  removeIds(patterns, options.remove?.neighbourhoods);
1937
4250
  const composers = { ...COMPOSERS, ...options.composers };
1938
- const lightBudget = options.lights?.budget ?? DEFAULT_LIGHT_BUDGET;
4251
+ const lightBudget = options.lights?.budget ?? DEFAULT_LIGHT_ALLOWANCE;
1939
4252
  const facades = options.facades ?? DEFAULT_FACADES;
1940
4253
  const propColors = { ...DEFAULT_PROP_COLORS, ...options.props };
1941
4254
  const towers = options.towers ? options.towers === true ? {} : options.towers : null;
@@ -1977,7 +4290,7 @@ var GameUrban = (() => {
1977
4290
  wires: (b, points, y, color) => drawWires(b, points, y, color, propColors)
1978
4291
  }
1979
4292
  };
1980
- const builder = (o = {}) => createUrbanBuilder(o.random ?? createSilhouetteRandom(o.seed ?? DEFAULT_URBAN_SEED), { budget: createLightBudget(o.budget ?? lightBudget) });
4293
+ const builder = (o = {}) => createUrbanBuilder(o.random ?? createSilhouetteRandom(o.seed ?? DEFAULT_URBAN_SEED), { budget: createLightAllowance(o.budget ?? lightBudget) });
1981
4294
  const neighbourhoods = {
1982
4295
  ids: () => [...patterns.keys()],
1983
4296
  get(id) {
@@ -6898,7 +9211,7 @@ var GameUrban = (() => {
6898
9211
  const THREE = kit.THREE;
6899
9212
  const trl = fx.t;
6900
9213
  const lbl = fx.label;
6901
- const V3 = kit.V3, PI = Math.PI, MT = kit.materials, CM = kit.makeMaterial, G = kit.geo, profX = kit.profX, halfAt = kit.halfAt, clamp2 = kit.clamp, smooth = kit.smooth;
9214
+ const V3 = kit.V3, PI = Math.PI, MT = kit.materials, CM = kit.makeMaterial, G = kit.geo, profX = kit.profX, halfAt = kit.halfAt, clamp3 = kit.clamp, smooth = kit.smooth;
6902
9215
  const GLOSS = CM("#07080a", { roughness: 0.18, metalness: 0.5 });
6903
9216
  const ALLOY = CM("#b9bec2", { metalness: 0.75, roughness: 0.3 });
6904
9217
  const ALLOYD = CM("#3a3e42", { metalness: 0.6, roughness: 0.35 });
@@ -6912,7 +9225,7 @@ var GameUrban = (() => {
6912
9225
  const a = (y - o.ys) / (o.belt - o.ys);
6913
9226
  return o.kt * a * a;
6914
9227
  }
6915
- const b = clamp2((o.ys - y) / (o.ys - o.yb), 0, 1.25);
9228
+ const b = clamp3((o.ys - y) / (o.ys - o.yb), 0, 1.25);
6916
9229
  return o.kb * b * b;
6917
9230
  }
6918
9231
  const f = function(x, y, z) {
@@ -6992,13 +9305,13 @@ var GameUrban = (() => {
6992
9305
  });
6993
9306
  }
6994
9307
  function roofAt(gh, x) {
6995
- const rR = gh.roofR || gh.roof, t = clamp2((x - gh.rt) / (gh.ft - gh.rt), 0, 1);
9308
+ const rR = gh.roofR || gh.roof, t = clamp3((x - gh.rt) / (gh.ft - gh.rt), 0, 1);
6996
9309
  return rR + (gh.roof - rR) * t + (gh.crown || 0) * 4 * t * (1 - t);
6997
9310
  }
6998
9311
  function windows(gh) {
6999
9312
  const inn = 0.07, rise = gh.rise || 0;
7000
9313
  function ybAt(x) {
7001
- return gh.belt + 0.035 + rise * clamp2((gh.fb - x) / (gh.fb - gh.rb), 0, 1);
9314
+ return gh.belt + 0.035 + rise * clamp3((gh.fb - x) / (gh.fb - gh.rb), 0, 1);
7002
9315
  }
7003
9316
  function lx(x0, x1, y, top) {
7004
9317
  return x0 + (x1 - x0) * (y - gh.belt) / (top - gh.belt);
@@ -7201,7 +9514,7 @@ var GameUrban = (() => {
7201
9514
  }
7202
9515
  const G2 = { P, N, nu, nv, y0: o.y0, y1: o.y1, len: tot * (full ? 2 : 1), E, sd };
7203
9516
  G2.at = function(u, y) {
7204
- const fi = clamp2(u, 0, 1) * nu, fj = clamp2((y - o.y0) / (o.y1 - o.y0), 0, 1) * nv, i0 = Math.min(nu - 1, Math.floor(fi)), j0 = Math.min(nv - 1, Math.floor(fj)), a = fi - i0, b = fj - j0;
9517
+ const fi = clamp3(u, 0, 1) * nu, fj = clamp3((y - o.y0) / (o.y1 - o.y0), 0, 1) * nv, i0 = Math.min(nu - 1, Math.floor(fi)), j0 = Math.min(nv - 1, Math.floor(fj)), a = fi - i0, b = fj - j0;
7205
9518
  function L(M) {
7206
9519
  return M[i0][j0].clone().multiplyScalar((1 - a) * (1 - b)).addScaledVector(M[i0 + 1][j0], a * (1 - b)).addScaledVector(M[i0][j0 + 1], (1 - a) * b).addScaledVector(M[i0 + 1][j0 + 1], a * b);
7207
9520
  }
@@ -7338,10 +9651,10 @@ var GameUrban = (() => {
7338
9651
  m.rotation.order = "XYZ";
7339
9652
  return m;
7340
9653
  }
7341
- function regLight(c, kind, label, inner, lens, pos) {
7342
- const L = { group: null, inner, lens, kind, tint: kind === "headlight" ? "clear" : "red", broken: false };
9654
+ function regLight(c, kind, label, inner2, lens, pos) {
9655
+ const L = { group: null, inner: inner2, lens, kind, tint: kind === "headlight" ? "clear" : "red", broken: false };
7343
9656
  c.lights.push(L);
7344
- const pt = c.part(kind, label, inner[0], pos, 0.8);
9657
+ const pt = c.part(kind, label, inner2[0], pos, 0.8);
7345
9658
  pt.light = L;
7346
9659
  return L;
7347
9660
  }
@@ -7373,7 +9686,7 @@ var GameUrban = (() => {
7373
9686
  du: 0.018,
7374
9687
  dv: 0.012
7375
9688
  });
7376
- const pu = pad / Gd.len, inner = [], lens = [];
9689
+ const pu = pad / Gd.len, inner2 = [], lens = [];
7377
9690
  const yb = function(u) {
7378
9691
  return sh(u)[0];
7379
9692
  }, yt = function(u) {
@@ -7398,13 +9711,13 @@ var GameUrban = (() => {
7398
9711
  ring.castShadow = false;
7399
9712
  const b = onSurf(c.sphere(rad, MT.head, 0, 0, 0, 1, 0.55, 1), s, 0.01);
7400
9713
  b.castShadow = false;
7401
- inner.push(b);
9714
+ inner2.push(b);
7402
9715
  });
7403
9716
  (o.drl || []).forEach(function(d) {
7404
- inner.push(skin(c, Gd, d.mat || DRL, { u0: d.u0, u1: d.u1, yb: d.yb, yt: d.yt, off: 95e-4, nv: 2 }));
9717
+ inner2.push(skin(c, Gd, d.mat || DRL, { u0: d.u0, u1: d.u1, yb: d.yb, yt: d.yt, off: 95e-4, nv: 2 }));
7405
9718
  });
7406
9719
  (o.amber || []).forEach(function(d) {
7407
- inner.push(skin(c, Gd, MT.amber, { u0: d.u0, u1: d.u1, yb: d.yb, yt: d.yt, off: 9e-3, nv: 2 }));
9720
+ inner2.push(skin(c, Gd, MT.amber, { u0: d.u0, u1: d.u1, yb: d.yb, yt: d.yt, off: 9e-3, nv: 2 }));
7408
9721
  });
7409
9722
  const ln = skin(c, Gd, MT.lens, {
7410
9723
  yb: function(u) {
@@ -7418,9 +9731,9 @@ var GameUrban = (() => {
7418
9731
  order: 3
7419
9732
  });
7420
9733
  lens.push(ln);
7421
- if (!inner.length) inner.push(ln);
9734
+ if (!inner2.length) inner2.push(ln);
7422
9735
  const cs = Gd.at(0.5, (rg[0] + rg[1]) / 2).p;
7423
- regLight(c, "headlight", o.label || (sd > 0 ? trl("labels.right_headlight") : trl("labels.left_headlight")), inner, lens, cs);
9736
+ regLight(c, "headlight", o.label || (sd > 0 ? trl("labels.right_headlight") : trl("labels.left_headlight")), inner2, lens, cs);
7424
9737
  return Gd;
7425
9738
  };
7426
9739
  K.tail = function(sd, o) {
@@ -7434,7 +9747,7 @@ var GameUrban = (() => {
7434
9747
  du: 0.018,
7435
9748
  dv: 0.012
7436
9749
  });
7437
- const pu = pad / Gd.len, inner = [], lens = [], yb = function(u) {
9750
+ const pu = pad / Gd.len, inner2 = [], lens = [], yb = function(u) {
7438
9751
  return sh(u)[0];
7439
9752
  }, yt = function(u) {
7440
9753
  return sh(u)[1];
@@ -7449,15 +9762,15 @@ var GameUrban = (() => {
7449
9762
  off: 3e-3,
7450
9763
  nv: 4
7451
9764
  });
7452
- inner.push(skin(c, Gd, o.main || TAILD, { u0: pu, u1: 1 - pu, yb, yt, off: 6e-3, nv: 4 }));
9765
+ inner2.push(skin(c, Gd, o.main || TAILD, { u0: pu, u1: 1 - pu, yb, yt, off: 6e-3, nv: 4 }));
7453
9766
  (o.hi || []).forEach(function(d) {
7454
- inner.push(skin(c, Gd, d.mat || TAILHI, { u0: d.u0, u1: d.u1, yb: d.yb, yt: d.yt, off: 9e-3, nv: 2 }));
9767
+ inner2.push(skin(c, Gd, d.mat || TAILHI, { u0: d.u0, u1: d.u1, yb: d.yb, yt: d.yt, off: 9e-3, nv: 2 }));
7455
9768
  });
7456
9769
  (o.rev || []).forEach(function(d) {
7457
- inner.push(skin(c, Gd, MT.rev, { u0: d.u0, u1: d.u1, yb: d.yb, yt: d.yt, off: 85e-4, nv: 2 }));
9770
+ inner2.push(skin(c, Gd, MT.rev, { u0: d.u0, u1: d.u1, yb: d.yb, yt: d.yt, off: 85e-4, nv: 2 }));
7458
9771
  });
7459
9772
  (o.amber || []).forEach(function(d) {
7460
- inner.push(skin(c, Gd, MT.amber, { u0: d.u0, u1: d.u1, yb: d.yb, yt: d.yt, off: 85e-4, nv: 2 }));
9773
+ inner2.push(skin(c, Gd, MT.amber, { u0: d.u0, u1: d.u1, yb: d.yb, yt: d.yt, off: 85e-4, nv: 2 }));
7461
9774
  });
7462
9775
  lens.push(
7463
9776
  skin(c, Gd, o.lens || MT.lensRed, {
@@ -7476,7 +9789,7 @@ var GameUrban = (() => {
7476
9789
  c,
7477
9790
  "taillight",
7478
9791
  o.label || (sd > 0 ? trl("labels.right_taillight") : trl("labels.left_taillight")),
7479
- inner,
9792
+ inner2,
7480
9793
  lens,
7481
9794
  Gd.at(0.4, (rg[0] + rg[1]) / 2).p
7482
9795
  );
@@ -7781,7 +10094,7 @@ var GameUrban = (() => {
7781
10094
  c.roundBox(x1 - x0, 0.08, 0.035, 0.015, MT.trim, (x0 + x1) / 2, by + 0.05, sd * (hw - 8e-3));
7782
10095
  if (s.clad)
7783
10096
  s.arches.forEach(function(a) {
7784
- const a0 = Math.asin(clamp2((by - a[1]) / a[2], -1, 1)), tor = c.add(new THREE.TorusGeometry(a[2] + 0.02, 0.035, 6, 26, PI - 2 * a0), MT.trim, a[0], a[1], sd * (hw - 0.01));
10097
+ const a0 = Math.asin(clamp3((by - a[1]) / a[2], -1, 1)), tor = c.add(new THREE.TorusGeometry(a[2] + 0.02, 0.035, 6, 26, PI - 2 * a0), MT.trim, a[0], a[1], sd * (hw - 0.01));
7785
10098
  tor.rotation.z = a0;
7786
10099
  tor.scale.z = 0.8;
7787
10100
  });
@@ -7912,7 +10225,7 @@ var GameUrban = (() => {
7912
10225
  c.cylinder(0.06, 0.06, 0.012, MT.head, x + 0.055, y, z, "x");
7913
10226
  }
7914
10227
  function sm(t) {
7915
- t = clamp2(t, 0, 1);
10228
+ t = clamp3(t, 0, 1);
7916
10229
  return t * t * (3 - 2 * t);
7917
10230
  }
7918
10231
  function sideGrid(S, sd, o) {
@@ -7929,7 +10242,7 @@ var GameUrban = (() => {
7929
10242
  }
7930
10243
  const G2 = { P, N, nu, nv, y0: o.y0, y1: o.y1, len: Math.abs(o.x1 - o.x0) };
7931
10244
  G2.at = function(u, y) {
7932
- const fi = clamp2(u, 0, 1) * nu, fj = clamp2((y - o.y0) / (o.y1 - o.y0), 0, 1) * nv, i0 = Math.min(nu - 1, Math.floor(fi)), j0 = Math.min(nv - 1, Math.floor(fj)), a = fi - i0, b = fj - j0;
10245
+ const fi = clamp3(u, 0, 1) * nu, fj = clamp3((y - o.y0) / (o.y1 - o.y0), 0, 1) * nv, i0 = Math.min(nu - 1, Math.floor(fi)), j0 = Math.min(nv - 1, Math.floor(fj)), a = fi - i0, b = fj - j0;
7933
10246
  function L(M) {
7934
10247
  return M[i0][j0].clone().multiplyScalar((1 - a) * (1 - b)).addScaledVector(M[i0 + 1][j0], a * (1 - b)).addScaledVector(M[i0][j0 + 1], (1 - a) * b).addScaledVector(M[i0 + 1][j0 + 1], a * b);
7935
10248
  }
@@ -9657,7 +11970,7 @@ var GameUrban = (() => {
9657
11970
  const g = K.grid(1, 0, { z1: 0.66, y0: 0.27, y1: 0.61, du: 0.03, dv: 0.03 }), zt = 0.6, zb = 0.43, yB0 = 0.3, yT = 0.585;
9658
11971
  const gyb = function(u) {
9659
11972
  const z = Math.abs(g.zu(u));
9660
- return z <= zb ? yB0 : yB0 + (yT - 0.02 - yB0) * clamp2((z - zb) / (zt - zb), 0, 1);
11973
+ return z <= zb ? yB0 : yB0 + (yT - 0.02 - yB0) * clamp3((z - zb) / (zt - zb), 0, 1);
9661
11974
  };
9662
11975
  K.skin(g, grilleMat("mesh"), { u0: g.uz(-zt), u1: g.uz(zt), yb: gyb, yt: yT, off: 0.022, tile: 0.05, nv: 6 });
9663
11976
  outline(c2, g, { u0: g.uz(-zt), u1: g.uz(zt), yb: gyb, yt: yT }, 0.012, LH, 0.024);
@@ -14607,8 +16920,8 @@ var GameUrban = (() => {
14607
16920
  kit.steeringWheel(c, 1.5, 1.42, -0.62, { r: 0.2, tilt: 0.9, col: 0.35 });
14608
16921
  c.roundBox(0.35, 0.3, 0.7, 0.04, CM("#25261f"), 1.85, 1.45, -0.6);
14609
16922
  kit.seat(c, 0.15, fl, 0, { w: 0.5, h: 0.45, back: 0.62, lean: 0.12, mat: CM("#2f3226", { roughness: 0.9 }) });
14610
- for (let r2 = 0; r2 < 3; r2++)
14611
- c.roundBox(0.25, 0.32, 0.25, 0.03, CM("#3f4a2c", { roughness: 0.6 }), -0.7 - r2 * 0.6, roofY - 0.35, 0.68);
16923
+ for (let r22 = 0; r22 < 3; r22++)
16924
+ c.roundBox(0.25, 0.32, 0.25, 0.03, CM("#3f4a2c", { roughness: 0.6 }), -0.7 - r22 * 0.6, roofY - 0.35, 0.68);
14612
16925
  const ramp2 = new THREE.Group(), RL = 1.36, rmat = paintMat(c, col);
14613
16926
  ramp2.position.set(R - 0.05, 0.62, 0);
14614
16927
  ramp2.rotation.z = -PI / 2;
@@ -15815,7 +18128,7 @@ var GameUrban = (() => {
15815
18128
  const THREE = kit.THREE;
15816
18129
  const trl = fx.t;
15817
18130
  const lbl = fx.label;
15818
- const V3 = kit.V3, PI = Math.PI, MT = kit.materials, CM = kit.makeMaterial, G = kit.geo, profX = kit.profX, halfAt = kit.halfAt, clamp2 = kit.clamp, smooth = kit.smooth;
18131
+ const V3 = kit.V3, PI = Math.PI, MT = kit.materials, CM = kit.makeMaterial, G = kit.geo, profX = kit.profX, halfAt = kit.halfAt, clamp3 = kit.clamp, smooth = kit.smooth;
15819
18132
  const GLOSS = CM("#07080a", { roughness: 0.18, metalness: 0.5 });
15820
18133
  const ALLOY = CM("#b9bec2", { metalness: 0.75, roughness: 0.3 });
15821
18134
  const ALLOYD = CM("#3a3e42", { metalness: 0.6, roughness: 0.35 });
@@ -15834,7 +18147,7 @@ var GameUrban = (() => {
15834
18147
  const a = (y - o.ys) / (o.belt - o.ys);
15835
18148
  return o.kt * a * a;
15836
18149
  }
15837
- const b = clamp2((o.ys - y) / (o.ys - o.yb), 0, 1.25);
18150
+ const b = clamp3((o.ys - y) / (o.ys - o.yb), 0, 1.25);
15838
18151
  return o.kb * b * b;
15839
18152
  }
15840
18153
  const f = function(x, y, z) {
@@ -16014,13 +18327,13 @@ var GameUrban = (() => {
16014
18327
  return L;
16015
18328
  }
16016
18329
  function roofAt(gh, x) {
16017
- const rR = gh.roofR || gh.roof, t = clamp2((x - gh.rt) / (gh.ft - gh.rt), 0, 1);
18330
+ const rR = gh.roofR || gh.roof, t = clamp3((x - gh.rt) / (gh.ft - gh.rt), 0, 1);
16018
18331
  return rR + (gh.roof - rR) * t + (gh.crown || 0) * 4 * t * (1 - t);
16019
18332
  }
16020
18333
  function windows(gh) {
16021
18334
  const inn = 0.07, rise = gh.rise || 0;
16022
18335
  function ybAt(x) {
16023
- return gh.belt + 0.035 + rise * clamp2((gh.fb - x) / (gh.fb - gh.rb), 0, 1);
18336
+ return gh.belt + 0.035 + rise * clamp3((gh.fb - x) / (gh.fb - gh.rb), 0, 1);
16024
18337
  }
16025
18338
  function lx(x0, x1, y, top) {
16026
18339
  return x0 + (x1 - x0) * (y - gh.belt) / (top - gh.belt);
@@ -16231,7 +18544,7 @@ var GameUrban = (() => {
16231
18544
  }
16232
18545
  const G2 = { P, N, nu, nv, y0: o.y0, y1: o.y1, len: tot * (full ? 2 : 1), E, sd };
16233
18546
  G2.at = function(u, y) {
16234
- const fi = clamp2(u, 0, 1) * nu, fj = clamp2((y - o.y0) / (o.y1 - o.y0), 0, 1) * nv, i0 = Math.min(nu - 1, Math.floor(fi)), j0 = Math.min(nv - 1, Math.floor(fj)), a = fi - i0, b = fj - j0;
18547
+ const fi = clamp3(u, 0, 1) * nu, fj = clamp3((y - o.y0) / (o.y1 - o.y0), 0, 1) * nv, i0 = Math.min(nu - 1, Math.floor(fi)), j0 = Math.min(nv - 1, Math.floor(fj)), a = fi - i0, b = fj - j0;
16235
18548
  function L(M) {
16236
18549
  return M[i0][j0].clone().multiplyScalar((1 - a) * (1 - b)).addScaledVector(M[i0 + 1][j0], a * (1 - b)).addScaledVector(M[i0][j0 + 1], (1 - a) * b).addScaledVector(M[i0 + 1][j0 + 1], a * b);
16237
18550
  }
@@ -16368,10 +18681,10 @@ var GameUrban = (() => {
16368
18681
  m.rotation.order = "XYZ";
16369
18682
  return m;
16370
18683
  }
16371
- function regLight(c, kind, label, inner, lens, pos) {
16372
- const L = { group: null, inner, lens, kind, tint: kind === "headlight" ? "clear" : "red", broken: false };
18684
+ function regLight(c, kind, label, inner2, lens, pos) {
18685
+ const L = { group: null, inner: inner2, lens, kind, tint: kind === "headlight" ? "clear" : "red", broken: false };
16373
18686
  c.lights.push(L);
16374
- const pt = c.part(kind, label, inner[0], pos, 0.8);
18687
+ const pt = c.part(kind, label, inner2[0], pos, 0.8);
16375
18688
  pt.light = L;
16376
18689
  return L;
16377
18690
  }
@@ -16403,7 +18716,7 @@ var GameUrban = (() => {
16403
18716
  du: 0.018,
16404
18717
  dv: 0.012
16405
18718
  });
16406
- const pu = pad / Gd.len, inner = [], lens = [];
18719
+ const pu = pad / Gd.len, inner2 = [], lens = [];
16407
18720
  const yb = function(u) {
16408
18721
  return sh(u)[0];
16409
18722
  }, yt = function(u) {
@@ -16428,13 +18741,13 @@ var GameUrban = (() => {
16428
18741
  ring.castShadow = false;
16429
18742
  const b = onSurf(c.sphere(rad, MT.head, 0, 0, 0, 1, 0.55, 1), s, 0.01);
16430
18743
  b.castShadow = false;
16431
- inner.push(b);
18744
+ inner2.push(b);
16432
18745
  });
16433
18746
  (o.drl || []).forEach(function(d) {
16434
- inner.push(skin(c, Gd, d.mat || DRL, { u0: d.u0, u1: d.u1, yb: d.yb, yt: d.yt, off: 95e-4, nv: 2 }));
18747
+ inner2.push(skin(c, Gd, d.mat || DRL, { u0: d.u0, u1: d.u1, yb: d.yb, yt: d.yt, off: 95e-4, nv: 2 }));
16435
18748
  });
16436
18749
  (o.amber || []).forEach(function(d) {
16437
- inner.push(skin(c, Gd, MT.amber, { u0: d.u0, u1: d.u1, yb: d.yb, yt: d.yt, off: 9e-3, nv: 2 }));
18750
+ inner2.push(skin(c, Gd, MT.amber, { u0: d.u0, u1: d.u1, yb: d.yb, yt: d.yt, off: 9e-3, nv: 2 }));
16438
18751
  });
16439
18752
  const ln = skin(c, Gd, MT.lens, {
16440
18753
  yb: function(u) {
@@ -16448,9 +18761,9 @@ var GameUrban = (() => {
16448
18761
  order: 3
16449
18762
  });
16450
18763
  lens.push(ln);
16451
- if (!inner.length) inner.push(ln);
18764
+ if (!inner2.length) inner2.push(ln);
16452
18765
  const cs = Gd.at(0.5, (rg[0] + rg[1]) / 2).p;
16453
- regLight(c, "headlight", o.label || (sd > 0 ? trl("labels.right_headlight") : trl("labels.left_headlight")), inner, lens, cs);
18766
+ regLight(c, "headlight", o.label || (sd > 0 ? trl("labels.right_headlight") : trl("labels.left_headlight")), inner2, lens, cs);
16454
18767
  return Gd;
16455
18768
  };
16456
18769
  K.tail = function(sd, o) {
@@ -16464,7 +18777,7 @@ var GameUrban = (() => {
16464
18777
  du: 0.018,
16465
18778
  dv: 0.012
16466
18779
  });
16467
- const pu = pad / Gd.len, inner = [], lens = [], yb = function(u) {
18780
+ const pu = pad / Gd.len, inner2 = [], lens = [], yb = function(u) {
16468
18781
  return sh(u)[0];
16469
18782
  }, yt = function(u) {
16470
18783
  return sh(u)[1];
@@ -16479,15 +18792,15 @@ var GameUrban = (() => {
16479
18792
  off: 3e-3,
16480
18793
  nv: 4
16481
18794
  });
16482
- inner.push(skin(c, Gd, o.main || TAILD, { u0: pu, u1: 1 - pu, yb, yt, off: 6e-3, nv: 4 }));
18795
+ inner2.push(skin(c, Gd, o.main || TAILD, { u0: pu, u1: 1 - pu, yb, yt, off: 6e-3, nv: 4 }));
16483
18796
  (o.hi || []).forEach(function(d) {
16484
- inner.push(skin(c, Gd, d.mat || TAILHI, { u0: d.u0, u1: d.u1, yb: d.yb, yt: d.yt, off: 9e-3, nv: 2 }));
18797
+ inner2.push(skin(c, Gd, d.mat || TAILHI, { u0: d.u0, u1: d.u1, yb: d.yb, yt: d.yt, off: 9e-3, nv: 2 }));
16485
18798
  });
16486
18799
  (o.rev || []).forEach(function(d) {
16487
- inner.push(skin(c, Gd, MT.rev, { u0: d.u0, u1: d.u1, yb: d.yb, yt: d.yt, off: 85e-4, nv: 2 }));
18800
+ inner2.push(skin(c, Gd, MT.rev, { u0: d.u0, u1: d.u1, yb: d.yb, yt: d.yt, off: 85e-4, nv: 2 }));
16488
18801
  });
16489
18802
  (o.amber || []).forEach(function(d) {
16490
- inner.push(skin(c, Gd, MT.amber, { u0: d.u0, u1: d.u1, yb: d.yb, yt: d.yt, off: 85e-4, nv: 2 }));
18803
+ inner2.push(skin(c, Gd, MT.amber, { u0: d.u0, u1: d.u1, yb: d.yb, yt: d.yt, off: 85e-4, nv: 2 }));
16491
18804
  });
16492
18805
  lens.push(
16493
18806
  skin(c, Gd, o.lens || MT.lensRed, {
@@ -16506,7 +18819,7 @@ var GameUrban = (() => {
16506
18819
  c,
16507
18820
  "taillight",
16508
18821
  o.label || (sd > 0 ? trl("labels.right_taillight") : trl("labels.left_taillight")),
16509
- inner,
18822
+ inner2,
16510
18823
  lens,
16511
18824
  Gd.at(0.4, (rg[0] + rg[1]) / 2).p
16512
18825
  );
@@ -16792,7 +19105,7 @@ var GameUrban = (() => {
16792
19105
  c.roundBox(x1 - x0, 0.08, 0.035, 0.015, MT.trim, (x0 + x1) / 2, by + 0.05, sd * (hw - 8e-3));
16793
19106
  if (s.clad)
16794
19107
  s.arches.forEach(function(a) {
16795
- const a0 = Math.asin(clamp2((by - a[1]) / a[2], -1, 1)), tor = c.add(new THREE.TorusGeometry(a[2] + 0.02, 0.035, 6, 26, PI - 2 * a0), MT.trim, a[0], a[1], sd * (hw - 0.01));
19108
+ const a0 = Math.asin(clamp3((by - a[1]) / a[2], -1, 1)), tor = c.add(new THREE.TorusGeometry(a[2] + 0.02, 0.035, 6, 26, PI - 2 * a0), MT.trim, a[0], a[1], sd * (hw - 0.01));
16796
19109
  tor.rotation.z = a0;
16797
19110
  tor.scale.z = 0.8;
16798
19111
  });
@@ -16913,7 +19226,7 @@ var GameUrban = (() => {
16913
19226
  c.cylinder(0.06, 0.06, 0.012, MT.head, x + 0.055, y, z, "x");
16914
19227
  }
16915
19228
  function sm(t) {
16916
- t = clamp2(t, 0, 1);
19229
+ t = clamp3(t, 0, 1);
16917
19230
  return t * t * (3 - 2 * t);
16918
19231
  }
16919
19232
  function sideGrid(S, sd, o) {
@@ -16930,7 +19243,7 @@ var GameUrban = (() => {
16930
19243
  }
16931
19244
  const G2 = { P, N, nu, nv, y0: o.y0, y1: o.y1, len: Math.abs(o.x1 - o.x0) };
16932
19245
  G2.at = function(u, y) {
16933
- const fi = clamp2(u, 0, 1) * nu, fj = clamp2((y - o.y0) / (o.y1 - o.y0), 0, 1) * nv, i0 = Math.min(nu - 1, Math.floor(fi)), j0 = Math.min(nv - 1, Math.floor(fj)), a = fi - i0, b = fj - j0;
19246
+ const fi = clamp3(u, 0, 1) * nu, fj = clamp3((y - o.y0) / (o.y1 - o.y0), 0, 1) * nv, i0 = Math.min(nu - 1, Math.floor(fi)), j0 = Math.min(nv - 1, Math.floor(fj)), a = fi - i0, b = fj - j0;
16934
19247
  function L(M) {
16935
19248
  return M[i0][j0].clone().multiplyScalar((1 - a) * (1 - b)).addScaledVector(M[i0 + 1][j0], a * (1 - b)).addScaledVector(M[i0][j0 + 1], (1 - a) * b).addScaledVector(M[i0 + 1][j0 + 1], a * b);
16936
19249
  }
@@ -17089,7 +19402,7 @@ var GameUrban = (() => {
17089
19402
  return function(c, I) {
17090
19403
  const gh = I.gh, col = c.meta.color;
17091
19404
  function yb(x) {
17092
- return gh.belt + 0.035 + (gh.rise || 0) * clamp2((gh.fb - x) / (gh.fb - gh.rb), 0, 1);
19405
+ return gh.belt + 0.035 + (gh.rise || 0) * clamp3((gh.fb - x) / (gh.fb - gh.rb), 0, 1);
17093
19406
  }
17094
19407
  ;
17095
19408
  [1, -1].forEach(function(sd) {
@@ -19960,7 +22273,7 @@ var GameUrban = (() => {
19960
22273
  return n.y > 0.3 && ce.x > 0.9 && ce.y > 0.6;
19961
22274
  },
19962
22275
  sdf: function(x, y, z) {
19963
- const t = clamp2((x - 1.17) / 0.5, 0, 1);
22276
+ const t = clamp3((x - 1.17) / 0.5, 0, 1);
19964
22277
  return Math.max(0.94 - x, x - 1.69, Math.abs(z) - (0.44 - 0.16 * t * t));
19965
22278
  }
19966
22279
  });
@@ -23958,7 +26271,7 @@ var GameUrban = (() => {
23958
26271
  const THREE = kit.THREE;
23959
26272
  const trl = fx.t;
23960
26273
  const lbl = fx.label;
23961
- const V3 = kit.V3, PI = Math.PI, MT = kit.materials, CM = kit.makeMaterial, clamp2 = kit.clamp, smooth = kit.smooth;
26274
+ const V3 = kit.V3, PI = Math.PI, MT = kit.materials, CM = kit.makeMaterial, clamp3 = kit.clamp, smooth = kit.smooth;
23962
26275
  const CLAD = CM("#1a1b1d", { roughness: 0.82 }), LINER = CM("#1c1d1f", { roughness: 0.92 }), RIMDARK = CM("#2a2d30", { metalness: 0.6, roughness: 0.4 });
23963
26276
  const RIMSIL = CM("#b9bec2", { metalness: 0.75, roughness: 0.3 }), RIMGUN = CM("#55595d", { metalness: 0.7, roughness: 0.35 });
23964
26277
  function axles(WB, oF, L, dF, dR) {
@@ -23989,16 +26302,16 @@ var GameUrban = (() => {
23989
26302
  const m = c.meta, W = m.width, by = o.bottom != null ? o.bottom : m.clearance, top = o.top != null ? o.top : m.belt;
23990
26303
  const Wg = m.glasshouse && m.glasshouse.Wg || W, front = m.front, rear = m.rear;
23991
26304
  const tuck = o.tuck == null ? 0.045 : o.tuck, peak = o.peak || 0.45;
23992
- const B = o.sh != null ? o.sh : clamp2(1 - (Wg + 0.06) / W, 0, 0.06);
26305
+ const B = o.sh != null ? o.sh : clamp3(1 - (Wg + 0.06) / W, 0, 0.06);
23993
26306
  const Rf = o.Rf || 0.55, Rr = o.Rr || 0.42, pf = o.pf == null ? 0.08 : o.pf, pr = o.pr == null ? 0.05 : o.pr;
23994
26307
  const bowF = o.bowF == null ? 0.07 : o.bowF, bowR = o.bowR == null ? 0.04 : o.bowR, Rb = o.Rb || 0.35, hw = W / 2;
23995
26308
  const crown = o.crown == null ? 0.045 : o.crown, xc = o.crownX != null ? o.crownX : m.glasshouse ? m.glasshouse.fb : front - 1, y0 = by + 0.55 * (top - by);
23996
26309
  function sc(y) {
23997
- const t = clamp2((y - by) / (top - by), 0, 1);
26310
+ const t = clamp3((y - by) / (top - by), 0, 1);
23998
26311
  return t < peak ? 1 - tuck * Math.pow(1 - t / peak, 2) : 1 - B * Math.pow((t - peak) / (1 - peak), 2);
23999
26312
  }
24000
26313
  function F(v2) {
24001
- const a = smooth((v2.x - (front - Rf)) / Rf), b = smooth((rear + Rr - v2.x) / Rr), zz = clamp2(v2.z / hw, -1, 1) * clamp2(v2.z / hw, -1, 1);
26314
+ const a = smooth((v2.x - (front - Rf)) / Rf), b = smooth((rear + Rr - v2.x) / Rr), zz = clamp3(v2.z / hw, -1, 1) * clamp3(v2.z / hw, -1, 1);
24002
26315
  const bf = smooth((v2.x - (front - Rb)) / Rb), br = smooth((rear + Rb - v2.x) / Rb);
24003
26316
  const cr = crown * (1 - zz) * smooth((v2.y - y0) / (top - y0)) * smooth((v2.x - xc) / 0.3);
24004
26317
  v2.x += -bowF * zz * bf + bowR * zz * br;
@@ -24075,7 +26388,7 @@ var GameUrban = (() => {
24075
26388
  o = o || {};
24076
26389
  const wd = o.w || 0.075, dp = o.d || 0.045, mat = o.mat || CLAD, out = [];
24077
26390
  arches.forEach(function(a) {
24078
- const a0 = Math.asin(clamp2((by - a[1]) / a[2], -1, 1)) - 0.02, r0 = a[2] - (o.inner == null ? 0.055 : o.inner), r1 = a[2] + wd;
26391
+ const a0 = Math.asin(clamp3((by - a[1]) / a[2], -1, 1)) - 0.02, r0 = a[2] - (o.inner == null ? 0.055 : o.inner), r1 = a[2] + wd;
24079
26392
  const sh = new THREE.Shape();
24080
26393
  if (o.trap) {
24081
26394
  const yb0 = Math.sin(a0) * r1, xb0 = Math.cos(a0) * r1, top = r1 * 0.93, xt = r1 * 0.52;
@@ -24424,7 +26737,7 @@ var GameUrban = (() => {
24424
26737
  function roof(c, drop, x0, x1) {
24425
26738
  const m = c.meta, b = m.glasshouse.belt, h = m.top - b;
24426
26739
  function dy(x, y) {
24427
- const t = clamp2((y - b) / h, 0, 1);
26740
+ const t = clamp3((y - b) / h, 0, 1);
24428
26741
  return t <= 0 ? 0 : drop * smooth((x0 - x) / (x0 - x1)) * t;
24429
26742
  }
24430
26743
  c.root.updateMatrixWorld(true);
@@ -25618,8 +27931,8 @@ var GameUrban = (() => {
25618
27931
  setOnHit(pt, function(c2, p0) {
25619
27932
  const o = p0.obj;
25620
27933
  if (o && o.userData.hung && o.parent && o.parent !== c2.vehicle) {
25621
- o.parent.rotation.x = clamp2(o.parent.rotation.x, -0.22, 0.22);
25622
- o.parent.rotation.y = clamp2(o.parent.rotation.y, -0.08, 0.08);
27934
+ o.parent.rotation.x = clamp3(o.parent.rotation.x, -0.22, 0.22);
27935
+ o.parent.rotation.y = clamp3(o.parent.rotation.y, -0.08, 0.08);
25623
27936
  }
25624
27937
  });
25625
27938
  });
@@ -26225,7 +28538,7 @@ var GameUrban = (() => {
26225
28538
  c2.cylinder(0.03, 0.04, 0.08, MT.chrome, sx + 2.35, fl + 0.99, -0.75);
26226
28539
  c2.part("fuel_tank", trl("labels.oxygen_cylinder"), null, new V3(sx + 2.35, fl + 0.5, -0.75), 0.5);
26227
28540
  c2.roundBox(0.04, 0.26, 0.34, 0.02, MT.screen, 0.1, 1.7, 0.5);
26228
- const light = c2.roundBox(
28541
+ const lightPanel = c2.roundBox(
26229
28542
  2.3,
26230
28543
  0.02,
26231
28544
  W - 0.3,
@@ -26235,7 +28548,7 @@ var GameUrban = (() => {
26235
28548
  m.top - 0.06,
26236
28549
  0
26237
28550
  );
26238
- light.castShadow = false;
28551
+ lightPanel.castShadow = false;
26239
28552
  m.roofItems = beaconBar;
26240
28553
  }
26241
28554
  });
@@ -26270,7 +28583,7 @@ var GameUrban = (() => {
26270
28583
  const shared = fx.shared;
26271
28584
  const trl = fx.t;
26272
28585
  const lbl = fx.label;
26273
- const V3 = kit.V3, PI = Math.PI, MT = kit.materials, CM = kit.makeMaterial, _paintCol = kit.paintColor, G = kit.geo, DS = THREE.DoubleSide, clamp2 = kit.clamp;
28586
+ const V3 = kit.V3, PI = Math.PI, MT = kit.materials, CM = kit.makeMaterial, _paintCol = kit.paintColor, G = kit.geo, DS = THREE.DoubleSide, clamp3 = kit.clamp;
26274
28587
  function colorize(geo, fn) {
26275
28588
  const p = geo.attributes.position, col = new Float32Array(p.count * 3);
26276
28589
  for (let i = 0; i < p.count; i++) {
@@ -26286,7 +28599,7 @@ var GameUrban = (() => {
26286
28599
  return a + (b - a) * t;
26287
28600
  }
26288
28601
  function sstep(a, b, x) {
26289
- const t = clamp2((x - a) / (b - a), 0, 1);
28602
+ const t = clamp3((x - a) / (b - a), 0, 1);
26290
28603
  return t * t * (3 - 2 * t);
26291
28604
  }
26292
28605
  function tab(T) {
@@ -26363,7 +28676,7 @@ var GameUrban = (() => {
26363
28676
  o,
26364
28677
  sec,
26365
28678
  t: function(x) {
26366
- return clamp2((x - o.x0) / o.L, 0, 1);
28679
+ return clamp3((x - o.x0) / o.L, 0, 1);
26367
28680
  },
26368
28681
  /* half beam of the hull at height y (inside, so floors and decks do not leak) */
26369
28682
  half: function(x, y) {
@@ -26537,7 +28850,7 @@ var GameUrban = (() => {
26537
28850
  for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {
26538
28851
  const a = poly[i], b = poly[j];
26539
28852
  if (a[1] > y !== b[1] > y && x < (b[0] - a[0]) * (y - a[1]) / (b[1] - a[1]) + a[0]) inside = !inside;
26540
- const vx = b[0] - a[0], vy = b[1] - a[1], wx = x - a[0], wy = y - a[1], t = clamp2((wx * vx + wy * vy) / (vx * vx + vy * vy), 0, 1);
28853
+ const vx = b[0] - a[0], vy = b[1] - a[1], wx = x - a[0], wy = y - a[1], t = clamp3((wx * vx + wy * vy) / (vx * vx + vy * vy), 0, 1);
26541
28854
  d = Math.min(d, Math.hypot(wx - vx * t, wy - vy * t));
26542
28855
  }
26543
28856
  return inside ? -d : d;
@@ -27583,7 +29896,7 @@ var GameUrban = (() => {
27583
29896
  c.roundBox(2.4, 0.1, 2.5, 0.04, blue, hx, hy0 + hh + 0.05, 0);
27584
29897
  const door = c.roundBox(0.05, 1.5, 0.66, 0.02, CM("#5a3e26"), hx - 1.3, hy0 + 0.8, 0.68, { rot: [0, 1.3, 0] });
27585
29898
  void door;
27586
- const inner = CM("#b9a888", { roughness: 0.9 });
29899
+ const inner2 = CM("#b9a888", { roughness: 0.9 });
27587
29900
  c.roundBox(0.4, 0.95, 1.9, 0.03, wood, hx + 0.75, hy0 + 0.48, 0);
27588
29901
  c.roundBox(0.42, 0.03, 1.9, 0.01, CM("#2a2b2c"), hx + 0.75, hy0 + 0.97, 0);
27589
29902
  [-0.45, 0.45].forEach(function(z) {
@@ -27602,10 +29915,10 @@ var GameUrban = (() => {
27602
29915
  c.rod(new V3(0, 0, 0), new V3(0, Math.cos(a8) * 0.36, Math.sin(a8) * 0.36), 0.014, MT.wood, { parent: whl });
27603
29916
  }
27604
29917
  c.cylinder(0.05, 0.05, 0.14, MT.wood, 0.06, 0, 0, "x", { parent: whl });
27605
- kit.seat(c, hx - 0.05, hy0, 0, { w: 0.5, h: 0.55, back: 0.45, mat: inner, headrest: false });
29918
+ kit.seat(c, hx - 0.05, hy0, 0, { w: 0.5, h: 0.55, back: 0.45, mat: inner2, headrest: false });
27606
29919
  c.roundBox(1.8, 0.12, 0.65, 0.04, CM("#7c5a3b"), hx - 0.2, hy0 + 0.5, -0.72);
27607
29920
  c.roundBox(1.7, 0.08, 0.55, 0.04, CM("#6b7c8f", { roughness: 1 }), hx - 0.2, hy0 + 0.6, -0.72);
27608
- c.roundBox(1.9, 0.02, 2.1, 0.01, inner, hx, hy0 + hh - 0.06, 0).castShadow = false;
29921
+ c.roundBox(1.9, 0.02, 2.1, 0.01, inner2, hx, hy0 + hh - 0.06, 0).castShadow = false;
27609
29922
  c.cylinder(0.1, 0.1, 1.2, MT.trim, hx - 0.75, hy0 + hh + 0.6, 0.65);
27610
29923
  c.cylinder(0.13, 0.1, 0.1, MT.trim, hx - 0.75, hy0 + hh + 1.22, 0.65);
27611
29924
  c.rod(new V3(hx + 0.4, hy0 + hh + 0.1, 0), new V3(hx + 0.4, hy0 + hh + 1.6, 0), 0.04, wood);
@@ -31797,7 +34110,7 @@ var GameUrban = (() => {
31797
34110
  return (request, b) => {
31798
34111
  const context = contexts[request.kind];
31799
34112
  const key = `${seed}:${request.kind}:${request.x}:${request.z}`;
31800
- const id = options.pick ? options.pick(request, context, key) : pickForContext(region, context, key);
34113
+ const id = options.pick ? options.pick(request, context, key) : (options.pickSpecies ?? ((table, spot) => pickForContext(region, table, spot)))(context, key);
31801
34114
  b.place("plant", id, request.x, request.z, { scale: request.scale, seed: key });
31802
34115
  };
31803
34116
  }