Vehicles: owners, locks, keys, boats, garages and armour
Getting a vehicle (Game design document §5.5, §6.3, §12): every car has an owner and a lock, keys are items, a lock can be picked, a level parks cars in its bays, and garages respray and store them. It rests on the items (a key is an item, the interaction key opens what it may) and is written in the mission language. Everything here is simulation (packages/sim: locks.ts, garages.ts, spawners.ts, dormant.ts), so the server decides it and the determinism gate replays it.
Owners
Every vehicle has an owner: nobody, a player (by client id), an NPC (by entity) or a mission (Vehicle.owner, Vehicle.ownerId).
| How a car comes | Whose | Locked |
|---|---|---|
| A player's car at their spawn | the player's (they take its wheel first) | no |
| Ambient traffic, a chaser's car, a police car | its NPC driver's | no |
| A level's vehicle spawner (a parking bay) | nobody's or an NPC's, as the block says | unless the block says not |
| A mission's vehicle actor | the mission's, nobody's or an NPC's (owner) | when the actor says (locked) |
| Taken out of a garage | the player's | no |
Ownership changes hands three ways: taking the wheel of a car nobody owns makes it the driver's, and a player who takes the wheel of an NPC's car makes it theirs (stolen); a car a player carjacks or picks the lock of is theirs from then on, whoever's it was. Another player's car and a mission's stay whose they were when somebody else drives them; a mission's car somebody sits in when the mission ends is its driver's.
Locks and keys
A vehicle's lock is shut or open (Vehicle.locked); its key is any item answering to the car's key tag (SimWorld.locks, usually key:vehicle:<id>: exactly the shape the phone's RentaCar app hands over). Missions check tags, so a mission defines the key of one car as its own item ({ "id": "van_key", "name": "Van key", "tags": ["key:vehicle:van"] }) and gives it, lays it out or hides it like any item; a level's container may hold it too, and so may a guard's pockets (a mission's npc actor's pockets, a level NPC spawner's items), searched off the body once it is down (see Stealth).
The interaction key E at a car (vehicleInteraction in carjack.ts, weighed by interactionOf):
| The car | The prompt | What E does |
|---|---|---|
| open, or the player's own | Get in · Get in as passenger | as always |
| locked, its key carried | Unlock | unlocks it for good and gets in, on the seat it would have taken (the wheel when free): one press, decided so the key feels like a key |
| locked, no key, its wheel free | Locked · hold to pick | a press does nothing; held for three seconds (PICK), it picks the lock: the character stands still at the door, the prompt says Picking… 40% (the server's word), and the car is unlocked and the player's |
| locked, no key, a driver at its wheel | Locked | nothing (riding along needs it open) |
| a driver to pull out (stopped, at the driver's door) | Carjack | a carjack is by force: no lock stops it |
Where several things are within reach, the closest wins and a tie goes by PRIORITY in interaction.ts, the order stealth set: close, pickup, talk, search, wake, use, hack, open, trunk, take out (a stored car, in a garage's empty bay), keypad, open door, close door, locked, carjack, unlock, enter, passenger, pick last, so a held key never picks a lock while something else is offered. A garage's respray and store are not interactions: at the wheel, 1 and 2 on its panel (the input's service).
A locked car's trunk stays shut to whoever it does not open to. The input carries the key held (interact, besides the one-shot action); picking is never predicted (the snapshot carries how far a character has picked, 0 to 1). A pick raises a pick event at the car; the police count it as a crime of 8 Heat when somebody (a pedestrian, an officer) is within 30 m: one pick alone stays under a star. Chasers never walk to a locked car to take it.
On foot, a tag over the car the player looks at (the nearest within 9 m near their aim, or within 3 m) says Locked, Locked · your key or Yours.
Vehicle spawners (parking bays)
A level parks cars with the block:vehicle-spawner block (see the Levels guide): a bay on its node's origin, facing its +z, never scaled. Its script's parameters: class (a class name, or several separated by commas: a mix it goes through in turn), owner (nobody or npc), locked (1 or 0), key (the tag its key answers to; key:vehicle:<node name> when empty), respawn (seconds; 0 never), dormant (1: its cars doze, see below) and wreck (1: its cars stand wrecked). The export lists it under the level's spawners (dormant and wreck only when on).
The server runs the spawners (startSpawners with the world and after a level reset, spawnerSystem every tick before the step; the clients' prediction worlds never spawn anything): a car stands in every bay; once it is gone (driven more than 8 m off, wrecked, removed), the next is put there respawn seconds later, as soon as the bay is clear (no car in it, nobody standing where the car would). A mission's vehicle actor is its own spawner: a class or a mix (each run of the mission takes the next), an owner, locked or not, a key tag; it does not respawn (it goes with the mission).
Parked cars
Every parked car of a level is a real car (decided 2026-10-25: "all vehicles, even in parking lots or part of the assets, destroyable and carjackable"): the generators put a dormant vehicle spawner wherever they used to stand a parked-car prop (cityparts.Placer.park: a car park's bays, a house's drive, a loading dock, a dealer's lot, a depot's yard; a square's food truck is an ice cream van), each an NPC's car, locked, back ten minutes after it is gone. It can be got into with its key, picked open, carjacked when somebody sits in it, shoved, shot to pieces and set alight like any car. A works yard's or a region plot's rusty wreck is a derelict: a wreck spawner's sedan, nobody's, open, standing wrecked (no engine: it is never driven), shoved and set alight by a shot, a blast or a neighbour's fire, never by itself nor by a crash, and never replaced.
A city parks over a hundred of them (main-riverbend 123 of its 134 spawners; a van at a loading dock only where a real van has room), and a car simulated in full costs the server its controller's wheels and its contacts every tick (it never sleeps: a sleeping chassis stops its controller). So a parked car dozes (packages/sim/src/dormant.ts, numbers in DORMANT):
- Freshly parked, it settles on its springs and, once it has stood still half a second, dozes: its body held (kinematic, as a client's proxy is:
proxy.ts), where it came to rest, its wheels' contact as it last was. A held car is solid to everything simulated and found by rays and shots, but the step does nothing for it: no controller, no wheel raycasts, no contacts with the level, passed over by the vehicle, fire and sinking systems and the search for fast pairs (substeps.ts). - It wakes, from where it stands, its parking brake on: before a car, a trailer or a person on foot moving faster than a walk touches it (every simulated mover looks the dormant cars within its reach up on a grid of the ground, 8 m cells as the light pieces', and asks Rapier whether its collider comes within what it covers in two ticks of theirs, plus 10 cm: a slow push wakes it as surely as a ram); when somebody is at it (in it, getting in, carjacking it, picking its lock, its trunk open); when it is hurt or a blast shoves it (
damage.ts'hurtand the fires callwakeVehiclefirst). - It dozes again once it has been left alone ten seconds: still (under 5 cm/s across the ground), nobody at it, not burning, and no player within 60 m.
- Its bay is refilled only out of sight: a dormant spawner waits, besides its respawn time and a clear bay, for no player within 120 m.
Dormancy is hashed and travels in the snapshots (dormant, a flag of the entity's third flag byte, PROTOCOL_VERSION 38): the server sends a dozing car in the slowest tier of the area of interest (it does not move), and a client's twin of one is held the same way (Mirror), so the predicted car meets it as the server's does and wakes it in the prediction too; a twin never dozes by itself. A parked car has its lights off at night, as any parked car (car-lights.ts), and a dozing one is no dot on the minimap nor on the full map (isMapMarker, minimap-data.ts): it shows once woken. The 32-bot load test on Riverbend, before and after: Performance.
Boats
Seven classes float instead of rolling (decided 2026-10-25: "add a drivable motorboat too: a new boat class on the water physics; tugs and fishing boats become boats you can steal"): the rowboat, the motorboat, the fishing boat, the tug, the barge, the pedal swan and the police boat (the ten-star build's) (vehicle-classes.ts, a class with a boat and no axles; how they float and steer is in the driving model). They are vehicles like any other: owned, locked, picked open, carjacked, shot, set alight, their trunk (the fishing boat's hold of six slots, the motorboat's locker of two) opened; but:
- Getting in has no doors: every seat is open, and a boat is boarded where one stands within 1.5 m of its hull (
BOAT_BOARDINGinseats.ts): from a jetty, a quay or swimming beside it, half a second's climb, the swimmer kept afloat meanwhile. The feet may be at most 1.2 m over the boat's deck (nobody drops in from a bridge over it) and 2.6 m under it (a swimmer beside a barge's high side). A boat's driver sits on its left, or amidships at the oars, so a carjack at a jetty works from the boat's left as at a car's door. - Getting out at a crawl puts the character on the nearest dry, free spot beside the boat (a jetty, a quay, a beach: ground no deeper than 20 cm under the water and no higher than the deck allows), tried along the seat's side, then the other, the bow and the stern, a little further out each time; with none, over the side into the water, afloat. Faster, at any speed, one bails out: a jump into the water, no tumble, no harm.
- Spawners of boats (every class of their mix a boat) stand on the water and under its surface:
checkLevelrefuses one off it, and a car's spawner in water deeper than a wade, and a mix of both (BOAT_CLASSESin@foi/world, held to the simulation's boats byboat.test.ts). A boat spawner's boat is put afloat at its draught, moored, and dozes once it has settled, like a parked car. The generators moor real boats wherever they used to place boat props: a city's rowboats and pedal swans along its banks and by its jetties (nobody's, open), its harbour's barges, tugs and fishing boats (an NPC's, locked: to be stolen), a region lake's rowboats and pedal swan by its dock. Vgoes round the boats in a boat (rowboat, motorboat, fishing boat, tug, barge, pedal swan, police boat) and round the classes on wheels in a car: a boat is never put on a road, nor a car on the water.Rrights a boat where it floats; aground, it puts it in the nearest boat spawner's bay.- Fire and water: a boat afloat burns like a car (its deck over the water:
vehicleInWater), the rowed and pedalled ones never (no engine); a boat never catches fire by itself from its crashes. A wreck floods over eight seconds (VehicleState.flood, hashed, carried in a vehicle'sseatbyte on the wire) and sinks; gone under, its people go over the side and swim. - Chasers never take a boat, nor does the Chasers panel offer one; a chaser on foot after a player on the water comes to the nearest bank and shoots from it (nobody plans a path through deep water).
- The client reads a boat's speed in knots on the HUD and names what drives it (an outboard engine, an inboard diesel, oars, pedals); it draws a wake of foam from the stern and spray off the bow by the speed (
boat-wake.ts); it plays an outboard's buzz, a diesel's thump or the oars' strokes (vehicle.boat.*in the sound bank, synthesized placeholders until files come).
Riverbend's river has seven bridges whose decks stand 0.8 m over the water (city.py's SURFACE and DECK): the rowboats pass under them, the pedal swans (their wings), the motorboat and the ships do not, so each sails its own reach of the river (the harbour's basin, the loop round the Old Town, …). Raising the decks to 3 m was looked at (2026-10-25) and left: a lower surface puts the quays and the jetties out of a swimmer's climb and of the boats' boarding reach, and a humped span needs ramps through the junctions at the banks of every city the template builds; levels-riverbend.test.ts pins the clearance.
The armoured and heavy classes
Seven classes appended after the boats (the ten-star build's V1: C8–C11, C17, R5, R6), so the earlier classes keep their indices on the wire; players and the AI drive them as any class (V reaches the six on wheels after the tractor, the police boat after the pedal swan), and stealing the police's own heavy gear is part of the fun. Their numbers are in the driving model; their constants (SWAT_VAN, APC, WATER_CANNON, ARMOURED_LIMO, CASH_VAN, POLICE_BIKE, POLICE_BOAT) are what the police's later phases send.
| Class | What it is |
|---|---|
| SWAT van | "Tactical Wellness Response": an armoured van, black with a mint stripe, a light bar and a siren; eight seats (two in the cab, six on benches behind its rear doors) |
| APC | An armoured battering-ram truck: a steel push plate that shoves parked cars aside, run-flats, bullets do it 10 %; slow at the top, strong off the line |
| water cannon | A riot truck, navy, with a water cannon on a turret over its cab, worked from the cab's second seat (or by the driver alone): 25 m of jet that is never lethal |
| armoured limo | The "presidential" limo: four times a car's hit points, run-flats, too heavy to PIT; slow off the line and thirsty |
| cash van | "Secure-ish Logistics": a cash-in-transit van, gunmetal with a gold band, three times a car's hit points, its strongbox a trunk of eight slots |
| police bike | The motorbike in the police's livery with a light bar and a siren; threads traffic where a cruiser cannot |
| police boat | The motorboat in the police's livery with a light bar, a siren and a small water jet on its bow |
A water jet (jets.ts) builds its pressure half a second with the trigger held before the water comes (a hiss: the pressure flag on the wire), every burst again, and its water douses what burns all along its stream as well as where it lands. At seven stars the police drive these themselves (the APC rams, the water cannon parks off the player and sprays, the bikes ride with the cruisers from four, the boats come onto the water: AI), and fit their cruisers with the Grappler's bumper net (grappler.ts: it hooks a car's rear wheel and drags it to a stop; a car over three tonnes snaps it) and a GPS dart launcher (tracking.ts: a tag stuck on the car, pulled off at its rear with E held three seconds).
Their models are drawn in Blender (assets/blender/models/vehicles.py): new bodies for the SWAT van, the APC and the water cannon, the limo's with its flags, the van's with its band, the motorbike's with its panniers and light bar, the motorboat's with an arch of lamps and a nozzle; their hulls and seats are npm run models:hulls'.
Garages
A level's garage is the block:garage block: a bay on its node's origin (the middle of its floor), scaled to its width, height and depth, its door at its +z end; its script's respray and store are the prices in dollars, −1 for a service it does not offer. The building around it is placed apart (walls of block:building, or a kit's piece): the garage is a behaviour on a door, not a mesh. A game's rules.garages ({ "respray": 200 }) sets every garage's price over the level's; the welcome carries it to the clients.
At the wheel of a car standing still (under 1 m/s) in a garage's bay, whole, the garage's panel (bottom centre) offers what it does with its price; 1 or a click resprays, 2 or a click stores, each paid out of the player's Cash (the one wallet that missions pay into, the cash picked up counts into and the shops sell for: see Items; a price the player cannot pay says so):
- Respray: the car takes the next of eight paints (
Vehicle.paint, drawn over its livery), is repaired and gets new tyres. The police clear the player's Wanted level, the Heat with it, unless an officer sees them or saw them within the last five seconds (they saw the car go in):resprayClearsin@foi/ai'swanted.ts, from theresprayevent. - Store: everyone gets out (the driver where the car stood) and the car is kept for the player in the room (
SimWorld.stored: its class, paint and key), gone from the world. Any car but another player's can be stored; a mission's stored vehicle is gone for its mission (vehicle:<id>.stored). - Take out: on foot in the empty bay of any garage that stores, with a car stored, the prompt says Take out compact (the last one stored): the car is put in the bay facing out of its door, the player's and open, the player at its wheel.
The stored cars travel to their player in the inventory text frame (class and paint); a level reset forgets them.
The mod garage
A garage that resprays fits upgrades too (R13, mods.ts' MOD_SHOP): at the wheel in its bay its panel lists each with what it does and its price, or fitted once the car has it (an APC's run-flats and push plate come with it); 3 to 8 or a click buys one for the room's Cash, as the respray is paid, through the input's service (3 and up, the server's alone).
| Key | Upgrade | Price | What it does |
|---|---|---|---|
3 | Run-flats | $150 | A nail strip only slows the car (a limp of three seconds) instead of popping its tyres |
4 | Bull bar | $200 | Rams harder (1.6 times) and takes less (half) nose first; a parked car it pushes lets go of its parking brake |
5 | Armour plating | $400 | Half again the hit points, bullets do 60 %; a little heavier and slower (the car is rebuilt where it stands) |
6 | Nitro ×3 | $150 | Three charges; C fires one: a two-second burn, its flame scorching whatever is right behind |
7 | Caltrops ×4 | $120 | A rear dispenser of four charges; Z drops the driver's own nail strip behind the car |
8 | Oil ×4 | $100 | A rear dispenser of four charges; Z drops an oil slick that takes a follower's grip for a couple of seconds |
One dispenser a car: buying the other kind gives up the first's charges. The upgrades are the car's (VehicleState.mods, the charges and the timers, hashed): they stay through a reset (R) and a class change (V), into a garage's storage and out again. The HUD's first line shows a car's kit (NITRO ×2, NITRO! while one burns, CALTROPS ×4, OIL ×3, a water tank's meter, LIMPING, OILED); the client draws a bull bar across the nose, the nitro's flame out of the exhaust and an oil slick as a dark sheen on the road (armour-view.ts, items-view.ts). Hardening against an EMP and a hack-proof engine computer are left to the phase that brings the EMP and the kill switch (D), which adds them to the shop with what they protect from.
Missions
- A vehicle actor gains
owner,lockedandkey, and itsclassmay be a list (a mix); agarageactor binds the level's garage nearest a place (within 6 m). - Actions:
lockVehicle,unlockVehicle(what an app's Unlock button does),giveKey(the game's item that answers to the vehicle's key tag; the validator wants one defined). - Events:
vehicle:<id>.unlocked|picked|stored|resprayed,garage:<id>.entered(the player drives or walks into its bay). - Objectives:
unlocktakes a vehicle (done once it is unlocked: its key, a pick, anunlockVehicle);delivertakes a garage as its destination (the vehicle in its bay, the player at its wheel; items handed over there).
See the Missions guide for the whole language; "Repo Season" on test-locks-garages uses all of it.
On the wire and on the server
- The input:
interact(the key held, a flag) andservice(1 a respray, 2 storing; a byte, one-shot): 16 bytes since the weapon left it (the selected slot says what is held). Neither is predicted. - A snapshot's entity:
locked(a flag),ownerKindandownerId,paint(bytes 80 to 83 of 84, after stealth's status and alert),pursuit(a flag: the police's interceptor tune at three stars, see the AI guide); a character's picking share in itsboostbyte. The cars' key tags travel in their ownkeystext frame when they change (to every player: the prompt needs them to tell Unlock from Locked).PROTOCOL_VERSION9. - A vehicle's upgrades ride in the fields of its record only a character uses (
PROTOCOL_VERSION42): its mods inweapon, its nitro and dispenser charges incooking(kitByte), its dispenser incover, its nitro burn's ticks inact, a limp's inalert, the oil's instatus, a water jet's tank ininside(thousandths) and its heading inaim,sprayinga flag of the third flag byte; a laid strip's kind in itscls; the nitro and dispense presses in the top two bits of the input'sslotbyte. The client adopts a car's mods and charges (a car newly plated rebuilt heavier) and predicts its nitro; the dispenser is the server's. - The mirror puts owners, locks, paints and key tags on the cars' twins and the player's stored cars in the local world, so the prompt says what the server will.
apps/server: the game server parks the level's cars and runs the spawners; the mission host spawns, locks and unlocks the missions' vehicles, binds their garages and tells the missions what was done to their vehicles (unlocked, picked, resprayed, stored) and who stands in their garages.
Determinism
Owners, locks, paints, the picking (ticks and car), every player's stored cars (their upgrades too), the spawners' clocks and every vehicle's upgrades, charges, timers and water tank are part of hashSimState; the gate's armoured (an APC with a bull bar and plating shoving a sedan, a lead firing its nitro and dropping caltrops then oil before a follower on run-flats, a water cannon hosing a man and a car) replays them; packages/sim/src/determinism.integration.test.ts replays a lock picked, a respray, a car stored and taken out again, and a spawner putting its cars in its bay, in two worlds that must agree on every tick.
Mini level and tests
test-boats(built fromapps/level-editor/assets/kits/test-boats.recipe.json): a pond 50 m by 80 m and 2.5 m deep in a yard 2.7 m high, a jetty from its south shore with a motorboat moored on its west side and a fishing boat and a rowboat on its east (dormant boat spawners), a slipway into the water from the south shore and a bridge 1 m clear of the water. E2E-860 (the motorboat boarded from the jetty where the player stands, driven 30 m north), E2E-861 (beached on the slipway), E2E-862 (the rowboat passes under the bridge, the motorboat stops at it), E2E-863 (wrecked against the bridge, it floods and sinks; its driver swims);packages/sim/src/boat.test.ts(the envelope, mooring, flooding, boarding, carjacking, fire, spawners),@foi/ai'sboats.test.tsand the determinism gate's rowboat (a swimmer climbing in, rowed in circles, wrecked and sunk).test-armoured(built fromapps/level-editor/assets/kits/test-armoured.recipe.json): the player's spawn on open ground south of a street, an APC parked beside it facing south with a compact across its way 12 m on, a water cannon on its other side facing north with a range dummy 18 m ahead of it and a sedan off its left, a nail strip across the way north of the spawn, a garage off the street with a driveway and $2000 in its bay, a row of light compacts to shove and the armoured classes parked along the street. E2E-990 (the APC shoves the compact out of its lane, @core), E2E-991 (the jet knocks the dummy down and puts out the sedan set alight), E2E-992 (run-flats over the strip), E2E-993 (a bull bar and plating bought), E2E-994 (nitro's burst), E2E-995 (caltrops pop a chaser's tyre), E2E-996 (the limo shrugs off a PIT-style hit), E2E-997 (the police bike), E2E-999 (the armoured classes after the tractor); E2E-998 is the police boat ontest-boats; unit:packages/sim/src/mods.test.ts,jets.test.ts,vehicle.test.ts' armoured classes, the determinism gate's armoured.test-parked-cars(built fromapps/level-editor/assets/kits/test-parked-cars.recipe.json): a car park north of a street, six locked NPC cars of dormant spawners in its bays (a compact, two sedans and a van facing north, a pickup and a sedan facing south), the player's car 10.5 m south of the sedan at −1.3. E2E-840 (a car driven into a parked one wakes it and shoves it), E2E-841 (a lock picked, the car driven off), E2E-842 (one shot until it blows up), E2E-843 (a woken car dozes again once the player is 75 m off);packages/sim/src/dormant.test.tsand the determinism gate's parked car (settled, dozing, then woken and shoved by a car driven into it).test-locks-garages(built fromapps/level-editor/assets/kits/test-locks-garages.recipe.json): a street with three locked cars of the level's spawners (bay-1's key lying on the street by it, bay-2's in a crate behind it, bay-3 a compact then a van with a range dummy watching), a garage with a driveway (a respray for $50, storage free), $200 by the spot the player gets out at (into their Cash); its server runs with police. Its game, Repo Season: a locked van, its key given, delivered to the body shop. E2E-160 to E2E-168 (tests/e2e/specs/levels/test-locks-garages/); E2E-169 is the RentaCar's locked car ontest-phone.test-city-life: gates and barriers (kinds of the door block that open for a driver carrying what unlocks them, see Items), and a garage whose respray is paid out of the same Cash a shop sells for. E2E-303, E2E-304, E2E-306.Unit tests:
packages/sim/src/locks.test.ts,garages.test.ts;packages/world/src/pieces-vehicles.test.ts;packages/ai/src/wanted.test.ts,police.test.ts;packages/missions/src/runtime-locks.test.ts,validate-locks.test.ts;packages/net-client/src/mirror-locks.test.ts;apps/server/src/locks.test.ts;apps/client/src/renderer/locks-view.test.ts.
What comes next
Keys carried in NPCs' pockets (with stealth), hot-wiring and alarms, a player locking their own car, a respray's cost by class, and garages modelled as kit pieces with a roller door. (Money is one Cash now: the garages charge the wallet the missions pay into.)