# Environment of your world (world.* kits) The light, the time of day, the weather, the water, the regions and the grass of a game, each in its own kit. Readable offline: `gripforge docs world-environment`, or the MCP tool `gripforge_docs` (topic `world-environment`). The large landscape itself is the subject of `world-terrain`. ## The map | Kit | What it gives the game | | --- | --- | | `world.lighting` | the sun, the sky, the lamps, fog and haze, bloom, ambient occlusion, grading, named looks | | `world.daynight` | a clock (hours, days, dawn / day / dusk / night) and a sun that follows it | | `world.weather` | snow, blizzard, rain, storm, fog; snow cover, wet ground, traction, visibility, cold | | `world.atmosphere` | the look of the world follows a number of the game (sky, sun, grass, weather) | | `world.water` | rivers, lakes, lava and ice with a living surface, current, floating, splashes | | `world.fluids` | patches of mud and blood that take prints, ruts and splashes | | `world.biomes` | regions of one map with their own conditions, habitat, opening rules and survival effects | | `world.grass` | an instanced meadow that sways and keeps off routes and water | | `world.bindings` | ties a number of the game to a live setting of another kit through a curve | Other `world.*` kits are covered elsewhere or by `gripforge kit api `: `world.terrain` (guide `world-terrain`), `world.scenery` (trees, rocks and cliffs dressed on a ground, instanced), `world.elements` (materials and states: fire, wet, frozen), `world.stages` (counters and stages of a changing world), `world.tracks` (prints and ruts), `world.streaming` (chunks around the player), and the structural ones (`world.rooms`, `world.interiors`, `world.portals`, `world.platforms`, `world.arena`, `world.racetrack`, `world.district`, `world.vision`, `world.gravity_zones`, `world.magnetism`, `world.destructibles`, `world.access`). Every kit here requires `game.engine` (role `layer` or `content`), changes nothing until installed, and can be set per scene through the scene's `layers[""]`. ## Turn it on ```text gripforge_gamekit_install { project_id: "gkp_…", id: "world.lighting" } gripforge_gamekit_install { project_id: "gkp_…", id: "world.daynight" } gripforge_gamekit_configure { project_id: "gkp_…", id: "world.daynight", config: { "dayMinutes": 12, "startHour": 7 } } ``` Only the keys that differ from the defaults need to be written. The full reference of a kit is `gripforge kit api `. ## Lighting (world.lighting) One kit for the light of every scene. `preset` is `daylight`, `golden_hour`, `overcast`, `night` or `custom`; `custom` reads the individual look fields. | Key | Meaning | | --- | --- | | `sunColor`, `sunIntensity`, `sunAzimuth`, `sunElevation` | the sun (elevation 1 to 89 degrees, azimuth -180 to 180) | | `skyColor`, `groundColor`, `fillIntensity`, `skyIntensity`, `environmentIntensity`, `exposure` | the ambient light and the exposure | | `shadows`, `shadowMapSize`, `shadowDistance` | the sun's shadows (512 to 4096, 20 to 300 m) | | `maxPointLights`, `maxSpotLights`, `maxShadowLights` | lamp budgets (8, 4; local shadows 0 by default) | | `activationDistance`, `fadeDistance`, `transitionSeconds` | lamps are lit near the player and fade in and out | | `bloomStrength`, `bloomRadius`, `bloomThreshold` | HDR bloom | | `fog` | exponential height fog: `density`, `heightFalloff`, `baseHeight`, `startDistance`, `maxOpacity`, `color`, `followSky` | | `haze` | linear distance haze: `start`, `end`, `opacity`, `color` (not drawn when `fog` has a density) | | `background`, `skyboxSlot`, `skyFill` | plain background colour, a sky panorama binding (`skybox` or `skybox:`), lifted shadows | | `ambientOcclusion`, `radianceProbes`, `renderQuality`, `colorGrading` | optional web effects, each an object; omitted keeps the old rendering | | `antialiasing`, `vignette`, `grain` | `fxaa` or `smaa`, edge darkening, film grain (off on the low graphics preset) | | `look`, `looks` | a named look applied at start (`arid`, `frozen`, `overcast`, `night`, `cinematic`, or your own `{ id, label, values }`) | Lamps are documents of the `world_lights` collection: `{ id, kind: "point" | "spot", position | entity, color, intensity, range, shadows?, direction?, angle?, penumbra? }`. The service (`world.lighting`, exclusive) offers `snapshot()`, `configure(patch)` (a patch that names a look applies it), `looks()`, `setLight`, `removeLight`, `registerLights`, `unregisterLights`. The live setting service `world.lighting.settings` exposes every ranged number of the look (nested ones dotted: `fog.density`), so `world.bindings` can drive them. The `skybox` asset slot takes an equirectangular panorama. The kit emits no event and takes no decision, so it has no hook. A mod may set the look (not the render budgets) and add, patch or remove `world_lights`. ## Day and night (world.daynight) | Key | Default | Meaning | | --- | --- | --- | | `dayMinutes` | 24 | real minutes one game day lasts (24 means one hour a minute) | | `startHour` | 8 | the hour of day 1 at the start | | `sunrise`, `sunset` | 6, 19 | the dawn and dusk hours | | `running` | `true` | the clock runs from the start | | `driveLighting` | `true` | move the sun and the sky through `world.lighting` (its preset becomes `custom`) | | `updateSeconds` | 2 | seconds between two lighting updates | Phases are `dawn`, `day`, `dusk`, `night`. The service `world.clock` has `hour()`, `day()`, `phase()`, `text()`, `set(hour, day?)`, `advance(hours)`, `pause(on?)`, `running()`, `speed(mult?)`, `sky()`; it is saved with the game. Vars: `world.hour`, `world.day`, `world.phase`, `world.time`. Events: `world.phase`, `world.hour`, `world.day`. Script verbs: actions `set_time { hour, day? }`, `wait_hours { hours }`; conditions `phase_is { phase }`, `time_between { from, to }`. A HUD view `clock` is registered in the `hud` slot. Hook `daynight.phase { phase, from, hour, day }`: asked before the clock leaves a phase (not at the start, not after a load). A tap may change `hour` (the clock jumps there, the next day if that hour is not later) or cancel (the clock goes back and stops if time was passing). ```json { "id": "no_night", "hook": "daynight.phase", "match": { "phase": "night" }, "set": { "hour": 5.75 } } ``` ## Weather (world.weather) Presets: `clear`, `snow`, `blizzard`, `rain`, `storm`, `fog`, faded into each other over `transitionSeconds`. | Key | Default | Meaning | | --- | --- | --- | | `preset`, `intensity` | `snow`, 0.7 | weather at start; strength of precipitation, wind and fog | | `cycle`, `cyclePresets`, `cycleMinutes` | `false`, clear / snow / blizzard / fog, 6 | pick a new weather on a timer | | `transitionSeconds` | 20 | the fade between two weathers | | `windDirection`, `gusts` | 30, 0.4 | degrees from +Z towards +X; random gust strength | | `accumulation` | 0.5 | how fast snow covers the ground (it melts afterwards, faster in rain) | | `slippery` | 0.45 | grip lost on full snow or heavy rain: `traction = 1 - slippery x cover` | | `fogVisibility` | `true` | fog and blizzard lower how far enemies see | | `cold`, `coldRate` | `false`, 1 | players chill in snow and blizzard | | `warmthTag`, `warmthRadius` | `warmth`, 6 | entities with this tag (braziers) warm everything within the radius | | `rules` | `[]` | weather on conditions (below) | `rules: [{ when, preset, priority?, minSeconds? }]` are checked four times a second; the highest priority that holds sets its preset and keeps it `minSeconds`; none holding, the cycle or the fixed preset comes back. Rain while a counter is in a band: ```json { "when": { "type": "counter_between", "counter": "pressure", "min": 60, "max": 200 }, "preset": "rain" } ``` Service `world.weather` (exclusive): `snapshot()` (preset, blend, precipitation, wind, fog, overcast, `snowCover`, `wetness`, `visibility`, lightning), `set(preset, seconds?)`, `traction(pos?)`, `exposure(entity)`, `addWarmth`, `removeWarmth`. Vehicle and character kits multiply their grip by `traction`. Events: `weather.changed`, `weather.lightning`, `weather.cold { entity, exposure, level }` (`chilled`, `freezing`, `frozen`, `warm`). Warm zones can also be documents of `warmth_sources`. Optional assets: `snowflake`, `raindrop`, `snow_ground`. The live setting `intensity` is open to `world.bindings`. Hook `weather.change { from, to, seconds, cause }` (`cause`: `set`, `cycle`, `rule`, `scene`): `to` and `seconds` can change, or cancel. ```json { "id": "no_blizzard", "hook": "weather.change", "match": { "to": "blizzard" }, "set": { "to": "snow" } } ``` ## Atmosphere (world.atmosphere) The look follows a number. `driver` is the path of a numeric var (default `counter.habitability`), `phases` a list of looks placed along it, `smoothing` (6 s) how fast the world catches up with a jump. A phase is `{ at, name?, skyColor?, skyAmount?, sunColor?, sunIntensity?, fillIntensity?, exposure?, grass?, weather? }`: colours and light glide through `world.lighting`, `grass` is the `world.grass` coverage, `weather` is a preset set once when the driver enters the phase. The default phases turn a rust-coloured dead world into a blue living one (at 0, 100, 400, 1000). With `world.daynight`, set its `driveLighting` to `false` so only this kit writes the sky. Service: `value()`, `phase()`, `look()`, `sky()`, `set(value | null)`, `phases()`. Event `atmosphere.phase`; vars `atmosphere.phase`, `atmosphere.value`; script action `set_atmosphere { value }` holds the look (a cutscene, a dream). Hook `atmosphere.enter { index, name, from, value, weather, seconds }` can change `weather` and `seconds`; it cannot cancel (hold the look with `set`). ## Water (world.water) Bodies are documents of `water_bodies`: a river `{ id, points: [[x, z]…], width }` (it flows from the first point to the last), a lake `{ id, at, radius }`, or a waterfall `{ id, kind: "waterfall", at, yaw, width, height }`; optional `preset`, `level`, `flow`, `shallow`, `deep`, `foam`, `opacity`. A kit that owns a waterway hands it over through `world.waterways`, so a MOBA river needs no data. | Key | Default | Meaning | | --- | --- | --- | | `preset` | `river` | `river`, `lake`, `swamp`, `lava`, `ice` (look of a body that names none) | | `flow` | 1.2 | current, metres per second | | `opacity`, `foam`, `waves` | 0.82, `true`, 0.07 | transparency, foam on the banks, wave height in metres | | `slow` | 0.2 | share of their speed walkers lose in water | | `splashes` | `true` | whoever moves in water throws splashes | | `floatTag` | `floating` | entities with this tag ride the waves and drift with a river | | `rise` | 0 | metres added to every surface at start; live as the setting `level` | Service `world.water`: `bodies()`, `at(x, z)`, `heightAt(x, z)`, `speedAt(x, z)`, `float(entity, { draft?, drift? })`, `rise()`. Lava hurts through `combat.damage` when present. Events: `water.splash`, `water.entered`, `water.left`, `water.changed { rise }`. Floating is a height and a drift, not buoyancy; nothing sinks. Hook `water.enter { entity, body, preset, slow, damage }`: change `slow` or `damage` for this walker, or cancel (no slow, splash or damage until it leaves the body). ```json { "id": "fireproof", "hook": "water.enter", "match": { "preset": "lava" }, "if": [{ "var": "suit.on", "op": "==", "value": true }], "set": { "damage": 0 } } ``` ## Mud and blood (world.fluids) Patches are documents of `fluid_surfaces`, for example `{ id: "mud-alley", position: [0, 0.015, 12], size: [12, 20], preset: "mud", resolution: 192, depth: 0.13, viscosity: 0.88, recoverySeconds: 180 }` (`mud` or `blood`). Use small patches (8 to 24 m); at most eight, and by default the two nearest simulate within 65 m. Keys: `enabled`, `autoContacts` (feet and four wheels), `splashes`, `simulationRange` (65), `maxActiveSurfaces` (2). Service: `surfaces()`, `active()`, `contact({ x, y, z, yaw, radius, length, pressure, vx, vz })`, `reset()`, `interactions(after)`, `covers(x, y, z)`, `probe(surface, x, z)`, `stats(surface)`. Event `fluid.entered`; hook `fluid.enter { entity, surface, preset, weight }` scales the pressure of an entity's contacts (`weight`) or keeps it out. It is a local height field, not a volumetric fluid; blood is not spawned by combat hits. ## Biomes (world.biomes) A biome is a document of `biomes`: a disc `{ center, radius }` or rectangle `{ rect }` shape, a `habitat` (`land`, `water`, `both`), a `priority` (the highest wins on overlap, then the smallest), `modifiers` (offsets on `world.stages` counters), `opensWhen` (closed until a counter is in range, then open for good) and `needs` (a drain multiplier or supply per second on `survival.needs` gauges). ```json { "id": "north_pole", "name": "North pole", "shape": { "center": [0, -120], "radius": 60 }, "modifiers": { "heat": -30 }, "needs": { "warmth": { "drain": 3 } } } ``` Config: `default` (name, habitat and modifiers of everywhere no biome covers), `announce` (`true`: emit entry and exit). Service: `biomes()`, `at(x, z)`, `of(entity)`, `current()`, `get(id)`, `isOpen(id)`, `local(id | null, counter)`, `habitatAt(x, z)`, `randomPoint(id, rng?)`, `contains(id, x, z)`, `scope(id)` / `scopeId()` (one world at a time with `travel.worlds`). The kit does not wall a closed biome: the game decides. Var `biome.current`; events `biome.entered`, `biome.left`, `biome.opened`; conditions `in_biome`, `biome_open`. Hooks: `biome.enter { biome, name, from }` (name another biome, or cancel so the player counts in none: a sealed suit) and `biome.open { biome, name }` (cancel to keep it closed). ## Grass (world.grass) A deterministic meadow of instanced tufts, kept off the routes, clearings, water and paving that other kits declare. | Key | Default | Meaning | | --- | --- | --- | | `density`, `maxCount` | 2, 40000 | tufts per square metre; ceiling of tufts drawn | | `size`, `seed` | 0, 11 | ground side in metres (0: the extent of the ground or terrain); the random seed | | `clearance`, `bank` | 5.5, 1.5 | metres kept free beside a route / along the water | | `height`, `width` | 0.7, 0.8 | size of a tuft (thickets are half as tall again) | | `color`, `tipColor`, `variation` | `#5f9a3e`, `#a9cc60`, 0.35 | blade colours | | `wind`, `fadeDistance` | 0.8, 140 | sway strength; distance where it fades out | | `thickets` | `true` | taller, denser grass in the stealth zones the world declares | | `coverage` | 1 | share of the meadow shown; live setting `coverage` | | `everywhere` | `false` | on a terrain, grow on any layer, not only grass, meadow and moss | Service: `placements()`, `look()`, `regenerate()`, `coverage(share?)`. Event `grass.changed`; it listens to `scene.entered` and `terrain.edited`. Hook `grass.place { seed, density, maxCount }` can change those before the meadow is placed (not cancellable). ## Bindings (world.bindings) `bindings` is a list of `{ from, to, curve, smoothing?, step?, when? }`: `from` is a numeric var, `to` is `{ kit, key }` or `{ capability, key }` of a live setting, `curve` a list of `[input, output]` points (linear between, flat outside), `smoothing` seconds of exponential glide, `step` the least move worth handing over (so instanced pieces are not rebuilt every tick), `when` a condition. After a load every output is recomputed at once. ```json { "bindings": [ { "from": "counter.habitability", "to": { "kit": "world.water", "key": "level" }, "curve": [[0, -4], [60, 0], [100, 1.5]], "smoothing": 5 }, { "from": "counter.habitability", "to": { "kit": "world.grass", "key": "coverage" }, "curve": [[20, 0], [80, 1]], "step": 0.02 } ] } ``` Settings served today: `world.water` `level`, `world.grass` `coverage`, `world.terrain` `vegetation`, `world.scenery` `coverage` and `coverage.`, `world.lighting` every ranged number, `world.weather` `intensity`. Service: `bindings()`, `set(id, enabled)`, `targets()`, `force(id, input | null)`. The kit has no hook; a mod overrides `bindings` as one whole list. ## Modding All the kits above declare their config keys moddable, and their collections (`water_bodies`, `biomes`, `fluid_surfaces`, `world_lights`, `warmth_sources`): a mod adds, patches or removes documents. Hooks are written as `hooks` documents or with `engine.hooks.tap`; they run on the web and Rust. Without engine content (no archetype, scene, script or story flag) no hook is called and the kit plays exactly as before. ## Engines | Kit | Where it runs | | --- | --- | | `world.lighting`, `world.weather`, `world.fluids` | web, native player, Godot, Unity, Unreal. The weather draws particles, fog, lightning and snow / wet surfaces natively; its rules and hook are web and Rust only. Fog, haze, grading, probes and occlusion are web render options | | `world.daynight`, `world.atmosphere`, `world.water`, `world.biomes`, `world.grass`, `world.bindings` | web and Rust (the native player); no separate Godot, Unity or Unreal delivery | ## When it does not do what you expect | Symptom | Cause and fix | | --- | --- | | The sky ignores `world.daynight` | `driveLighting` is `false`, or `world.atmosphere` writes the sky: only one should | | The lighting preset became `custom` | `world.daynight` drives it; set `driveLighting: false` to keep your own look | | Fog does not show | `fog` needs `enabled: true` and a `density`; `haze` is not drawn when `fog` has a density | | Weather rules do nothing on Godot, Unity or Unreal | rules and the hook are web and Rust only | | Water does not slow anyone | `slow` is 0, or `water.enter` was cancelled for that walker | | Grass grows on roads | the road is not declared by a kit (`lanes.routes`, `world.clearings`): declare it or raise `clearance` |