Refactor weather system to consolidate weather data handling; remove redundant weather API endpoint and related components, ensuring a single weather reading represents the entire world. Update UI to toggle weather effects and enhance rendering logic for improved visual consistency. Adjust documentation to reflect changes in weather data structure and API responses.

This commit is contained in:
Leonid Pershin
2026-08-17 10:40:52 +03:00
parent c80c9f14e2
commit 062ab497db
22 changed files with 273 additions and 815 deletions
+17 -24
View File
@@ -108,7 +108,6 @@ them without reworking the data model.
| `GET /api/worlds/{id}` | Status of one world |
| `GET /api/worlds/{id}/map` | Metadata plus the chunk index |
| `GET /api/worlds/{id}/chunks/{x}/{y}` | One chunk of geometry |
| `GET /api/worlds/{id}/weather` | The weather field over the map: a 12×12 grid of samples, row-major from the south-west corner |
| `PATCH /api/worlds/{id}/clock` | Pause / resume or set speed (`timeScale` 14). Body: `{ paused?, timeScale? }` |
| `DELETE /api/worlds/{id}` | Remove a world and its chunks |
| `GET /api/climates` | The climate catalogue for the create form, with the latitude band each preset is the default for |
@@ -132,10 +131,10 @@ continentality or altitude rather than latitude, so they are never guessed and h
Weather is a hybrid: the climate gives a deterministic baseline (seasonal curve, daily curve, wet season),
and a handful of pressure systems drift across the map on top of it as ECS entities, fading in and out. Cloud,
rain, wind and the apparent temperature all fall out of that field, which is why a front visibly crosses the
map instead of the whole world flipping from sunny to wet at once. Systems drift at a fixed rate in normalised
world space rather than a real one: a genuine front crosses ten kilometres in minutes, which at five game
minutes per real second would be a flicker.
rain, wind and the apparent temperature all fall out of that field, sampled at the middle of the map — one
reading is the world's weather. Systems drift at a fixed rate in normalised world space rather than a real
one: a genuine front crosses ten kilometres in minutes, which at five game minutes per real second would be a
flicker, whereas this gives a sky that turns over across a game day.
The drifting systems are persisted in `state.json` so a restart resumes the sky it had. Come back after more
than a game day away and the model rolls a fresh sky for the season instead — stepping days of drift in one
@@ -153,8 +152,7 @@ you cannot tell how deep the snow lies without knowing what the sky did for the
integrated as the world ticks and stored alongside the pressure systems. A world created in a Siberian
January starts under snow rather than waiting for the first fall.
`GET /api/worlds/{id}` carries the weather at the middle of the map for the HUD; the full grid is a separate
call, because the world list would otherwise haul sixty-four samples per world on every poll.
`GET /api/worlds/{id}` carries the weather along with the clock, so the client needs no second poll for it.
Geometry travels as flat `[x0, y0, x1, y1, …]` arrays of world metres, which is exactly what PixiJS
`Graphics.poly()` accepts, so the client never reshapes it. Responses are compressed; chunk files are written
@@ -191,25 +189,20 @@ floor so hairlines stay visible. At street level the map picks up the things tha
- gentle bends in roads and watercourses are rounded off by Chaikin corner cutting; corners sharper than 50°
are left alone, because a gridded town is full of genuine right angles
`WeatherLayer` sits over the map. **What it draws is about the ground, not about the viewport.** Only the
light of the time of day covers the screen evenly — the sun sets on a whole town at once — and that is the
one flat rectangle: a colour interpolated from the sun's elevation through golden hour, dusk and night.
`WeatherLayer` sits over the map in screen space. One reading covers the whole world — a generated world is
a town, not a continent, and a shower does not fall on half of one — so the wash covers the view evenly.
Below the place names goes a colour for the time of day, interpolated from the sun's elevation through golden
hour, dusk and night and greyed down by cloud while the sun is up; then white for lying snow; then a pale haze
for fog, blizzards and sandstorms. Above the names falls the precipitation — slanted streaks for rain,
drifting dots for snow, blown dust for a sandstorm — leaning downwind at a slant taken from the wind and
capped so a gale still looks like weather rather than a barcode. Thunderstorms flash.
Everything else is read from the field under each patch of screen and painted where it is actually
happening: lying snow, the dimming under cloud, and the precipitation map itself, which darkens the ground a
shower is standing over so the shape of a front is legible from any zoom. The patches are a coarse grid,
drawn oversized and blurred, which turns ten steps into a gradient. Falling rain and snow are drawn above
the place names, and each drop is only drawn if the ground beneath it is wet, so the fall thins out across
the edge of a front instead of the whole screen raining together.
The whole overlay can be switched off from the game bar; the choice is remembered like the theme. The weather
still happens either way — the button only decides whether it is drawn.
Pressure systems are sized to be cells on the map rather than the whole sky. A real depression spans a
thousand kilometres and would sit over a town as one flat value with no edge at all — the same trade already
made for drift speed. Their lower bound is set by the export grid: a system narrower than about two node
spacings aliases into it and the client interpolates a lie.
The maths lives in `sky.ts` and `weatherField.ts`, which import no PixiJS and are unit-tested; `weatherLayer.ts`
only knows how to paint the result. A dark theme pulls the night wash back rather than switching it off,
because the map is already drawn dark and dusk still has to feel like dusk.
The maths lives in `sky.ts`, which imports no PixiJS and is unit-tested; `weatherLayer.ts` only knows how to
paint the result. A dark theme pulls the night wash back rather than switching it off, because the map is
already drawn dark and dusk still has to feel like dusk.
Place names are drawn in screen space so text keeps a constant size at every zoom, and the work is split in
two. `labelPlacement.ts` decides *which* names to show: candidates are ranked — water bodies first, then