# v2: systems and environment Principle 7 in practice: electricity, heat, fire, water, the world clock, weather and the living environment. Index: `agents/world-format-v2.md`. Schemas: `agents/v2-formats.md` section 14 (system manifests). Built: `v2/sim/systems/`, `v2/sim/clock/`, `v2/sim/weather/` (each with its own `CLAUDE.md`, which holds the current tuning numbers). ## Principle 7, as proven Systems never call each other. They only (a) read and write components (`powered`, `temperature`, `wet`, `health`, `fill`), (b) fire and receive I/O events (`OnPowerLost`, `OnIgnite`, `OnBroken`) and (c) query shared fields (water surface, tide, weather, heat, power per area). The Metro doesn't know electricity exists; it has `powered`. Emergence comes from simple rules meeting, never from scripting the moment. First proven in `tools/systems-sandbox/` (2026-09-27: heat, fire, snow, power, metro, weather and damage never import each other; the burning car melts the snow, fire spreads to timber (36/36 tiles) and never to brick (0/96), rain puts it out, cutting the feeder stops the Metro and darkens the lamps, seed + input log replays to the same hash), then ported to the kernel as `v2/sim/systems/`. Rules carried into the kernel: - **Events delivered one tick late** (raised in N, delivered at the start of N+1, in raise order): ordering bugs become impossible and replay nearly free. - **Player input is just events**, the same path missions and mods use. - **Declare each system's reads and writes and check them:** shared fields have several writers (`wet` from weather and snow, `health` from fire and damage), so coupling moves from imports into data. The kernel enforces the manifests and generates a who-writes-what table. - **Explicit tick phases:** `input, environment, systems, physics, post` (orchestrator decision 2026-09-27; formats v1.2 matches). Open: formats section 14 orders systems within a phase by `after` then id, the kernel by declared order; decide in JS (changes hashes). - **Emergent behaviour needs scenario tests** asserting each moment, or a retune silently breaks it (rain first lost to a 1000°C fire). The Heaton slice (`v2/sim/scenarios/`) is the big one. - The one seam where two systems' worlds touch: a train reports which overhead-line section it is on, which power treats as a graph link. ## Electricity (first) A graph of sources (substations, generators), conductors (cables, the Metro overhead line) and consumers (street lamps, traffic lights, shop signs, the Metro, doors, alarms); no power, no function. Cut the overhead line and trains on that section coast to a stop; knock out a substation and a street goes dark and the traffic AI treats its junctions as unsignalled. Flow on a graph updated a few times a second, not circuit physics: Cities: Skylines does plain reachability; Factorio pools a network and slows every consumer proportionally on a shortfall (lights dim, the Metro crawls), which is the legible failure to copy. Rust's typed player-wired nodes are the model for moddable power. Built: Metro on any placed line with a power-cut test. ## Heat (second) Every entity has a temperature; materials have heat capacity and conductivity; sources emit (a burning car, an engine, a street lamp, a person). A coarse heat field near players diffuses and cools towards the weather's ambient. Rules that read it: snow and ice melt above 0°C, tyres lose grip on ice, metal glows, things ignite at their ignition point, people avoid heat. As built, heat is an energy balance per 2 m cell in real units (radiative and convective loss); the constants are in `v2/sim/systems/CLAUDE.md`. ## Fire Combustion is a rule on the heat model: flammable materials above ignition burn, consume fuel, emit heat, spread through the heat field and go out when fuel runs out, water hits them or rain soaks them (wetness raises effective ignition). Fire damages `health` (hooking destruction) and makes smoke (a volumetric that blocks NPC sight). Spread is capped per area so it stays fun, and fire is server-authoritative so every peer sees the same fire. - **As built (2026-09-27, real units):** a car peaks ~1,000-1,130°C, spreads nose-to-tail in ~1-2 min dry, burns out in ~21-24 min; a shed burns ~18 min. Inputs `Ignite`, `Detonate`, `Douse`; tuning lab `node v2/sim/systems/fire-lab.mjs`. - **Decided for play:** rain of >= 1 mm/h on a surface stops it catching (stronger than real physics; one constant, `CATCH` in `fire.mjs`); a light shower stops spread but not a fully involved car; ~12 mm/h or a hose (`Douse`) puts it out. ## Later systems on the same framework Water network (hydrants, burst mains), gas (leaks, explosions), traffic signal timing, alarms and police response. Every system is components on entities exposed through I/O (`OnPowerLost`, `OnPowerRestored`, input `SetPowered`), so mods and missions can use them. ## Environment: three kinds of state 1. **Pure fields:** `f(world_time, seed, position)`, identical on every peer, zero network cost. The server only sends `{epoch, time_scale, weather_seed, weather_mode}` on join and on change. Deterministic maths only (`dmath.mjs` in JS, the `libm` crate in Rust). 2. **Integrated fields:** snow depth, wetness, puddles, ice: coarse grids stepped at the fixed tick from the pure fields, deterministic given the same ticks. Player-caused changes (a fire melting snow, a burst main) are owned deltas, networked as usual. 3. **Nobody reads the clock directly.** Streetlights have a `photocell` reading `ambient_light`; traffic AI reads `visibility`. Only the field layer knows the time. ### Clock, sun, moon, tide (built 2026-09-27, `v2/sim/clock/`) Test: `node --test v2/sim/clock/test/fields.test.mjs`. - `clock(t)`: date, season, day of year; `time_scale` per world (real time in the shared world, faster or frozen in private ones). Sun, moon and tide key off it. - `sun`, `moon`: SunCalc's model (plus Meeus terms for the moon) on deterministic `dmath.mjs` (fdlibm port using only + - * / sqrt; the Rust core ports SunCalc, ~150 lines, on the `libm` crate; bit-identical on V8 and JavaScriptCore; a test fails the build on any engine `Math.sin` etc.). Sunrise/sunset within ~2 min of USNO; full moons within ~70 min. - `tide(t, x)`: all 50 real North Shields constituents from TICON-4 (CC-BY), high/low waters within 4.2 min and 0.12 m of a published table; our own predictor (NOAA SP98 formulas), validated against ADMIRALTY's free Discovery API in CI, never called at runtime. Upriver, lag and scale by channel distance, fading at the tidal limit (Wylam). Range roughly 1.5 m neaps to 5.2 m springs (unverified). **Estimated:** mean level above chart datum (2.95 m), the Tyne lag/scale/fade parameters, the ambient light curve. - **Storm surge:** weather adds a `surge` term; a spring tide plus an easterly gale floods the Quayside through the local shallow-water sim. - Events: `OnTideHigh/Low`, `OnSunrise/Sunset` (missions only; systems read `ambient_light`), `OnFrost` when a cell crosses 0°C. - The golden hash over 2026 hourly samples (`9c8e08d0`) is world format: change it only with a format bump. ### Weather (built 2026-09-27, `v2/sim/weather/`, simulated mode only) Test: `node --test v2/sim/weather/test/weather.test.mjs`. - `weather(t, x)`: wind vector (regional mean plus gusts), temperature, dew point, three cloud layers, precipitation rate and type, visibility, lightning potential. - **Simulated mode (default):** a seeded hourly Markov chain over weather regimes (and a hidden air-mass state, which makes snow come in episodes), per-month tables baked from ~30 years of ERA5 (1995-2024, via Open-Meteo). Matches ERA5: monthly temps within 0.37°C at first build (0.32°C now), 689 vs 682 mm rain, overcast share 0.438 vs 0.440, snow days 8.3 vs 7.8 (current figures in `v2/sim/weather/CLAUDE.md`). ERA5's grid rain is below the Newcastle gauge's ~784 mm; the sim follows ERA5. Golden hash `c9e49069` (seed 42, 2026). Since 2026-09-27 this real weather drives the kernel's weather system by default. - **Real mode (not built):** the server polls Open-Meteo (Met Office UKV 2 km) and broadcasts ~40-byte hourly keyframes; peers interpolate. Licence: the data is CC BY 4.0 with attribution (settled in `v2/sim/weather/CLAUDE.md`; the spec once listed CC BY-SA too); the free API itself is for non-commercial use. - **Local modifiers:** `wind_exposure` on bridge decks and the river corridor (tall buses and cyclists on the Tyne Bridge feel it); valley fog pooling at night. - **Haar:** the North Sea fret as a moving fog volume advected inland on easterlies, most likely April to September: the coast at 5°C in fog while the centre is sunny. - **Default sky is overcast:** cheapest and most authentic, so the lighting budget goes on sky-light AO rather than hard sun shadows. - **Systems reading weather:** grip (material + `wet`, `ice`, `snow_cover`); fire (rain feeds `wet`); heat (air temperature is the ambient; sun gain = sun altitude x (1 - cloud), reusing the LIDAR shadow march); NPCs (`visibility`, umbrellas, shelter, fewer people); wind drag on `sail` components (flags, litter, bins, tarps; foliage is shader-only); lightning strikes seeded by `(t, cell)` so every peer agrees, firing `OnLightningStrike`, adding heat (can ignite) and maybe `OnPowerLost`, thunder delayed by distance / 343 m/s; puddles rise with rain, drain, evaporate and freeze into `ice`. - Rendering of sky, clouds, rain and snow: `rendering-and-audio.md`. ### Vegetation, life and night - **Seasons:** per-species leaf-out and colour from day of year and seed; autumn leaf litter rides the wind; grass after Ghost of Tsushima. - **Town Moor cows:** grazing roughly April to October, a herd agent that turns tail to the wind. **Kittiwakes:** up to ~1,105 nesting pairs on the Tyne Bridge from March, the world's most inland colony: a sound and particle landmark. Quayside gulls chase dropped food (`npcs.md`). - **Streetlights:** white LED default (the council replaced ~34,000 sodium lamps from 2017); sodium as an era preset or mod. - **Night sky:** star limiting magnitude from sky brightness and cloud; moon phase drives moonlight. A power cut darkens the sky and more stars appear, for free from the rules. Still to check: the tide lag from North Shields to the Quayside; Newcastle days with lying snow (the sim matches ERA5 snow days, not lying snow); whether Newcastle publishes a street-tree inventory; the ~784 mm annual rainfall and ~1,105 kittiwake pairs figures (from the research pass, not independently sourced). ## Water Levels of detail, like everything else; get clever rather than simulate everything. - **Big bodies (the Tyne, the sea, the Ouseburn):** never fluid-simulated. A surface function: FFT/Gerstner waves for chop plus a flow map for current, level and flow from the tide clock. Cheap to query anywhere, identical on every peer, free to network. - **Buoyancy:** floating things sample the surface at a few "pontoons" on their hull (Unreal's Buoyancy Component) and get pushed up and dragged. A car in the Tyne floats, tilts nose down and sinks as it fills (`fill` on the entity). - **Local interactive water:** a small GPU shallow-water heightfield near players only: wakes, ripples, puddles, a street flooding from a burst main; faded out elsewhere. - **Falling water:** rain, splashes, gutter drips, hydrant jets, waterfalls (Jesmond Dene) as GPU particles hitting the depth buffer and spawning splashes and wet decals. Cosmetic, never networked beyond "it is raining" and "this hydrant is open". - **Wetness as a material state:** darker, shinier, puddle reflections, lower tyre grip, driven by weather and local water; physical materials read it, so rain changes driving. - Hooks: burst main (water network), a hydrant knocked over by a car (spray, a growing puddle), flooding closes a road for the traffic AI.