Commit Graph

10 Commits

Author SHA1 Message Date
dejvino
cf5d55bdf0 Look around with the mouse
Hold the right button and drag to swing the view round the car; let go
and it eases back to the road over a moment, the way you turn your head
back rather than being snapped to it. Measured: 9.6m of orbit on a
short drag, and exactly zero drift from centre once released.

Deliberately not pointer lock. Locking the cursor feels better in a
driving game right up until you want to press one of the debug panel
buttons or read the quest board, both of which are ordinary DOM sitting
over the canvas - a game that swallows the cursor to look left is one
you have to escape out of to use its own interface.

The camera orbits the car rather than turning on the spot, so the car
stays in frame and you can still see what you are about to drive into
while looking away from it.

Read at frame rate rather than from the fixed step, since this is the
one input where lag shows up directly as the view dragging behind the
mouse - through a separate accessor, because `read()` clears the
buffered one-shot keys and calling it from the render loop would
swallow whichever keypress landed that frame.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 17:51:57 +02:00
dejvino
d9cdca70d5 Overhaul: something you can do about the ceiling
The ceiling was the only number in the game that moved one way, with no
counterplay at any price. In practice that reads less as a design
pillar than as a punishment you cannot answer - you watch the car get
permanently worse and there is nothing to *do* about it.

Press C at any base to strip and rebuild, worst subsystem first. It
buys ceiling and deliberately nothing else: an overhaul raises what the
car could be, and ordinary repair still has to fill it in. Early on
every part goes to repairs and there is never a surplus, so this is
invisible; it only becomes the thing to spend on once the ceiling is
what is actually holding you back.

The rate is the design, not the impossibility. 0.12 of ceiling per
part, against a full point of condition per part for a repair, so
undoing one crash's permanent cost runs about seven missions. Decline
is now something you can fight rather than only absorb.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 07:52:27 +02:00
dejvino
9e476087d4 A way out of a mission that is not wiping the campaign
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>
2026-08-09 07:14:21 +02:00
dejvino
9eaa05d7ef Districts that remember you, and roads worth staying on
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>
2026-08-08 12:06:59 +02:00
dejvino
9ae00dc978 Sharper steering, solid traffic, and a map that is mostly war
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>
2026-08-07 21:42:17 +02:00
dejvino
62c67ea649 Phase 7: sound, synthesised from the simulation
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>
2026-08-07 21:31:49 +02:00
dejvino
90dc18fd17 Braking, stutter, a real road hierarchy, denser towns, and autosave
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>
2026-08-07 13:28:15 +02:00
dejvino
73e8ac68f1 Phase 2: bases, quest board, and known-vs-new targets
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>
2026-08-07 10:06:32 +02:00
dejvino
506d07be5d Phase 1: road network and heat
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>
2026-08-07 07:47:19 +02:00
dejvino
a66032674d Phase 0: drivable car with persistent wear
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>
2026-08-07 07:39:05 +02:00