The heat meter, the district's standing, the road id and the name of
the band you are in exist to tune curves, and every one of them works
against the thing it measures. Phase 3's gate is telling occupied
ground from liberated without being told, and there was a line on
screen naming it. Phase 1's gate is avoiding a road for its history,
with a numeric heat bar underneath it. Neither can be judged while
reading the answer.
Without ?debug the HUD is now speed, condition, parts and runs, the
mission, and whether your cover is intact. The world has to say the
rest: the light, the fog, who is standing in the street, and what is
physically sitting in the road.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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. It also left the palette doing all the work of telling the bands
apart, when the population is the more legible signal: who is standing
in the street tells you whose street it is.
Militia mirror AMBIENT_PATROLS the other way up - thick at home,
thinning fast, none past the contested band, because a friendly face on
the frontier would be a safety net the setting is not supposed to have.
Driving across the map now reads 14, 14, 11, 5, 0 against enemies 0, 0,
3, 4, 4.
They are placed by checking who holds the ground they would stand on
rather than the band the player is in - near a border those are
different answers - and they leave when the line moves over them, or
when the player has driven far enough that they are only ambience
following him about.
There is a quieter consequence. Pursuit already called a chase off when
a hunter had insurgents on its doorstep, so running for your own lines
now genuinely works. The rule existed; there was nobody around to
trigger it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every building was the same grey box between four and twelve metres
tall. From the air the whole 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 now 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 - it is what
makes crossing into the next one a visible event, and it is the only
reason the exceptions read at all.
Because the occasional building ignores its district completely: much
taller, lighter, a different shape. Those are the things you actually
navigate by. They clear 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.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The map was blank everywhere on a fresh start, including behind your
own lines, which read as amnesia rather than insurgency. You are a
local with a car: the pocket your side holds is the part of the map you
would obviously know, and the war is the part you would not.
It also fixes the board. offersAt splits its offers into targets you
have visited and targets you have not, and with nothing visited every
offer came up "new" - so the known-against-new comparison the whole
mission design turns on was not on screen until you had wandered around
for a while. The first board of a fresh campaign now reads: a known
target at 0.14 parts against new ones at 0.31 and 0.41.
Strictly bounded to liberated ground. Contested ground is not yours to
know, and none of this survives the line moving - the map still only
remembers who held a place when you last stood in it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A patrol drove at 14 m/s whether it was on its rounds or chasing. A
healthy car does 29. Being hunted therefore meant holding W until the
95m sight radius did the rest: no cornering, no routing, no decision
anywhere in it.
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. A long trunk
straight is an escape and the lanes are not, so the road hierarchy is
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 now instead of 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.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
state.active was only ever cleared by completing a job. A target can
end up behind concrete there is no way round, and the only exit was
holding R for five seconds - which does not drop the job, it destroys
every mile of wear, every road the enemy learned and the whole map you
built.
Park at any base with a job you have not started bringing home and the
board offers to take it off you for X. It costs 12m of front line, half
what dying costs: deciding a route is not worth it is a judgement the
game should let you make, and it cannot be free or the board becomes a
reroll button. Not offered on the return leg, since that arrival is a
hand-in and it is about to pay you.
Dropped jobs are counted apart from completed ones. They are not runs.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A car could be driven to zero in every subsystem and would keep going
forever at a wheezing thirty km/h. 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 the ground your work had won is handed back - 25m, 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 so it can be
driven off the forecourt, 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: 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 miserable car you can still drive is the
interesting version of that ending.
None of it is announced. The front moving against you is something you
find out by driving back out there.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Tyres shed 8e-5 per metre, so twelve kilometres of clean driving - no
crashes at all - took them from new to ruined. A two-kilometre round
trip paid ~0.3 parts against ~0.24 of condition spent: driving
perfectly barely broke even, and any knock put you permanently behind.
That is not a decision about how hard to push the car, it is a decline
with no way to arrest it.
At a quarter of the old rates the same trip spends about 0.06, so
roughly two clean runs pay for one bad crash. Enough slack to gamble
with, not enough to ignore.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The impact coefficients in applyWear were written against an imagined
scale. Measured on the running game, a head-on into a building at
85 km/h comes back as a single contact event of ~2.5e6 N — 25 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 hit,
in a game whose missions pay about 0.3 parts each. One crash ended the
campaign, and the campaign had no way to end.
Anchor the reference to the measured figure instead, and cap what any
single step can take: 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.
The same 14-second flat-out run into a building now costs chassis
100 -> 65% and its ceiling 100 -> 89%. Expensive, survivable, and
still the worst thing that can happen to the car.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Cover is now a state you can lose. Suspicion builds while enemy eyes are on you,
squared with proximity so one patrol beside the car outweighs a crowd across the
street, and blocked by buildings like everything else. It cannot build at all
behind your own lines. Ramming, being shot at and running somebody over skip the
meter and land in full.
At the top of it you are hunted: nearby enemies drop what they were doing and
come, and keep joining as you drive past fresh patrols, since a chase you could
outrun by passing more of them would be no chase. Being hunted is also now the
only reason the enemy shoots at you — a target is a state, not a property of the
road you happen to be on.
Two ways out. Break line of sight for fourteen seconds, with any glimpse
resetting the clock; hunters drive to where they last saw you rather than where
you are, so turning a corner and changing direction is the move. Or let your own
side occupy them — hunters are patrols, not fanatics, and enough peeling off
ends the chase outright. Escaping leaves suspicion at 0.6 rather than zero: they
know there is a car worth looking for.
Tuning a test caught: the post-escape value sat a hair above the "suspicious"
threshold, so the afterglow expired within a second and the rule may as well not
have existed. Decay slowed to match, so cover is earned back by being dull
rather than by turning one corner.
Verified end to end in the running game: loitering beside a patrol in occupied
ground filled the meter and pulled three hunters, fleeing out of sight cleared it
after exactly fourteen seconds, and a visible player was closed on from 61m to
12m while taking hits.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A patrol working a road now takes a little off the district around it as well,
at roughly a tenth of the rate it takes off the road. Deliberately feeble: any
faster and sitting still while patrols came and went would quietly launder your
reputation, and district heat exists precisely to be the part you cannot patrol
away. Passed as a position rather than a district id, so the unit sim still does
not need to know how areas are gridded.
Adds a debug panel under ?debug=1 with buttons to wear the car down or patch it
up. Driving a wrecked car is something you want to try while driving, not while
typing at a console. An earlier version of the console helper took an amount and
ignored it, which would have made it silently useless.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Area heat. Road heat alone rewarded a strange kind of play: work one part of
town hard but take a different street every time, and nothing ever got hot. A
second, coarser pool now accrues from being inside a district at all — on-road
or off — and bleeds into every road running through it, including ones never
driven. Deliberately capped below the barricade threshold: a hot district gets
roads patrolled, but concrete still takes somebody actually using that road. It
also decays slower than road heat, because one patrol re-secures a road while a
district's reputation lingers.
Surfaces. Off the route the car now makes about 40% of its road top speed, loses
grip and scrubs momentum in a fifth of the distance, so the road hierarchy is
something you feel rather than something the map merely claims. A first tuning
made off-road a 12 km/h crawl, which removed the choice instead of pricing it —
going round a checkpoint cross-country has to stay a legitimate move.
Speed. Heat scales with how fast you were going, not just how far, and stops
rewarding ever-higher speed past a point so the answer is never one exact number
on the clock. With the road classes, that makes the routing decision real: the
trunk fast and noticed, or the lanes slow and ignored.
Save format goes to v2 for the district pool; older saves are refused rather
than half-restored. Also fixes a population test that hardcoded densities since
tuned, and README claims about patrols and AI that Phase 6 made untrue.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Steering. The falloff with speed was linear, so the car had already lost a third
of its lock by 20km/h — exactly the speed you take a right-angle junction at, on
a map made of right-angle junctions. It is quadratic now, which leaves low
speeds nearly untouched and still calms things down at pace, plus a faster rack
and a little more lock. Pinned with a test that drives an actual quarter turn and
measures its radius. A first attempt asserted that fast corners take longer,
which is simply false: a fast car swings through ninety degrees quicker, just
across far more tarmac. Radius is what a corner costs you.
Contact. Units were points that ignored everything, so there was nothing to hit.
Vehicles now carry kinematic bodies and ramming one is a real collision that
damages them and shoves you. People get no collider on purpose — a capsule means
snagging on pedestrians or launching them — so you drive through them and they go
down, and running over a civilian is counted for later.
Territory. Liberated ground covered more than half the map because the player
started in the middle of it, so most of the world was safe by geometry. The
starting base is now at the friendly edge and the bands are anchored to give
roughly 15/25/27/33 across liberated, contested, occupied and frontier. Measured
in the running game rather than guessed, since the axis runs diagonally across a
square and area is not linear in depth.
Also fixes a fragile test that compared reward rates across whichever targets a
board happened to offer, and so mostly measured route length rather than the
novelty premium it claimed to check.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
No audio files. The build stays self-contained, so the engine is detuned
oscillators, tyres and gunfire are shaped noise, ambience is a filtered drone.
The constraint pays: sound generated from the sim can carry the sim's state
instead of decorating it.
The engine has gears, so the note climbs and drops rather than tracking the
speedometer forever. A worn engine sounds wrong rather than merely slow —
roughness rises and the top of the rev range falls away as condition drops, so
decline is audible as well as felt. Bald tyres squeal sooner than good ones, off
the lateral slip the physics actually reports. Gunfire falls off with the square
of distance and pans as the car turns, so a firefight is a direction. Territory
has a tone: the drone drops and thickens past the line, matching the palette.
Split so it can be tested: audio/mix.ts holds every decision as plain numbers and
is covered; audio/audio.ts is only wiring, which cannot run outside a browser.
A test caught panning being mirrored — +X is the car's left in this world, so the
sine needed negating.
Also adds hold-R-for-five-seconds to wipe the campaign, with a filling meter so
it cannot happen by accident, and M to mute. Tapping R still just respawns.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three fixes, all from watching it run.
Cars stuck against buildings. routeTo dropped the node a route starts from,
which is fine for a unit standing on it and wrong for anything re-routing from
mid-segment: its first leg became a straight line to a node two hops away,
across buildings and open country. Routes now include their start node so a unit
rejoins the road before setting off.
No patrols anywhere. They only existed as a reaction to the player's own tracks,
so hostile territory was empty until you had personally made a road notorious.
Enemy ground now carries standing patrols by how dangerous it is — none behind
your lines, five on the frontier — spawned onto their roads so the territory has
them the moment you arrive.
This surfaced a second bug: dispatched units were culled at 546m from the player
while dispatch origins reached 600m, so a patrol or engineer could be deleted on
its first step and simply never turn up. Anything with an assignment is now kept
regardless of range; its expiry still bounds it.
Barricades appearing instantly. Crossing a heat threshold placed concrete on the
road with nobody involved. Construction is now staged: an engineer is dispatched
for the barricade, another for the tower, and heatProps is driven by finished
builds rather than by heat levels. It still refuses to close a slab around a car
parked in the gap.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Nothing on a road simply appears any more. A road that turns notorious gets a
patrol dispatched to it: it starts at a junction a few hundred metres away,
drives there, sweeps the road for about a minute, and while it works the road it
brings the heat down — then leaves. Checkpoints are built rather than placed. At
turret level an engineer is sent, the tower rises over forty seconds of someone
standing on site, and a gunner mans it once it stands, up on top so he clears his
own barricade. Let the road cool and the whole thing is abandoned. heat.ts now
only pours concrete; everything living has to arrive.
Civilian traffic routes across the network with somewhere to be, people wander on
foot, and both scatter when shooting starts.
Skirmishes break out between the two armies on contested and occupied ground
regardless of where the player is. Rounds are real objects in flight rather than
instant hits between combatants — that is the point of the feature. If shots
resolved instantly, driving through a battle would cost nothing and "the only
road to the target runs through a firefight" would not be a decision. Strays hit
bystanders, civilians and the player alike; buildings stop them, so cover means
something; hits go through the same wear model as a crash, so being shot costs
ceiling too.
The enemy does not shoot at an undercover driver by default. They open up only
past the line and only on a road they have already checkpointed.
Reload was being counted in stepUnits while firing lived in stepCombat, so combat
stepped on its own never reloaded. A test caught it; ownership now sits with the
module that fires.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The pane this build runs in does not composite, so requestAnimationFrame never
fires, the loop stalls, and nothing can be screenshotted from outside. Adds a
`?debug=1` handle that steps the simulation by hand and renders on demand, plus
a dev-only endpoint that takes a POSTed frame and writes it to shots/. Moving
the image as bytes rather than a pasted data URL matters: a truncated base64
string decodes to nothing.
Looking at the result immediately found a real problem. Base beacons were wide
translucent cylinders — legible from a distance, a solid green wall from inside,
and the car parks in the middle of one. Now a slim pillar plus a ring on the
ground that marks where to stop. Also raised the sky and fog brightness, which
was dark enough to read as night.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Mission types. Four of them, differing in pacing and risk rather than in the
noun on the board: a quick supply drop, a retrieval whose danger is all on the
return leg, a long stationary intel transmission in the open, and a cheap survey
whose payoff is the map. Retrieval carries cargo that impacts damage, and pays
for what arrives rather than what you set off with, which makes the drive home a
different problem from the drive out.
Radio. Chatter framing the player as an undercover driver being talked at by
people busy elsewhere. Every line is triggered by something actually happening
in the sim — crossing a border, a road you personally made notorious, a car past
saving, or the front genuinely drifting. The enemy-has-other-priorities line
fires off measured drift, so the brief's ambient-drift-is-the-mechanism idea is
said out loud without ever showing a number. Topics carry cooldowns and never
repeat a sentence back to back; a test caught that they could.
Field work. Past the line, with nothing in hand, a wounded stranger or a contact
or a strippable wreck occasionally turns up. Deliberately not on a board: no
route preview, no comparison, no shopping between them. Take the detour in front
of you or drive on. They never appear behind your own lines, where a safe errand
would defeat the point.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Braking. Rapier brake impulses were far too small next to a 1100kg chassis, so
the car would not come to a standstill. Raised, plus a coast brake so lifting
off actually slows you and a hold below walking pace so "stop the car" for a
mission is not a fight. First attempt overshot to 1.9g, which reads as hitting a
wall; tuned to about 0.8g, ~25m from 70km/h, and pinned with tests that assert
both an upper and a lower bound on stopping distance.
Stutter. The render drew the raw latest physics transform, so frames that
consumed two steps or none landed as visible jitter at speed. Now interpolated
between the last two steps by the leftover accumulator. Buildings moved into one
instanced draw call and the minimap dropped to 10Hz, both of which were wasted
work in a frame budget that also has to fit a shadow pass.
Roads. Three classes: single-track lanes, two-lane roads, four-lane trunks that
cross the whole map and are never thinned away. Class drives width, the route
corridor, barricade span, map line weight, surface colour, and two things that
matter more: routing is now by travel time so journeys prefer trunks, and
attention scales with class, so a farm track is slow and unwatched while a trunk
is fast and the first place anyone looks.
Towns. Buildings were small and sparse enough to drive around freely. They are
now laid out on a jittered lattice — rejection sampling cannot pack boxes and
saturated at a third of the target — which takes bypass lanes around a barricade
from 0% obstructed to about two thirds. World scaled to 400m half-extent, 81
junctions, 107 roads.
Autosave. Every 20 seconds, plus an immediate checkpoint on taking and
completing a job. Saves carry the subsystem ceilings, what the player knew
including the parts that are now wrong, and where the war had got to. A save is
validated against seed, format version and array lengths before anything is
applied: half-restoring a campaign is worse than starting a fresh one.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The objective beacon now goes out once the cargo is dropped or the survey is
done. The target is no longer somewhere the player needs to be, and the bases'
own beacons take over for the trip home.
The front moves from two independent sources. Mission completions push it
forward, weighted by what the job was worth, and that contribution decays over
about seven minutes so ground won is not held for free. On top of that the line
drifts on its own in both directions, which the brief is explicit about: ambient
drift is what makes this a war rather than a level, and it is the only mechanism
for the enemy's attention being pulled elsewhere.
Drift is a sum of slow waves, not a random walk. A first attempt at a damped
walk moved 3m in four minutes, because white noise scaled by dt averages itself
out. Waves at incommensurate periods are unpredictable in practice, bounded by
construction, and reproducible from the seed, so a test can assert that a region
changed hands while the player was away. Most of the movement is shared across
boundaries, since a front advances as a whole; independent per-band waves were
squeezing bands into the ordering clamp.
Crucially the map no longer reads live state. Intel now records who held each
cell when the player last stood in it, and the minimap draws that, with the
border inferred from where remembered control changes rather than from the
front's true position. Otherwise the map would quietly hand the player a live
readout of a line that is supposed to move behind their back. Stale roads are
drawn faded, so untrustworthy notes look untrustworthy.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three things, all tangled together enough to land at once.
Regions. A single directional front cuts the map into liberated, contested,
occupied and frontier. Heat accrual behind your own lines is exactly zero, not
merely small — a trickle would eventually fortify your own back garden, and
would mean the safe area quietly punished you for using it. Decay is judged by
whoever holds the road, not whoever holds the ground the car is standing on.
Modelled as one front rather than blobs of territory because the brief wants the
line to move in both directions, and Phase 4 can then just slide the offsets.
Territory is signalled by tone, never by the world announcing itself: crossing a
border eases the fog, light and palette between four looks. Phase 3's gate is
telling occupied from liberated without being told, so the HUD line naming the
band is debug scaffolding like the heat numbers.
Minimap. There was no way to tell where a target was. The map is drawn from
Intel, not from the world: the road network's shape is always faintly visible,
but only roads actually seen carry remembered heat, only ground actually driven
through is filled in, and the front line is dashed in only where both sides of
it have been explored. Adds a coarse fog-of-war grid to Intel, plus an
eight-point compass arrow in the HUD.
Base huts. The building was centred on the junction, so the starting hut sat
directly over the car and hid it. Huts now stand off the junction, in whichever
direction is furthest from tarmac, and are solid.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Four allied bases, placed by greedy farthest-point so no choke point can strand
the player, with the first at the spawn junction so the loop is available at
once. Missions hand in at any base, not the issuing one.
The board offers known and new targets side by side and describes each route in
words. Crucially it never reads heat directly — it reads sim/intel.ts, a record
of what the player has actually observed and when. Notes age while roads keep
escalating unwatched, so a known route is knowable, stale, and never a promise.
Recon missions survey everything within 130m, which is how a new target becomes
a known one without driving every road there.
New targets pay 1.8x. That multiplier is the phase's tuning dial: too low and
nobody takes the unknown, too high and nobody takes the known.
Payment is parts, which repair the car. To keep "decline, not reset" intact,
each subsystem gains a ceiling that falls permanently with damage — repairs
restore up to what the car is still capable of, never to what it was. Without an
economy the board's rewards had no stakes; with one, the risky job is what keeps
the car running a while longer.
Adds Dijkstra over the road graph, taking a cost function so a "safest route"
preview is a small change later.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Driving alongside a road accrued no heat at all, so the dominant strategy was
to never actually use one. Heat is now credited to any road within 12m of the
centreline, tarmac or verge.
The cutoff is hard rather than tapered: a taper leaves a gradient to optimise
along, where sitting at 80% of the catchment costs 20% of the heat. A cliff
edge means you either use the route or genuinely leave it.
12m is a balance, not a guess. It puts ~7.5m of verge past the kerb — two car
widths, so hugging the shoulder gains nothing — while leaving 57% of the map
off-route. At 20m two thirds of the map counts as on-route, heat becomes
unavoidable everywhere, and choosing a different route stops meaning anything.
Barricades now span the full corridor for the same reason. Stopping at the kerb
left them free to round on the verge at speed.
Junction heat is still deferred; a point near a junction is credited to the
nearest segment.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Roads are a jittered 7x7 grid with ~a quarter of the edges thinned out, kept
connected. The loops are the point: "take a different road" is not a decision
without alternative routes.
Every metre driven adds heat to that segment; all roads shed it slowly. Crossing
a threshold escalates Clear -> Patrol -> Barricade -> Turret, each of which puts
real obstructions on the tarmac. Hysteresis stops a road on a boundary from
rebuilding its barricade every few seconds.
Level changes are deferred while the car is on the segment. Building a barricade
around the player would spawn a static collider inside the chassis, and the
player is meant to discover escalation by returning to a road, not by watching
it assemble behind them.
Checkpoint positions are seeded from the segment id, so a stretch of road always
fortifies in the same place — recognising it is part of learning the map.
Scenery now avoids the tarmac and the car spawns at a junction.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Vite + TypeScript + three.js + Rapier raycast vehicle, fixed 60 Hz step.
Flat plate, seeded obstacle scatter, chase camera, debug HUD. Car condition
(engine/tires/chassis) degrades permanently and is derived into handling
numbers, so decline is felt through the wheel rather than read off a meter.
src/sim/ is kept free of three.js and Rapier imports — the later heat, region
and front-line systems all live there, and staying engine-free is what makes
them unit-testable without a browser.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>