@jgengine/shell 0.15.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +62 -0
- package/dist/Shell3dPresentation.js +1 -1
- package/dist/environment/EnvironmentScene.js +1 -1
- package/dist/presentationResolve.d.ts +2 -0
- package/dist/presentationResolve.js +1 -0
- package/dist/render/modelLoad.d.ts +24 -0
- package/dist/render/modelLoad.js +46 -0
- package/dist/scene/CityRenderer.js +28 -54
- package/dist/structures/BuildingKitBatch.d.ts +13 -0
- package/dist/structures/BuildingKitBatch.js +78 -0
- package/dist/structures/GeneratedBuilding.d.ts +19 -3
- package/dist/structures/GeneratedBuilding.js +13 -81
- package/dist/structures/buildingKitFit.d.ts +59 -0
- package/dist/structures/buildingKitFit.js +188 -0
- package/dist/structures/buildingKitRegistry.d.ts +9 -0
- package/dist/structures/buildingKitRegistry.js +26 -0
- package/dist/weather/DustField.d.ts +41 -0
- package/dist/weather/DustField.js +129 -0
- package/dist/weather/WeatherLayer.d.ts +8 -2
- package/dist/weather/WeatherLayer.js +5 -2
- package/dist/weather/index.d.ts +1 -0
- package/dist/weather/index.js +1 -0
- package/dist/weather/weatherMath.d.ts +4 -0
- package/dist/weather/weatherMath.js +4 -0
- package/dist/world/WorldHud.d.ts +6 -2
- package/dist/world/WorldHud.js +4 -4
- package/dist/world/worldBarSamples.d.ts +15 -3
- package/dist/world/worldBarSamples.js +34 -3
- package/package.json +4 -4
- package/skills/jgengine/api.md +38 -5
- package/skills/jgengine/capabilities.md +24 -0
- package/skills/jgengine-ui/api.md +33 -12
- package/skills/jgengine-ui/capabilities.md +12 -0
- package/skills/jgengine-world/SKILL.md +5 -0
- package/skills/jgengine-world/api.md +27 -2
- package/skills/jgengine-world/capabilities.md +24 -0
- package/skills/jgengine-world/recipes/building-kit.md +78 -0
|
@@ -587,6 +587,12 @@
|
|
|
587
587
|
- `PoseState` (interface): interface PoseState — ⚠ undocumented
|
|
588
588
|
- `createPoseState` (function): function createPoseState(resolveAllowed: (instanceId: string) => PoseAllowedStates | null | undefined): PoseState — Stance/pose transitions — stand, crouch, prone — that change the hitbox and movement.
|
|
589
589
|
|
|
590
|
+
## @jgengine/core/movement/solidObstacles
|
|
591
|
+
|
|
592
|
+
- `ObstacleReach` (interface): interface ObstacleReach — Largest blocking-collider extent in the scene, split so tower height cannot inflate the XZ query.
|
|
593
|
+
- `resolveWalkerStep` (function): function resolveWalkerStep(ctx: GameContext, position: EntityPosition, stepX: number, stepZ: number, options: { radius?: number; stepUpHeight?: number } = {}): { stepX: number; stepZ: number } — Slide a walker's horizontal step against the same solid geometry that stops the player, returning the X/Z it may actually take this tick. A step into a wall is cut on the blocked axis and preserved on the other, exactly as {@link stepPlayerMovement} resolves the player's.
|
|
594
|
+
- `solidObstaclesNear` (function): function solidObstaclesNear(ctx: GameContext, position: EntityPosition, reachX: number, reachZ: number, height = WALKER_HEIGHT): CollisionObstacle[] — Every blocking physical obstacle overlapping the box `position` ± `reachX`/`reachZ`, read from the scene's resolved colliders.
|
|
595
|
+
|
|
590
596
|
## @jgengine/core/movement/steering
|
|
591
597
|
|
|
592
598
|
- `YawVectorXZ` (type): type YawVectorXZ = readonly [number, number] — Yaw-frame steering math. The engine's heading convention is `forward = (sin yaw, cos yaw)` on the XZ plane with +Y up, which makes a positive yaw increment rotate counterclockwise seen from above — a turn to the entity's LEFT on screen. Every hand-written `heading += steer * rate * dt` therefore turns the wrong way; integrate steering through `steerYaw` instead.
|
|
@@ -1617,7 +1623,7 @@
|
|
|
1617
1623
|
- `BoundsSpec` (type): type BoundsSpec = | { readonly kind: "sphere"; readonly radius: number; readonly offset?: Vec3 } | { readonly kind: "aabb"; readonly half: Vec3; readonly offset?: Vec3 } | { readonly kind: "rect"; readonly halfWidth: number; readonly halfDepth: number; readonly halfHeight?: number; readonly offset?:… — How a renderable declares its extent. AABB, bounding sphere, and 2D rectangle cover the common cases; `point` is the degenerate zero-size default for objects that never override. `offset` shifts the volume from the object origin (e.g. a tall model whose pivot is at its feet).
|
|
1618
1624
|
- `BoxFormationOptions` (interface): interface BoxFormationOptions — Options for {@link boxFormation}.
|
|
1619
1625
|
- `BuildRole` (type): type BuildRole = "owner" | "editor" | "viewer" — ⚠ undocumented
|
|
1620
|
-
- `BuildingEnvironmentDescriptor` (type): type BuildingEnvironmentDescriptor = { kind: "building" } & Required< Pick<BuildingEnvironmentConfig, "count" | "footprint" | "stories" | "storyHeight" | "spacing" | "style"> > & Pick<BuildingEnvironmentConfig, "seed" | "position" | "palette" | "along"> — ⚠ undocumented
|
|
1626
|
+
- `BuildingEnvironmentDescriptor` (type): type BuildingEnvironmentDescriptor = { kind: "building" } & Required< Pick<BuildingEnvironmentConfig, "count" | "footprint" | "stories" | "storyHeight" | "spacing" | "style"> > & Pick<BuildingEnvironmentConfig, "seed" | "position" | "palette" | "along" | "kit"> — ⚠ undocumented
|
|
1621
1627
|
- `BuildingIndex` (interface): interface BuildingIndex — ⚠ undocumented
|
|
1622
1628
|
- `BuildingPaletteOverrides` (type): type BuildingPaletteOverrides = Partial<BuildingPalette> — ⚠ undocumented
|
|
1623
1629
|
- `BuildingStyle` (type): type BuildingStyle = | "generic" | "capital" | "village" | "desert" | "industrial" | "coastal" | "neon" | "ruin" | "frontier" | "aerial" — ⚠ undocumented
|
|
@@ -1837,6 +1843,9 @@
|
|
|
1837
1843
|
- `NoiseFieldConfig` (interface): interface NoiseFieldConfig — Configuration for {@link noiseField}: seed, amplitude, and fractal noise shaping.
|
|
1838
1844
|
- `NoiseVoice` (interface): interface NoiseVoice — A filtered white-noise burst — impacts, whooshes, breath, crackle. Realised from a shared 1s noise buffer at a randomised playback rate and start offset, decaying exponentially to silence at `duration * decay`.
|
|
1839
1845
|
- `NoteEvent` (interface): interface NoteEvent — One scheduled note in a theme, positioned on the loop's quarter-note grid.
|
|
1846
|
+
- `ObjectSlotRejection` (type): type ObjectSlotRejection = | "unknown-object" | "no-slot-inventory" | "unknown-inventory" | "no-space" | "wrong-kind" | "slot-occupied" | "invalid-slot" | "insufficient" — Why an object slot mutation was refused — the instance, its catalog entry, the player inventory, or the declared `accepts`/capacity.
|
|
1847
|
+
- `ObjectSlotResult` (type): type ObjectSlotResult = { status: "ok" } | { status: "rejected"; reason: ObjectSlotRejection } — Outcome of an object slot mutation: applied, or refused with a reason and no state change on either side.
|
|
1848
|
+
- `ObjectSlotTransfer` (interface): interface ObjectSlotTransfer — Which player inventory a transfer moves between, and what it moves.
|
|
1840
1849
|
- `ObjectVisual` (interface): interface ObjectVisual — ⚠ undocumented
|
|
1841
1850
|
- `OceanEnvironmentDescriptor` (type): type OceanEnvironmentDescriptor = { kind: "ocean" } & Required< Pick<OceanEnvironmentConfig, "bounds" | "level" | "waveHeight" | "waveScale" | "waveSpeed" | "color"> > & Pick<OceanEnvironmentConfig, "position" | "levelAt"> — ⚠ undocumented
|
|
1842
1851
|
- `Order` (interface): interface Order<TPayload = unknown> — One issued order. Fully serializable: `kind` is a registry key (not a closure), `payload` is the caller's plain data, and `state` is the kind's own plain progress scratch (e.g. a patrol waypoint index) written during `start`/`update`. Never holds functions, so the whole queue round-trips through `JSON`/`structuredClone`.
|
|
@@ -1951,6 +1960,7 @@
|
|
|
1951
1960
|
- `SceneKindObject` (interface): interface SceneKindObject — The raw document object a resolver receives — shape shared by markers, volumes, and paths.
|
|
1952
1961
|
- `SceneKindResolveContext` (interface): interface SceneKindResolveContext — Ground sampler + options a resolver may read (terrain height/normal snap).
|
|
1953
1962
|
- `SceneObject` (interface): interface SceneObject — ⚠ undocumented
|
|
1963
|
+
- `SceneObjectSlots` (interface): interface SceneObjectSlots — Per-instance container contents for placed objects whose catalog entry declares `slotInventory` — the engine side of "install a GPU into a rack", "put an item in a chest". Contents live on `SceneObject.slots`, so they replicate and save with the object rather than needing a game-owned table keyed by `instanceId`, and every mutation is validated against the declared `accepts` and stack limits.
|
|
1954
1964
|
- `SceneRaycastApi` (interface): interface SceneRaycastApi — ⚠ undocumented
|
|
1955
1965
|
- `SceneRaycastHit` (interface): interface SceneRaycastHit — ⚠ undocumented
|
|
1956
1966
|
- `ScreenEffect` (interface): interface ScreenEffect — A live effect in the current composite: its label, color, region, and the eased opacity `0..1` to draw *right now*. These objects are pooled and reused across {@link ScreenEffectsController.composite} calls — read them, don't retain.
|
|
@@ -2490,6 +2500,18 @@
|
|
|
2490
2500
|
- `BuildingIndex` (interface): interface BuildingIndex — ⚠ undocumented
|
|
2491
2501
|
- `buildingIndex` (function): function buildingIndex(buildings: readonly GeneratedBuilding[]): BuildingIndex — ⚠ undocumented
|
|
2492
2502
|
|
|
2503
|
+
## @jgengine/core/world/buildingKit
|
|
2504
|
+
|
|
2505
|
+
- `BuildingKit` (interface): interface BuildingKit — Binds a building's generated part slots to models. A kit is plain serializable data — scene documents can carry one — and is deliberately unopinionated about art direction: the engine ships the seam, games ship the bindings.
|
|
2506
|
+
- `BuildingKitBinding` (type): type BuildingKitBinding = | { readonly type: "model"; readonly part: BuildingKitPart; readonly fit: BuildingKitFit } | { readonly type: "box" } | { readonly type: "omit" } — What a renderer should do with one generated part.
|
|
2507
|
+
- `BuildingKitFit` (type): type BuildingKitFit = "stretch" | "contain" | "cover" | "native" — How a kit model's native bounds are mapped onto the slot box `BuildingPartPlacement.scale` describes.
|
|
2508
|
+
- `BuildingKitInput` (interface): interface BuildingKitInput — The authoring shape {@link defineBuildingKit} normalizes into a {@link BuildingKit}.
|
|
2509
|
+
- `BuildingKitPart` (interface): interface BuildingKitPart — One bindable variant for a building part slot. `model` is an opaque reference the host resolves (an asset id, a pack-relative path, or a URL) — core never loads anything.
|
|
2510
|
+
- `BuildingKitPartInput` (type): type BuildingKitPartInput = string | BuildingKitPart — A variant as authored: a bare model reference, or the full {@link BuildingKitPart} when it needs fit or transform tuning.
|
|
2511
|
+
- `buildingKitVariantCounts` (function): function buildingKitVariantCounts(kit: BuildingKit): Partial<BuildingVariantCounts> — The variant counts a kit actually supplies, shaped for `BuildingConfigInput.variants`. Feed this into `generateBuilding` so seeded picks spread across exactly the models the kit binds instead of indexing past them and wrapping unevenly.
|
|
2512
|
+
- `defineBuildingKit` (function): function defineBuildingKit(input: BuildingKitInput): BuildingKit — Validates and normalizes a kit description, accepting bare model strings as single-field variants. Throws on empty variant lists so a typo fails at authoring time rather than silently falling back to untextured boxes.
|
|
2513
|
+
- `resolveBuildingKitPart` (function): function resolveBuildingKitPart(kit: BuildingKit | undefined, kind: BuildingPartKind, slot?: BuildingKitSlot): BuildingKitBinding — Resolves one generated part against a kit. Pass the placement's `kind` and its `kit` slot; the slot's `variant` wraps around the bound list, so a kit may carry any number of variants independent of the generator's `variants` counts.
|
|
2514
|
+
|
|
2493
2515
|
## @jgengine/core/world/buildingLots
|
|
2494
2516
|
|
|
2495
2517
|
- `BuildingLotOptions` (interface): interface BuildingLotOptions — Options for {@link deriveBuildingLots}.
|
|
@@ -2523,6 +2545,9 @@
|
|
|
2523
2545
|
- `DEFAULT_BUILDING_STYLE` (const): const DEFAULT_BUILDING_STYLE: BuildingStyle — ⚠ undocumented
|
|
2524
2546
|
- `GeneratedBuilding` (interface): interface GeneratedBuilding — ⚠ undocumented
|
|
2525
2547
|
- `Vec3` (type): type Vec3 = readonly [number, number, number] — ⚠ undocumented
|
|
2548
|
+
- `createBuildingConfig` (function): function createBuildingConfig(input: BuildingConfigInput = {}): BuildingConfig — Fill a partial building description out to a complete {@link BuildingConfig}, clamping counts and probabilities. Use it to inspect or adjust the resolved shape before generating.
|
|
2549
|
+
- `generateBuilding` (function): function generateBuilding(input: BuildingConfigInput = {}): GeneratedBuilding — Generate one building as a flat list of placed facade parts: bays × floors per facade, a ground row, corners, and a roof, with every decorative part rolled from `probabilities` and every part tagged with a `kit` slot the renderer binds to a model (see `world/buildingKit`). Deterministic from `seed`.
|
|
2550
|
+
- `generateBuildingDistrict` (function): function generateBuildingDistrict(config: BuildingGridConfig): GeneratedBuilding[] — Generate a grid of buildings on lots sized by `lotSize` and separated by `streetWidth`, each with its floor count rolled from `floorRange`.
|
|
2526
2551
|
|
|
2527
2552
|
## @jgengine/core/world/carve
|
|
2528
2553
|
|
|
@@ -2732,7 +2757,7 @@
|
|
|
2732
2757
|
- `BiomeSky` (interface): interface BiomeSky — Per-band sky/light override cross-faded along z by `createBiomeSkySampler`; unset fields fall through to the base sky.
|
|
2733
2758
|
- `BiomesWorldConfig` (interface): interface BiomesWorldConfig extends WorldGridConfig — ⚠ undocumented
|
|
2734
2759
|
- `BuildingEnvironmentConfig` (interface): interface BuildingEnvironmentConfig — ⚠ undocumented
|
|
2735
|
-
- `BuildingEnvironmentDescriptor` (type): type BuildingEnvironmentDescriptor = { kind: "building" } & Required< Pick<BuildingEnvironmentConfig, "count" | "footprint" | "stories" | "storyHeight" | "spacing" | "style"> > & Pick<BuildingEnvironmentConfig, "seed" | "position" | "palette" | "along"> — ⚠ undocumented
|
|
2760
|
+
- `BuildingEnvironmentDescriptor` (type): type BuildingEnvironmentDescriptor = { kind: "building" } & Required< Pick<BuildingEnvironmentConfig, "count" | "footprint" | "stories" | "storyHeight" | "spacing" | "style"> > & Pick<BuildingEnvironmentConfig, "seed" | "position" | "palette" | "along" | "kit"> — ⚠ undocumented
|
|
2736
2761
|
- `BuildingFrontageConfig` (interface): interface BuildingFrontageConfig — Street-aware placement for `building()`: instead of a grid scattered around `position`, lots are stepped along each road's frontage, offset by a consistent setback (a curb + sidewalk strip), and turned so every building FRONT faces its road. `footprint` (`w` = frontage width, `d` = depth) and `spacing` (along-road gap) are shared with the grid mode. Deterministic and bounded by `maxLots`.
|
|
2737
2762
|
- `BuildingFrontageRoad` (interface): interface BuildingFrontageRoad — One road a `building({ along })` frontage lines with buildings: centerline + full width.
|
|
2738
2763
|
- `EnvironmentArea` (interface): interface EnvironmentArea extends WorldBounds — ⚠ undocumented
|
|
@@ -122,6 +122,18 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
122
122
|
|
|
123
123
|
- `createBuildSocketCatalog` (function) · `import { createBuildSocketCatalog } from "@jgengine/core/world/buildSockets"`
|
|
124
124
|
|
|
125
|
+
## building-generator — generate seeded buildings as placed facade parts — bays, floors, roof, decor
|
|
126
|
+
|
|
127
|
+
- `createBuildingConfig` (function) · `import { createBuildingConfig } from "@jgengine/core/world/buildings"`
|
|
128
|
+
- `generateBuilding` (function) · `import { generateBuilding } from "@jgengine/core/world/buildings"`
|
|
129
|
+
- `generateBuildingDistrict` (function) · `import { generateBuildingDistrict } from "@jgengine/core/world/buildings"`
|
|
130
|
+
|
|
131
|
+
## building-kit — bind generated facade part slots to real models instead of untextured blocks
|
|
132
|
+
|
|
133
|
+
- `buildingKitVariantCounts` (function) · `import { buildingKitVariantCounts } from "@jgengine/core/world/buildingKit"`
|
|
134
|
+
- `defineBuildingKit` (function) · `import { defineBuildingKit } from "@jgengine/core/world/buildingKit"`
|
|
135
|
+
- `resolveBuildingKitPart` (function) · `import { resolveBuildingKitPart } from "@jgengine/core/world/buildingKit"`
|
|
136
|
+
|
|
125
137
|
## building-lots — derive street-facing building lots from road frontage
|
|
126
138
|
|
|
127
139
|
- `deriveBuildingLots` (function) · `import { deriveBuildingLots } from "@jgengine/core/world/buildingLots"`
|
|
@@ -354,6 +366,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
354
366
|
|
|
355
367
|
- `defineMoveOrder` (function) · `import { defineMoveOrder } from "@jgengine/core/world"`
|
|
356
368
|
|
|
369
|
+
## object-slot-inventory — placed objects hold validated per-instance container contents that replicate and save with the placement
|
|
370
|
+
|
|
371
|
+
- `SceneObjectSlots` (interface) · `import { SceneObjectSlots } from "@jgengine/core/world"`
|
|
372
|
+
|
|
357
373
|
## order-queue — serializable per-entity command queue with lifecycle, preemption, and pluggable order kinds
|
|
358
374
|
|
|
359
375
|
- `createOrderQueue` (function) · `import { createOrderQueue } from "@jgengine/core/world"`
|
|
@@ -480,6 +496,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
480
496
|
|
|
481
497
|
- `SOIL_KIND` (const) · `import { SOIL_KIND } from "@jgengine/core/world"`
|
|
482
498
|
|
|
499
|
+
## solid-obstacles — the one blocking-geometry query the player resolver and NPC movers share
|
|
500
|
+
|
|
501
|
+
- `solidObstaclesNear` (function) · `import { solidObstaclesNear } from "@jgengine/core/movement/solidObstacles"`
|
|
502
|
+
|
|
483
503
|
## spatial-region-box — sample uniformly within an axis-aligned 3D box
|
|
484
504
|
|
|
485
505
|
- `boxRegion` (function) · `import { boxRegion } from "@jgengine/core/world"`
|
|
@@ -577,6 +597,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
577
597
|
|
|
578
598
|
- `VolumetricCloudsConfig` (interface) · `import { VolumetricCloudsConfig } from "@jgengine/core/world"`
|
|
579
599
|
|
|
600
|
+
## walker-solid-step — slide an NPC's step against the geometry that blocks the player
|
|
601
|
+
|
|
602
|
+
- `resolveWalkerStep` (function) · `import { resolveWalkerStep } from "@jgengine/core/movement/solidObstacles"`
|
|
603
|
+
|
|
580
604
|
## wave-runner — observable serializable wave/spawn runner over the seeded spawn director — WAVE N, wave progress, budget/alert readout, onSpawn sink, snapshot/restore
|
|
581
605
|
|
|
582
606
|
- `createWaveRunner` (function) · `import { createWaveRunner } from "@jgengine/core/world"`
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Recipe — dressing generated buildings with a model kit
|
|
2
|
+
|
|
3
|
+
**What this wires:** the facade generator already emits every part slot a
|
|
4
|
+
building needs — walls, windows, storefronts, awnings, roof clutter. A
|
|
5
|
+
`BuildingKit` binds those slots to real GLBs, so the same massing renders as
|
|
6
|
+
untextured blocks, as a clean prefab town, or as a ruin, purely by swapping the
|
|
7
|
+
bound art.
|
|
8
|
+
|
|
9
|
+
## Bind a pack to slots
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { defineBuildingKit } from "@jgengine/core/world/buildingKit";
|
|
13
|
+
|
|
14
|
+
const kit = defineBuildingKit({
|
|
15
|
+
id: "settlement-clean",
|
|
16
|
+
parts: {
|
|
17
|
+
wall: ["/models/pack/Wall_Straight.glb", "/models/pack/Wall_Flat.glb"],
|
|
18
|
+
window: ["/models/pack/Wall_Window.glb"],
|
|
19
|
+
storefront: ["/models/pack/Door_Metal.glb"],
|
|
20
|
+
airConditioner: [{ model: "/models/pack/Vent_Big.glb", fit: "contain" }],
|
|
21
|
+
},
|
|
22
|
+
omit: ["clothesline"],
|
|
23
|
+
});
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
A bare string is shorthand for `{ model }`. `fit` decides how the model meets
|
|
27
|
+
its slot box: `stretch` (default) fills the bay — right for tiling panels;
|
|
28
|
+
`contain` keeps a prop's proportions. `defineBuildingKit` throws on an empty
|
|
29
|
+
variant list, so a typo fails at authoring time instead of rendering nothing.
|
|
30
|
+
|
|
31
|
+
Resolve `model` through your game's asset catalog rather than typing URLs, and a
|
|
32
|
+
missing id fails at boot:
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
const url = assets.resolve("pack/Wall_Straight")?.url;
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Hand it to the world
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { building } from "@jgengine/core/world/features";
|
|
42
|
+
|
|
43
|
+
building({ position: [0, 0], count: 8, style: "desert", kit });
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Kinds the kit leaves unbound keep the engine's untextured block, so a partial
|
|
47
|
+
kit is a valid kit — bind walls and windows first, add roof clutter later.
|
|
48
|
+
Kinds listed in `omit` render nothing at all: use it for parts the setting
|
|
49
|
+
rejects, not for parts you have not got around to.
|
|
50
|
+
|
|
51
|
+
## Two kits, one generator
|
|
52
|
+
|
|
53
|
+
Bind a second kit with broken panels and welded-shut doors, pass it to the
|
|
54
|
+
zones that should read as derelict, and the generator, seeds, and layout stay
|
|
55
|
+
identical. That split is the point of the seam — style is art binding, not a
|
|
56
|
+
second code path.
|
|
57
|
+
|
|
58
|
+
## Spread the seeded picks
|
|
59
|
+
|
|
60
|
+
The generator's `variants` counts decide which slot index each bay asks for. If
|
|
61
|
+
a kit binds a different number of models than the config assumes, picks wrap
|
|
62
|
+
unevenly and one wall model dominates. When you drive the generator directly,
|
|
63
|
+
feed the kit's own counts back in:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import { buildingKitVariantCounts } from "@jgengine/core/world/buildingKit";
|
|
67
|
+
import { generateBuilding } from "@jgengine/core/world/buildings";
|
|
68
|
+
|
|
69
|
+
generateBuilding({ seed: "town-1", variants: buildingKitVariantCounts(kit) });
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Common traps
|
|
73
|
+
|
|
74
|
+
- Do not bind `roof` to a wall-sized plate; the roof slot is one slab spanning
|
|
75
|
+
the whole footprint, not a tiling bay.
|
|
76
|
+
- `contain` for anything that must not distort (vents, crates, signs);
|
|
77
|
+
`stretch` for panels authored to fill a bay.
|
|
78
|
+
- `omit` and "unbound" are different outcomes — unbound still draws a block.
|