import type { BuildingPaletteOverrides, BuildingProbabilities, BuildingStyle } from "./buildings.js"; import type { BuildingKit } from "./buildingKit.js"; import type { AvoidZone } from "./geometry.js"; import type { TerrainPathProfile } from "./pathTerrain.js"; import type { PlaceWorldFeature } from "./place.js"; import type { TerraformSnapshot } from "./terraform.js"; import type { SpriteAtlas } from "../assets/spriteAtlas.js"; import type { VolumetricCloudsConfig } from "./volumetricClouds.js"; export interface WorldBounds { w: number; d: number; } export type TerrainMaterial = "grass" | "sand" | "snow" | "rock" | "ash" | "highland" | "slate"; export type EnvironmentVec2 = readonly [number, number]; export interface EnvironmentArea extends WorldBounds { h?: number; /** World-space center of the area; default `[0, 0]` (the world origin). Sites a weather/vegetation band away from the origin, like `building()`/`ocean()` `position`. */ position?: EnvironmentVec2; } export interface TerrainColors { low?: string; high?: string; waterline?: string; } /** * Procedural detail-surface layer for terrain: a noise-driven shader that keeps the * biome-tinted base ground (from `colors`/`biomeBands`) and blends distinct rock, * sand, and snow over it by slope, height, and waterline — turning a flat * vertex-colour surface into varied, textured-reading ground with no image assets. */ export interface TerrainDetailConfig { /** Steep-slope rock colour. Default "#6f7175". */ rockColor?: string; /** Waterline/beach sand colour. Default "#c2b283". */ sandColor?: string; /** High-altitude snow colour. Default "#eef3f7". */ snowColor?: string; /** Slope (0 flat … 1 vertical) at which rock starts taking over. Default 0.42. */ rockSlopeStart?: number; /** World height above which snow appears. Default 26. */ snowHeight?: number; /** Water level for the sand band; defaults to the terrain's `waterLevel`, and no band is drawn when the terrain has none. */ waterLevel?: number; /** Fine detail noise wavelength in world units. Default 5. */ detailScale?: number; /** Large-scale colour-variation wavelength in world units. Default 45. */ macroScale?: number; /** Surface roughness of the detail material. Default 0.9. */ roughness?: number; /** Overall detail intensity 0..1. Default 1. */ strength?: number; /** * Per-surface ground response 0..1 for painted layers: grass, dirt, gravel, rock, sand, mud, snow * and road each draw their own procedural albedo, roughness and micro-normal, meet along * noise-broken edges, and slopes turn to rock. Default 1; 0 keeps flat painted colour. */ surfaceStrength?: number; /** * RGB multipliers for the two macro colour sweeps painted over the base ground. `dry` tints the * sun-bleached patches, `wet` the cooler pockets between them. The defaults ([1.14, 1.05, 0.76] * and [0.78, 1.0, 0.86]) suit temperate meadows; the green-leaning `wet` default paints mossy * pockets onto any biome that keeps it, so arid/ashen worlds should supply warm or neutral tints. */ sweeps?: { dry?: readonly [number, number, number]; wet?: readonly [number, number, number]; }; /** Real PBR texture maps blended over the procedural detail — the seam for `@jgengine/assets` `buildMaterialCatalog` output. Omit to keep the procedural-only look. */ material?: TerrainDetailMaterialConfig; } /** * PBR map URLs for a real ground texture — the same shape * `libraryTerrainMaterial(name).maps` from `@jgengine/assets` returns. Kept dependency-free here so * `core` never imports the assets package; any URLs (pulled maps, a CDN, a data URI) satisfy it. * Omitted roughness keeps the terrain scalar; omitted AO skips occlusion sampling. */ export interface TerrainMaterialMaps { color: string; normal: string; roughness?: string; ao?: string; /** * Height map for callers that displace geometry themselves. The terrain detail shader never * samples it, so pass the URL only when the pulled set actually ships one — never invent it. */ displacement?: string; } /** * Real PBR texture applied over the ground surface — the seam that lets a game put a * `buildMaterialCatalog` material on terrain. Blends with, never replaces, the procedural detail * shader: color/roughness/ao tile the maps by world position, `strength` fades them over the * existing vertex-colour + noise look. */ export interface TerrainDetailMaterialConfig { /** e.g. `buildMaterialCatalog({ basePath: "/materials" }).resolve("material/grass")!.maps`. */ maps: TerrainMaterialMaps; /** World units per texture tile; smaller tiles the texture more densely. Default 4. */ repeat?: number; /** Blend strength of the texture over the procedural/vertex-colour base, 0..1. Default 1. */ strength?: number; /** * How much of the map's OWN colour replaces the terrain's authored colour, 0..1. Default 1 — * at which the map's albedo wins and the biome tint, macro sweeps, and slope-rock blend under it * are painted over, so a whole region flattens to the texture's single hue. Drop toward 0 to keep * the authored colour and take only the map's grain (its light and shade), which is what a * hand-tinted world wants from a stock PBR set. */ tint?: number; /** * What a failed map load does. Scanned maps are pulled by a game's asset step, not committed, so a * checkout without them looks exactly like a broken one. `"procedural"` (default) keeps the * authored procedural ground and logs one warning naming the map; `"fail"` hands the error to the * presentation retry screen, which is what a view that cannot render without the scan asks for. */ missing?: "procedural" | "fail"; } export interface TerrainFlattenMask { center: EnvironmentVec2; radius: number; /** Target flat height; defaults to the noise field's height at `center`. */ height?: number; /** Extra blend-ring width outside `radius` where the flat height smoothsteps back to the noise value; default `radius * 0.5`. */ falloff?: number; } /** Palette and blend fields shared by every `TerrainMaterialRegion` shape. */ export interface TerrainRegionStyle { /** Named palette preset for this region (see `TERRAIN_MATERIAL_PALETTES` in `world/terrain`); overridden field-by-field by `colors`. */ material?: TerrainMaterial; colors?: TerrainColors; /** Blend-ring width outside the region core back to the surrounding palette; default: half the region's characteristic size. */ falloff?: number; } /** A circular palette zone painted over the base terrain palette — snow caps, ash wastes, spawn circles. */ export interface TerrainCircleRegion extends TerrainRegionStyle { shape?: "circle"; center: EnvironmentVec2; radius: number; } /** A ribbon palette zone following a centerline — roads and rivers, instead of chaining overlapping circles. */ export interface TerrainPolylineRegion extends TerrainRegionStyle { shape: "polyline"; /** Centerline vertices in world XZ; needs at least two points. */ points: readonly EnvironmentVec2[]; /** Full ribbon width; the painted core extends `width / 2` either side of the centerline. `falloff` defaults to `width * 0.5`. */ width: number; } /** A rectangular palette zone, optionally rotated about the world y axis — plazas, fields, districts. */ export interface TerrainRectRegion extends TerrainRegionStyle { shape: "rect"; center: EnvironmentVec2; /** Half-extents along the region's local x and z before rotation. `falloff` defaults to half the smaller extent. */ halfExtents: EnvironmentVec2; /** Rotation about the world y axis in radians; default 0. */ rotationY?: number; } /** * A palette zone painted over the base terrain palette. Circle (the default when no `shape` is given), * `polyline` ribbons for roads/rivers, and rotatable `rect` districts all paint fully inside their core * and blend back across `falloff`; later regions in the list win overlaps. */ export type TerrainMaterialRegion = TerrainCircleRegion | TerrainPolylineRegion | TerrainRectRegion; /** Per-band fog override cross-faded along z by `createBiomeFogSampler`; unset fields fall through to the base sky fog. */ export interface BiomeFog { color?: string; near?: number; far?: number; density?: number; } /** Per-band sky/light override cross-faded along z by `createBiomeSkySampler`; unset fields fall through to the base sky. */ export interface BiomeSky { horizonColor?: string; zenithColor?: string; sunIntensity?: number; ambientIntensity?: number; } /** * A z-ordered ground palette zone — the linear-boundary counterpart to the radial `materialRegions`. * Adjacent bands cross-fade into each other across a `fade`-wide window centered on the midpoint z * between their centers, so a multi-biome world (vale → marsh → peaks along z) blends its ground * color instead of hard-switching. Bands may also carry per-zone `fog`, `sky`, and `weather`. Order * the list by ascending `z`. */ export interface BiomeBand { /** World-space center z of the zone. */ z: number; /** Cross-fade window width straddling the boundary to the next band. Default 64. */ fade?: number; /** Named palette preset for the zone (see `TERRAIN_MATERIAL_PALETTES`); overridden field-by-field by `colors`. */ material?: TerrainMaterial; /** Explicit low/high/waterline hex colors for the zone. */ colors?: TerrainColors; /** Fog color/near/far/density for the zone, cross-faded per camera z; unset fields keep the base sky fog. */ fog?: BiomeFog; /** Sky horizon/zenith colors and sun/ambient intensity for the zone, cross-faded per camera z; unset fields keep the base sky. */ sky?: BiomeSky; /** Precipitation covering the zone — expands into a full-width weather strip centered at `[0, z]`, `fade`-deep. */ weather?: "rain" | "snow"; } export interface TerrainEnvironmentConfig { bounds?: WorldBounds; height?: number; heightMap?: string; /** * Game-supplied height function replacing the built-in noise field — banded biomes, ridge walls, * terracing, anything expressible as `(x, z) => y`. `flatten` masks still carve into it, and both * the rendered ground and `ctx.world.ground` sample it. A function doesn't serialize; keep it pure. */ heightField?: (x: number, z: number) => number; /** Named palette preset (see `TERRAIN_MATERIAL_PALETTES` in `world/terrain`); default "grass". Overridden field-by-field by `colors`. */ material?: TerrainMaterial; /** Explicit low/high/waterline hex colors; any field left unset falls back to the resolved `material` preset. */ colors?: TerrainColors; /** Palette zones blended over the base `material`/`colors` for multi-biome readability. */ materialRegions?: readonly TerrainMaterialRegion[]; /** Ordered z-banded ground palettes that cross-fade along the world's z axis (vale → marsh → peaks). Painted under `materialRegions`. */ biomeBands?: readonly BiomeBand[]; segments?: number; seed?: string; frequency?: number; octaves?: number; ridged?: boolean; baseHeight?: number; waterLevel?: number; /** Flat pads carved into the noise field, e.g. for building pads or spawn circles. */ flatten?: readonly TerrainFlattenMask[]; /** Authored path profiles that flatten/grade/carve the heightfield along a centerline (roads, rivers, ramps) with optional retaining walls. Applied after `flatten`. */ pathProfiles?: readonly TerrainPathProfile[]; /** * Procedural detail surface (per-surface painted ground, slope rock, sand and snow over the biome * base). Omitted, the ground still renders with default detail; `false` opts out to the flat * vertex-colour ground. */ detail?: TerrainDetailConfig | false; } export interface RainEnvironmentConfig { /** * `w`/`d`/`h` size a wrap-volume of raindrops **anchored to and re-centered on the camera every * frame** — not a world-space region. `area.position` (and the `position` sugar below) has **no * visual effect** on rain/snow: the shell's `RainField`/`SnowField` always run `followCamera` and * never apply the mesh's transform, only `camera.position`. Keep `w`/`d`/`h` near camera scale * (roughly 50-120 per axis) so drops stay a visible size at normal view distance — sizing the * volume to a world region (e.g. matching a district's footprint) spreads the same drop count * across that much more space until they're sub-pixel and the storm reads as empty sky, even * though `summarizeEnvironment` shows the layer exists. Reach for `density`, `width`, and `opacity` * to make a storm read heavier/taller instead of enlarging `area`. */ area?: EnvironmentArea; /** World-space center `[x, z]` of the band, matching `building()`/`ocean()` — sugar for `area.position`. No visual effect on rain/snow (see `area`); kept for API symmetry with other weather-adjacent features. */ position?: EnvironmentVec2; density?: number; speed?: number; dropLength?: number; wind?: EnvironmentVec2; color?: string; /** Streak width of each raindrop quad, in world units. Default 0.018 — raise for a heavier-reading storm without enlarging `area`. */ width?: number; /** Drop opacity, 0-1. Default 0.48 — raise for a denser-reading storm without enlarging `area`. */ opacity?: number; } export interface SnowEnvironmentConfig { /** * `w`/`d`/`h` size a wrap-volume of snowflakes **anchored to and re-centered on the camera every * frame** — not a world-space region. `area.position` (and the `position` sugar below) has **no * visual effect** on rain/snow: the shell's `RainField`/`SnowField` always run `followCamera` and * never apply the mesh's transform, only `camera.position`. Keep `w`/`d`/`h` near camera scale * (roughly 50-120 per axis) so flakes stay a visible size at normal view distance — sizing the * volume to a world region (e.g. matching a district's footprint) spreads the same flake count * across that much more space until they're sub-pixel and the storm reads as empty sky, even * though `summarizeEnvironment` shows the layer exists. Reach for `density`, `flakeSize`, and * `opacity` to make a storm read heavier instead of enlarging `area`. */ area?: EnvironmentArea; /** World-space center `[x, z]` of the band, matching `building()`/`ocean()` — sugar for `area.position`. No visual effect on rain/snow (see `area`); kept for API symmetry with other weather-adjacent features. */ position?: EnvironmentVec2; density?: number; speed?: number; flakeSize?: number; drift?: number; wind?: EnvironmentVec2; color?: string; /** Flake opacity, 0-1. Default 0.86 — raise for a denser-reading storm without enlarging `area`. */ opacity?: number; } export interface GrassEnvironmentConfig { area?: EnvironmentArea; /** World-space center `[x, z]` of the band, matching `building()`/`ocean()` — sugar for `area.position`, letting a biome zone site its own vegetation away from the origin. */ position?: EnvironmentVec2; density?: number; bladeHeight?: readonly [number, number]; bladeWidth?: number; windStrength?: number; colors?: readonly string[]; seed?: string; /** Blade curl range (arc amount, min..max). Shell default [0.12, 0.42] — raise for wind-combed scrub, lower for stiff turf. */ bladeBend?: readonly [number, number]; /** Tuft spread radius in meters — how far a clump's blades splay from its root. Shell default 0.16; wider reads as bushy clumps rather than lone blades. */ tuftRadius?: number; /** Per-tuft brightness jitter 0..1 (shell default 0.28) — higher breaks up the field so it never reads as one repeated blade. */ colorVariation?: number; /** 0..1 blend of blade normals toward straight-up (shell default 0.6). Low values light each blade plane individually and go dark when side-lit; high values light the field like a soft carpet. */ normalLift?: number; /** 0..1 strength of the turf ground-cover mat painted under the blades (shell default 0.9); 0 leaves the ground bare. */ groundCover?: number; } export interface OceanEnvironmentConfig { bounds?: WorldBounds; position?: EnvironmentVec2; level?: number; /** * Scheduled water level as a function of game-time seconds — tides, floods, draining chambers. * Wins over `level` (which stays the initial/static value); `waterSurfaceFromDescriptor` bakes it * into `WaterSurface.height`, and the shell repositions the rendered ocean from it every frame. * A function doesn't serialize; keep it pure and deterministic (compose with `time/stateSchedule`). */ levelAt?: (time: number) => number; waveHeight?: number; /** Primary Gerstner wavelength in world units (shared with shell Ocean `waveScale`; default 18). */ waveScale?: number; waveSpeed?: number; color?: string; } /** * Where the sun sits, as a compass bearing and a height above the horizon, both in degrees. * `azimuth` 0 points toward -Z (north), 90 toward +X (east); `elevation` 90 is straight overhead. */ export interface SkySunConfig { /** Compass bearing of the sun in degrees; 0 = -Z, 90 = +X. Default 0. */ azimuth?: number; /** Height above the horizon in degrees; 0 sits on the horizon, 90 is overhead. Default 68. */ elevation?: number; } export interface SkyEnvironmentConfig { /** Fixed look used when `timeOfDay` is off (or no clock is available); default "day". */ preset?: "day" | "dusk" | "night"; /** Drive sun/sky from the world clock's `calendar().dayFraction` instead of the fixed `preset`. */ timeOfDay?: boolean; /** * Horizon tint at noon. Under `timeOfDay` this only sets the noon keyframe; dawn, dusk, and night * still crossfade through the engine presets, so an authored tint is visible for part of the day. */ horizonColor?: string; /** Zenith tint at noon; same `timeOfDay` crossfade caveat as `horizonColor`. */ zenithColor?: string; sunIntensity?: number; ambientIntensity?: number; /** Authored noon sun colour; the atmosphere reddens it toward the horizon. Default warm white. */ sunColor?: string; /** Authored noon fog colour. Dawn, dusk and night cross-fade to their own haze so fog matches the sky. */ fogColor?: string; /** Peak moonlight as a fraction of `sunIntensity`. Default the engine's readable night value. */ moonIntensity?: number; /** Moonlight colour. Default cool moonlight blue. */ moonColor?: string; /** * Sun placement for the dome glow and the sky-owned sun light. With a fixed `preset` this is the * sun's exact direction; under `timeOfDay` it is where the sun stands at noon and the day arc * swings around that bearing. Unset with an authored `lighting.directional` light, the shell takes * the first directional light's direction so the dome sun agrees with the shadows. */ sun?: SkySunConfig; /** Sky-dome sphere radius in world units. Enlarge for a playfield sited far from the origin so the viewer never exits the dome. Must stay below the camera far plane or the dome depth-clips to black. Default 260. */ radius?: number; /** Horizon haze-band strength: 0 removes the dusty band, ~1 makes it heavy. Default 0.5. */ hazeStrength?: number; /** Sun-glow brightness multiplier around the sun disc. Default 1. */ sunGlowStrength?: number; /** * How fast the gradient reaches `zenithColor` off the horizon. Below 1 pulls the zenith hue down * into the frame — the difference between a sky an eye-level camera actually sees and a horizon * wash; above 1 keeps colour banked overhead. Default 0.65. */ gradientExponent?: number; /** Procedural cloud-band opacity over the gradient: 0 is a clear sky, 1 is heavy overcast. Default 0.35. */ cloudiness?: number; fog?: { color?: string; near?: number; far?: number; }; /** Raymarched volumetric cloud layer over the sky dome. Off by default — omit for no clouds. */ volumetricClouds?: VolumetricCloudsConfig; } /** One road a `building({ along })` frontage lines with buildings: centerline + full width. */ export interface BuildingFrontageRoad { /** Road centerline vertices in world XZ; at least two points. */ path: readonly EnvironmentVec2[]; /** Road full width in world units; lots set back from `width / 2`. */ width: number; } /** * 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`. */ export interface BuildingFrontageConfig { /** Roads whose frontage is lined with lots. */ roads: readonly BuildingFrontageRoad[]; /** Sidewalk strip between the curb (`width / 2`) and the building front, world units. Default 3. */ setback?: number; /** Place lots on both sides of each road. Default true. */ bothSides?: boolean; /** Clip region; lots whose center falls outside are dropped. Omit to keep every lot. */ area?: { center?: EnvironmentVec2; halfExtents: EnvironmentVec2; }; /** Hard cap on emitted lots so a long network stays bounded. Default 400. */ maxLots?: number; } export interface BuildingEnvironmentConfig { count?: number; position?: EnvironmentVec2; footprint?: WorldBounds; stories?: readonly [number, number]; storyHeight?: number; spacing?: number; /** Named palette archetype (see `BUILDING_STYLE_PALETTES` in `world/buildings`); default "generic". Overridden part-by-part by `palette`. */ style?: BuildingStyle; /** Explicit per-part-kind hex colors; any part left unset falls back to the resolved `style` palette. */ palette?: BuildingPaletteOverrides; /** Facade part probabilities (windows, shopfronts, awnings, …); unset parts follow the `style`. */ probabilities?: Partial; seed?: string; /** * Street-aware placement: derive lots along the given roads' frontage (facing each street, set * back by a sidewalk strip) instead of the grid around `position`. See {@link BuildingFrontageConfig}. */ along?: BuildingFrontageConfig; /** * Binds facade part slots to real models (see `world/buildingKit`). Unbound kinds keep the * untextured block; without a kit the whole facade stays blocks, as before. */ kit?: BuildingKit; /** * Whether the buildings block movement and physics through `ctx.world.solids` (default `true`). * `false` keeps them render-only, e.g. a distant skyline nobody can reach. */ solid?: boolean; } export type PadSize = readonly [number, number] | { radius: number; }; export interface PadEnvironmentConfig { center: EnvironmentVec2; size: PadSize; /** Surface offset above the terrain height at `center`; default `0.05` — just proud of the ground. Ignored when `elevation` is set. */ height?: number; /** Absolute world-space Y for the pad surface — a landing pad on a rooftop, a floating platform. Skips the implicit terrain flatten (there's no ground to flatten toward). */ elevation?: number; color?: string; /** Rotation applied to rectangular pads; ignored for circular pads. */ rotationY?: number; } export type TerrainEnvironmentDescriptor = { kind: "terrain"; } & Required> & Omit; /** A bounded terrain patch floating at its own altitude — sky islands, arena platforms, split landmasses with void between. */ export interface TerrainIslandConfig extends TerrainEnvironmentConfig { /** World-space center of the island; its `bounds` rect surrounds this point. */ origin: EnvironmentVec2; } export type TerrainIslandDescriptor = Omit & { kind: "island"; origin: EnvironmentVec2; }; export type RainEnvironmentDescriptor = { kind: "rain"; } & Required>; export type SnowEnvironmentDescriptor = { kind: "snow"; } & Required>; export type GrassEnvironmentDescriptor = { kind: "grass"; } & Required> & Pick; export type OceanEnvironmentDescriptor = { kind: "ocean"; } & Required> & Pick; export type BuildingEnvironmentDescriptor = { kind: "building"; } & Required> & Pick; export type PadEnvironmentDescriptor = { kind: "pad"; } & Required> & Pick; export type SkyEnvironmentDescriptor = { kind: "sky"; } & Required> & Omit; /** Config for {@link road}: a flat asphalt ribbon draped over the terrain along a centerline. */ export interface RoadEnvironmentConfig { /** Centerline vertices in world XZ; at least two points. */ path: readonly (readonly [number, number])[]; /** Ribbon width in world units. Default 8. */ width?: number; /** Asphalt color. Default "#3b3e47". */ color?: string; /** Paint a dashed centerline. Default true. */ markings?: boolean; /** Centerline dash color. Default "#e8c74a". */ markingColor?: string; /** Lift above the terrain to avoid z-fighting; stagger overlapping roads. Default 0.08. */ elevation?: number; /** Sidewalk bands on both edges; `false` for none. Default `{ width: 2.6, color: "#a7adb8" }`. */ sidewalk?: { width?: number; color?: string; } | false; /** * Procedural surface the carriageway shades with, named like terrain paint (`"asphalt"`, * `"gravel"`, `"dirt"`). `false` keeps flat colour. Default `"road"` (asphalt grain and tyre wear). */ surface?: string | false; } /** Resolved road descriptor produced by {@link road} and rendered by the shell environment scene. */ export type RoadEnvironmentDescriptor = { kind: "road"; } & Required> & { /** Resolved sidewalk band, or `false` when the road has none. */ sidewalk: { width: number; color: string; } | false; /** Procedural carriageway surface; absent means `"road"`, `false` flat colour. */ surface?: string | false; }; export type WeatherEnvironmentDescriptor = RainEnvironmentDescriptor | SnowEnvironmentDescriptor; export type VegetationEnvironmentDescriptor = GrassEnvironmentDescriptor; export type WaterEnvironmentDescriptor = OceanEnvironmentDescriptor; export type StructureEnvironmentDescriptor = BuildingEnvironmentDescriptor; export type EnvironmentDescriptorList = T | readonly T[]; export interface EnvironmentWorldConfig { terrain?: TerrainEnvironmentDescriptor; /** Disconnected landmasses at independent altitudes over the base `terrain` (or over void without one) — see `island()`. */ islands?: readonly TerrainIslandDescriptor[]; sky?: SkyEnvironmentDescriptor; weather?: EnvironmentDescriptorList; vegetation?: EnvironmentDescriptorList; water?: EnvironmentDescriptorList; structures?: EnvironmentDescriptorList; /** Road ribbons draped over the terrain — see `road()`. */ roads?: EnvironmentDescriptorList; /** Ground pads, e.g. platforms or paved patches; each implicitly flattens the terrain beneath it. */ pads?: readonly PadEnvironmentDescriptor[]; /** * An authored editor {@link TerraformSnapshot} whose offsets layer over the base terrain — the * editor-to-runtime ground seam. The same snapshot in a game's `editorLayers.terrain` renders and * collides identically at runtime. */ sculpt?: TerraformSnapshot; /** * Clearance discs flattened into the ground (spawns, plots, paths) — the terrain half of a * clearance zone whose foliage half is scatter's `avoid`. Derive them from a document with * `clearanceZonesFrom`. */ clearings?: readonly AvoidZone[]; } export interface EnvironmentWorldFeature { kind: "environment"; terrain?: TerrainEnvironmentDescriptor; islands?: readonly TerrainIslandDescriptor[]; sky?: SkyEnvironmentDescriptor; weather?: readonly WeatherEnvironmentDescriptor[]; vegetation?: readonly VegetationEnvironmentDescriptor[]; water?: readonly WaterEnvironmentDescriptor[]; structures?: readonly StructureEnvironmentDescriptor[]; roads?: readonly RoadEnvironmentDescriptor[]; pads?: readonly PadEnvironmentDescriptor[]; /** Authored sculpt snapshot layered over the base terrain — see {@link EnvironmentWorldConfig.sculpt}. */ sculpt?: TerraformSnapshot; /** Clearance discs flattened into the ground — see {@link EnvironmentWorldConfig.clearings}. */ clearings?: readonly AvoidZone[]; } export interface WorldGridCell { x: number; z: number; /** Extruded box height for this cell; falls back to the config's `baseHeight`, then `1`. */ height?: number; /** Per-cell tint; falls back to the config's `defaultColor`. */ color?: string; } /** Shared by `biomes()`/`voxel()`/`plots()`/`tilemap()` so the shell can render their declared content as instanced boxes without a hand-written renderer. */ export interface WorldGridConfig { cells?: readonly WorldGridCell[]; /** World-unit size of one grid cell. Default 1. */ cellSize?: number; /** Height used for cells that omit their own `height`. Default 1. */ baseHeight?: number; /** Color used for cells that omit their own `color`. */ defaultColor?: string; } export interface BiomesWorldConfig extends WorldGridConfig { map: string; zones: string; bounds?: WorldBounds; } export interface VoxelWorldConfig extends WorldGridConfig { seed: string; generate?: string; streaming?: { radius: number; }; } export interface PlotsWorldConfig extends WorldGridConfig { city?: string; interiors?: string; } export interface TilemapWorldConfig extends WorldGridConfig { map: string; /** Atlas-backed tile rendering; cell glyphs map to atlas frame ids. */ tileSet?: { atlas: SpriteAtlas; tiles: Record; }; /** Render ordering key consumed by the shell tile renderer. */ sortingLayer?: string; } /** * A declared world shape passed to `defineGame`. The preferred model is the place feature from * `world()` (`@jgengine/core/world/place`): substrate + laws, with all dressing authored in the * editor. The remaining members — biomes, voxel grid, plots, tilemap, environment, flat — are the * legacy code-declared shapes kept for existing games. */ export type WorldFeature = PlaceWorldFeature | ({ kind: "biomes"; } & BiomesWorldConfig) | ({ kind: "voxel"; } & VoxelWorldConfig) | ({ kind: "plots"; } & PlotsWorldConfig) | ({ kind: "tilemap"; } & TilemapWorldConfig) | EnvironmentWorldFeature | { kind: "flat"; }; /** Derives implicit `TerrainFlattenMask`s carving each pad's footprint into the terrain beneath it. Elevated pads (absolute `elevation`) float free and carve nothing. */ export declare function padFlattenMasks(pads: readonly PadEnvironmentDescriptor[]): readonly TerrainFlattenMask[]; /** Composes an `environment()` feature from terrain, sky, weather, vegetation, water, structures, roads, and pads. * * Legacy dressing-in-code path — not how a world is defined. A world is the place you play in * (`world()` from `world/place`: ground + laws); sky look, foliage scatter, props, and sculpt are * scene content authored in the editor. Reach for `environment()` only when consuming * editor/preset-written scene data (`environmentContentFromDocument`) or maintaining an existing * game; never scaffold new worlds from it. * * @capability world-environment compose editor/preset-derived outdoor dressing — terrain, weather, water, structures — into a legacy environment feature */ export declare function environment(config?: EnvironmentWorldConfig): EnvironmentWorldFeature; /** Declares a heightfield terrain patch for `environment()` — bounds, noise, materials, and flatten masks. */ export declare function terrain(config?: TerrainEnvironmentConfig): TerrainEnvironmentDescriptor; export declare function sky(config?: SkyEnvironmentConfig): SkyEnvironmentDescriptor; /** Declares a rainfall weather effect for `environment()` — area, density, speed, wind, and drop width/opacity. */ export declare function rain(config?: RainEnvironmentConfig): RainEnvironmentDescriptor; /** Declares a snowfall weather effect for `environment()` — area, density, drift, wind, and flake opacity. */ export declare function snow(config?: SnowEnvironmentConfig): SnowEnvironmentDescriptor; /** Declares a grass vegetation patch for `environment()` — area, blade sizing, density, and colors. */ export declare function grass(config?: GrassEnvironmentConfig): GrassEnvironmentDescriptor; /** Declares an ocean water body for `environment()` — bounds, level, and wave tuning. */ export declare function ocean(config?: OceanEnvironmentConfig): OceanEnvironmentDescriptor; /** Declare a road ribbon for an `environment()` world; the shell drapes and renders it over the terrain. */ export declare function road(config: RoadEnvironmentConfig): RoadEnvironmentDescriptor; export declare function island(config: TerrainIslandConfig): TerrainIslandDescriptor; /** Declares a cluster of procedurally-massed buildings for `environment()` — count, footprint, stories, style. Pass `along` to line road frontage instead of gridding around `position`. */ export declare function building(config?: BuildingEnvironmentConfig): BuildingEnvironmentDescriptor; export declare function pad(config: PadEnvironmentConfig): PadEnvironmentDescriptor; /** Declares a biome-painted world — the whole-world alternative to a single `environment()` terrain. */ export declare function biomes(config: BiomesWorldConfig): WorldFeature; /** Declares a voxel-grid world for block-based games. */ export declare function voxel(config: VoxelWorldConfig): WorldFeature; /** Declares a subdivided-plots world — farming, base-building, and other parcel-based layouts. */ export declare function plots(config?: PlotsWorldConfig): WorldFeature; /** Declares a 2D tilemap world from a map string. */ export declare function tilemap(config: TilemapWorldConfig): WorldFeature; /** Declares an empty flat world — the minimal `WorldFeature` for games with no terrain of their own. */ export declare function flat(): WorldFeature;