Implement climate and weather features in world simulation; enhance API with weather retrieval and climate selection options, update UI to support climate selection during world creation, and improve weather display in the game interface.
This commit is contained in:
@@ -108,14 +108,42 @@ 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: an 8×8 grid of samples, row-major from the south-west corner |
|
||||
| `PATCH /api/worlds/{id}/clock` | Pause / resume or set speed (`timeScale` 1–4). 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 |
|
||||
|
||||
Generation takes tens of seconds — mostly waiting on Overpass — so `POST` returns straight away and the client
|
||||
polls for status. Only one generation runs at a time, to stay a good citizen on the shared Overpass mirrors.
|
||||
The number of worlds that may exist at once is capped by `WorldStorage:MaxConcurrentWorlds` (today that means
|
||||
folders on disk; later the same budget will limit concurrent simulation).
|
||||
|
||||
### Climate and weather
|
||||
|
||||
Each world picks one of twelve Köppen-lite climates at creation. Leave it out and the server guesses from the
|
||||
latitude; the create form previews that guess using the band limits `GET /api/climates` returns, so the rule
|
||||
lives in exactly one place. Three presets — tropical monsoon, cold steppe and highland — depend on
|
||||
continentality or altitude rather than latitude, so they are never guessed and have to be chosen.
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
jump is not a simulation, it is a teleport.
|
||||
|
||||
Snow is the one part of the weather with memory. Everything else is a function of the current instant, but
|
||||
you cannot tell how deep the snow lies without knowing what the sky did for the last few days, so it is
|
||||
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.
|
||||
|
||||
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
|
||||
in wire format and streamed straight from disk.
|
||||
@@ -151,6 +179,18 @@ 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 in screen space, so the weather does not slide about when you pan. Below the
|
||||
place names goes a wash: a colour for the time of day, interpolated from the sun's elevation through golden
|
||||
hour, dusk and night, 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 — leaning downwind at a slant taken from the local wind and capped so a gale still
|
||||
looks like weather rather than a barcode. The whole thing is read from the server grid under the middle of
|
||||
the screen, so panning towards a front walks into the rain.
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
arterials, then land cover, then side streets — and placed greedily, dropping anything that would overlap a
|
||||
|
||||
Reference in New Issue
Block a user