Document the pass, and correct the notes it made stale

The README is this project's design document, so the reasoning behind
each change belongs in it rather than only in commit messages. Adds
sections on what a crash costs and why the coefficients are anchored to
a measured figure, killed in action, giving up a job, districts and
landmarks, militia, and how a hunter drives.

Also removes three notes that had gone stale - persistence not
existing, wear rates awaiting tuning, and the heat lines wanting to
come out of the HUD - and replaces the "take the debug lines out first"
instruction under Next with three Phase 9 gates, since all of those are
now done.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
dejvino 2026-08-09 07:32:12 +02:00
parent 6c7e67cf79
commit 1a7a4776c1

178
README.md
View File

@ -11,6 +11,8 @@ Endless driving survival game.
- **Phase 6** — an inhabited world: traffic, people, patrols, and a live war.
- **Phase 7** — sound, synthesised from the simulation.
- **Phase 8** — recognition: being noticed, being hunted, and getting away.
- **Phase 9** — stakes: a crash that costs something, a death that costs
something, a chase you can lose, and a town you can navigate.
## Running
@ -28,9 +30,16 @@ seeds may be numbers or words.
| `Space` | handbrake (rear wheels only) |
| `R` | respawn the car — **does not** repair it |
| `1``3` | accept a mission, when parked at a base |
| `X` | give up the mission in hand, when parked at a base |
| `M` | sound on/off |
| hold `R` | wipe the campaign and start over (5 seconds) |
Add `?debug=1` for the tuning readouts — heat, district standing, which band
you are in — and the debug panel. Without it the HUD tells you the car, the job
and your cover, and nothing else: several of the design gates below are about
reading the world rather than a number, and they cannot be judged with the
number on screen.
Progress saves itself every 20 seconds, and immediately whenever you take or
finish a job. Saves are per seed; `?seed=x` is a separate campaign.
@ -150,6 +159,30 @@ bypass lanes around a checkpoint from none obstructed to about two thirds. You
can still get around one; it means threading a maze at walking pace instead of a
free detour at speed.
### Districts, and the buildings that ignore them
Every building used to be the same grey box between four and twelve metres tall.
From the air the world was one texture; from the driver's seat every junction
looked like the junction before it, and "learn the map" was not something the
map supported.
The world is cut into districts, and a district agrees with itself: its
buildings share a palette, a rough height and a silhouette, and vary only
slightly around them. That agreement is the point twice over — it makes crossing
into the next district a visible event, and it is the only reason the exceptions
read at all.
Because about one plot in fifty ignores its district completely: much taller,
lighter, a different shape. Those are the things you actually navigate by, and
they are floored at a height that clears every ordinary roofline on the map,
since a landmark you cannot see from outside its own district is no use in the
only place you need it.
Colour rides on the instance rather than the material, so a district costs no
extra draw calls. Colliders stay boxes whatever the silhouette says — both the
physics and the line-of-sight grid already assume a building is solid to its own
footprint.
### Other deliberate choices
- **Concrete never closes around the car standing in the gap.** A block spawning
@ -196,6 +229,15 @@ the most, but it pays for what *arrives*, not what you set off with. Hand-in
works at *any* base, not the issuing one, because the brief is explicit that a
blocked road home must never strand the player.
### Giving one up
Park at any base with a job you have not started bringing home and `X` takes it
off you, for 12 m of front line. A target can end up behind concrete there is no
way round, and the only exit used to be holding `R` — which does not drop the
job, it destroys the campaign. It cannot be free either, or the board is a
reroll button: park, dislike the route, drop it, take another. Dropped jobs are
counted apart from finished ones. They are not runs.
### Work you find rather than work you are given
Past the line, and only when you have nothing in hand, someone occasionally
@ -229,6 +271,53 @@ This is what gives the board's reward a reason to matter. Without an economy the
choice between a safe route and a risky one has no stakes; with one, the risky
job is what keeps the car alive a while longer.
### What a crash actually costs
The impact coefficients were originally written against an imagined scale.
Measured on the running game, a head-on into a building at 85 km/h comes back
from Rapier as a single contact event of about **2.5e6 N** — twenty-five units
of "impact" against the old 1e5 reference. Through the old coefficients that was
123% of the chassis and 37% of its ceiling, gone, in one step, in a game whose
missions pay about 0.3 parts. One crash ended the campaign, and the campaign had
no way to end.
Everything is now anchored to that measured figure, and damage is capped per
step — a crash is a pile of contacts across several steps plus whatever the car
scrapes on the way to a standstill, and that sum has no natural bound. A hard
crash costs about 15% of the chassis and 5% of its ceiling.
Baseline wear came down with it. Tyres shed 8e-5 per metre, so twelve clean
kilometres took them from new to ruined while a two-kilometre round trip paid
0.3 parts against 0.24 of condition spent. Driving perfectly barely broke even.
At a quarter of that, a typical mission spends about 0.06, so roughly two clean
runs pay for one bad crash — enough slack to gamble with, not enough to ignore.
## Killed in action
A car could be driven to zero in every subsystem and would keep going forever at
a wheezing thirty kilometres an hour. Nothing was at stake, so nothing was worth
protecting, so there was never a reason to take the quiet road. "Decline, not
reset" only reads as a stake if the decline can run out.
When the **chassis** is gone, so are you. Somebody pulls you out; you come round
at the nearest base; the job in hand is written off with the car; and 25 m of
front line goes back to the enemy, through the same path a finished mission uses
so the war moves for one reason only.
The replacement is a real car — patched to its ceilings, because one that still
needed the parts you do not have would just kill you again on the way out of the
yard — and every ceiling is 8% down on the one you wrote off. That is the
ratchet applied at the one moment repairs cannot reach.
It stops at a floor, and that is worth being honest about: strictly it breaks
"the ceiling only ever falls". Without one, enough deaths issue a car that is
written off the instant you are handed it, and the only way out of the campaign
is wiping it. A genuinely miserable car you can still drive is the interesting
version of that ending rather than the broken one.
Nothing about any of it is announced. The front moving against you is something
you find out by driving back out there.
## A world with people in it
Up to Phase 5 the world was scenery. Heat poured concrete on a road and that was
@ -256,6 +345,20 @@ Let the road cool and the whole thing is abandoned.
**Traffic and people.** Civilian cars route across the network with somewhere to
be; people wander on foot near the buildings. Both scatter when shooting starts.
**Your own side is standing in your own streets.** Liberated territory read as
empty — no patrols by design, and nobody else either — so the safest part of the
map was also the deadest, and coming home felt like arriving nowhere rather than
arriving somewhere held. Militia mirror the ambient patrols the other way up:
fourteen at home, five in contested ground, none past that, because a friendly
face on the frontier would be a safety net the setting is not supposed to have.
Crossing the map reads 14, 14, 11, 5, 0 against enemies 0, 0, 3, 4, 4 — who is
standing in the street is a more legible signal of whose ground you are on than
the palette is.
They are placed by checking who holds the ground they would *stand* on rather
than the band the player is in, since near a border those are different answers,
and they leave when the line moves over them.
### The war you are driving through
Skirmishes break out between the two armies on contested and occupied ground,
@ -294,6 +397,20 @@ patrols — a chase you could outrun by passing more of them would be no chase.
This is also the *only* thing that makes the enemy shoot at you: being a target
is a state, not a property of the road you are on.
**They drive differently when they are chasing.** A patrol on its rounds does
14 m/s and a healthy car does 29, so being hunted used to mean holding `W` until
the 95 m sight radius did the rest. Chasing cars wind up to 21 m/s, which sits
deliberately between the two: faster than anyone averages through a grid of
junctions and buildings, slower than the car's top end on a clear run. So a long
trunk straight is an escape and the lanes are not — which puts the road
hierarchy under real pressure for the first time. The fast conspicuous road is
the one you want when they are behind you, and it is the one they will be
looking on next time.
They also go **round** buildings rather than through them. Line of sight was
already blocked by them and movement was not, which made "break line of sight
and change direction" advice the world did not honour.
**Getting away** takes one of two things:
- **Break line of sight for fourteen seconds.** They drive to where they last
@ -301,7 +418,10 @@ is a state, not a property of the road you are on.
the move. Any glimpse resets the clock.
- **Let your own side occupy them.** Hunters are patrols, not fanatics: an
insurgent squad on their doorstep is a better use of their time. Enough of
them peeling off ends the chase outright.
them peeling off ends the chase outright. This is why the militia matter
mechanically as well as atmospherically — running for your own lines works
because the closer you get to home the more likely they peel off. The rule
always existed; there was simply nobody around to trigger it.
Escaping does not hand your cover straight back. Suspicion drops to 0.6 and
decays slowly from there — they know there is a car worth looking for, and you
@ -407,9 +527,11 @@ running diagonally, so area is nowhere near linear in depth.
**You are never told which band you are in by the world itself.** Crossing a
border eases the fog, the light and the palette: your own ground is open and
green, contested goes flat and hazy, occupied turns dusty and closes in, the
frontier is cold and short-sighted. The HUD names the band for now, but that
line is debug scaffolding for the same reason the heat numbers are — Phase 3's
gate is telling them apart *without* being told.
frontier is cold and short-sighted. On top of the light, the population: home
ground is thick with your own people and thins to nobody past the line. The HUD
names the band only under `?debug`, for the same reason the heat numbers are
behind it — Phase 3's gate is telling them apart *without* being told, and you
cannot judge that with the answer on screen.
## The map
@ -417,6 +539,15 @@ A sketch map, top right, drawn from `Intel` rather than from the world:
- **The road network's shape is always visible**, as faint dashed lines. You are
an operative with a map, not an amnesiac.
- **Your own ground starts filled in.** A fresh campaign knows the pocket its
side holds — the cells, the roads through them, and the junctions as places
you have been. You are a local with a car; the war is the part you would not
know. It also fixes the board, which splits its offers into targets you have
visited and targets you have not: with nothing visited every offer came up
"new", so the known-against-new comparison the mission design turns on was not
on screen until you had wandered around for a while. None of it survives the
line moving — the map still only remembers who held a place when you last
stood in it.
- **Roads you have actually seen** are drawn solid and coloured by the heat you
remember on them — which is what you last saw, not what is there now.
- **Ground you have driven through** is filled in, tinted by who held it *when
@ -445,13 +576,14 @@ ever opening a browser.
```
src/
sim/ pure model, no engine imports:
world, roads, heat — the map and its memory
world, roads, heat — the map, its districts and its memory
radio, opportunities — texture: chatter and field work
units, combat — traffic, patrols, soldiers, rounds
save — the campaign as JSON
regions — the front line and who holds what
intel, routing, quests — what the player knows and is asked to do
bases, car — where missions come from, and decline
kia — what happens when the decline runs out
physics/ Rapier world, raycast vehicle, input → wheel forces
render/ three.js scene, roads, markers, chase camera
core/ fixed-timestep loop, seeded RNG, keyboard
@ -477,13 +609,11 @@ one place where "how broken the car is" turns into "how it drives".
- **The car handles like a placeholder.** Suspension, grip, and engine numbers in
`physics/physics.ts` and `sim/car.ts` are first guesses that pass a smoke test,
not something tuned by feel. That tuning *is* Phase 0's verification gate.
- **Wear rates are deliberately aggressive** so decline is visible in minutes
rather than hours. Turn them down in `applyWear` once the curve reads right.
- **Wear rates are tuned against the parts economy** rather than by feel: a
clean mission spends about a fifth of what it pays, and a hard crash costs
roughly two clean missions. Those ratios are the dial, not the raw numbers.
- **No interpolation** between physics steps. Fine at 60 Hz; revisit if the step
rate changes.
- **Nothing is persisted** across reloads — condition, heat, intel and completed
runs all reset. IndexedDB comes with the campaign layer, and `sim/intel.ts`
plus `sim/heat.ts` are the two things that most need it.
- **Repairs are automatic on hand-in.** Parts get spent the moment you report
back. The brief wants repair to be a physical, on-foot act with scavenged
parts; that is Phase 6.
@ -499,11 +629,12 @@ one place where "how broken the car is" turns into "how it drives".
gameplay shape is right; the presentation wants berms, wire, or wreckage on the
outer sections. They can also clip scenery placed on the verge — both are
static bodies, so physics is unaffected, but it looks wrong up close.
- **Hunters steer round buildings by trial rather than by pathfinding.** They
try seven headings, widening either side of straight-on, and take the first
that is clear. Good enough that corners work; it will not solve a cul-de-sac.
- **Heat has no diegetic signal at a distance.** You learn a road is hot by
arriving at the checkpoint. The brief wants it readable from patrol density and
wreckage before you commit — that needs the enemy presence Phase 5 brings.
- **The heat HUD lines are debug scaffolding.** The brief is explicit that no
numeric heat meter ships; they exist to tune the curve and should come out.
- **Bundle:** ~2.7 MB raw / ~945 KB gzipped, dominated by Rapier's WASM, which
`rapier3d-compat` inlines as base64. Switching to the non-compat `@dimforge/rapier3d`
package serves the WASM as a separate file (~570 KB gzipped, compiled in
@ -547,15 +678,28 @@ and people you have no reason to interact with, and a war that is obviously not
about you. If firefights read as encounters staged for the player, the spacing
and spawn rules in `sim/units.ts` are the dial.
**Phase 9:** three gates, and all of them are about whether anything is at
stake.
- You slow down for a corner you would previously have taken flat, because the
car is worth protecting. If you still drive like it is a rental, `IMPACT_REFERENCE`
is too forgiving; if a single mistake ends the run, it is too harsh.
- Being hunted makes you think about *where* rather than only about the
throttle. If you still escape by holding `W` on any road at all, raise
`HUNT_SPEED`; if the chase is unloseable, the answer is more militia near home
rather than slower hunters, since being saved by your own side is the more
interesting exit.
- You can say where you are without the HUD, from the roofline and from who is
standing in the street. Districts and militia are both aimed squarely at this;
if it still takes reading a line of text, push `PALETTES` apart before
touching anything else.
Still deferred: a deeper repair economy
with scavenged parts and on-foot work, car identity and disguise feeding into
heat, and expanded combat. Nothing there should be pulled forward until the loop
heat, expanded combat, and something happening when the front line reaches a
landmark — missions move it and nothing ever acknowledges that it moved. Nothing there should be pulled forward until the loop
below it is proven.
The most useful thing before any of that is taking the debug lines out of the
HUD. Several gates now depend on reading the world rather than a readout, and
the scaffolding actively works against you when you sit down to judge them.
Watch for one failure mode in particular: if you find yourself taking whichever
job is *nearest* and ignoring the intel line entirely, the board is decorative
and the phase has not landed, whatever the reward numbers say.