The robbers' gear
What a player brings to a fight with the police at five stars and beyond (Game design document §7.4, §12): areas that act over time (tear gas, smoke, foam, a ground fire), body armour and what else is worn or taken (a gas mask, the juggernaut suit, an adrenaline shot), and the weapons that make them (a taser, a molotov, smoke and tear-gas canisters, a flashbang, an EMP grenade, a sticky charge, a grenade launcher, an anti-material rifle, a minigun). Every piece is simulation (packages/sim), so an NPC carries and throws it through its ordinary input as a player does, and the police of the later phases (SWAT's gas and flashbangs, the Smart City's drones under the EMP) build on the same calls. Every tool has a tell and a counter: a canister is seen landing and pops before it spreads, a flashbang ticks for a second and a half, a molotov's bottle flies and smashes, the EMP's ring is blue; the mask keeps gas out, smoke blinds both sides, water puts fire out, the suit and adrenaline shrug off stuns.
Zones
A zone (packages/sim/src/zones.ts) is an entity with a centre on the ground, a kind, a reach, the ticks it has lived and lives in all, and whoever made it (a player's client id, 0 for an NPC or the police). It grows to its full reach over its first second and shrinks away over its last; rain (sim.sky.rain at least 0.3) wears gas, smoke and fire out twice as fast, never foam; water (douseAt(sim, x, z, radius), called every step where a water jet's water landed: jets.ts' douses, the water cannon's and the police boat's) puts a ground fire and a burning car out (fire.ts' douseFire) and halves a cloud's reach and its time left.
| Kind | Reach | Lasts | Height | Does to whoever stands in it |
|---|---|---|---|---|
| gas | 5 m | 20 s | 3 m | 2 hit points a second, never the last (tear gas takes down, it does not kill); gassed while in it and two seconds after: a cough once a second (the guards hear it, 12 m), the aim swaying (up to 0.12 rad, a pure function of the tick), the player's screen tinted green and blurred; a gas mask or the suit keeps it all out, an adrenaline shot keeps the head clear (no sway, no blur) but not the lungs; an NPC that is no officer and no range dummy runs out of it |
| smoke | 6 m | 15 s | 4 m | nothing: no line of sight passes through it, either way (sightBlocked(sim, from, to): a chaser's and an officer's eyes, a guard's, a security camera's, a flashbang's flash) |
| foam | 3 m | 25 s | 1.2 m | a person moves at 40 % of their pace; a car's tyres stick (a fifth of the drive, under a third of the grip, and the glue's drag) |
| fire | 3.5 m | 8 s | 2 m | 8 hit points a second (none of it armour's); a car standing in it takes 10 a second as a fire's hits, and fire.ts lights it as a blast's hit would (at 50 hit points or below) |
The server makes zones and the snapshots carry them (a zone entity: its kind, its reach now in centimetres, its seconds left); every client's world holds a twin of each, a proxy at the snapshot's reach (setZone), so the prediction slows the player and glues the player's car in foam as the server does. The harm (the gas, the burns, the gassed status) is the server's alone: a twin only slows. The client draws a cloud of gas as a sickly yellow-green haze of puffs, smoke as thick grey puffs, foam as white blobs heaped on the ground and a ground fire as flames (apps/client/src/renderer/zones-view.ts); a canister pops and a bottle smashes as a zone appears.
Armour and what is worn
Character.armour (0 to 200) is spent before health, in damage.ts' hurt, the one place hits are taken: all of a bullet's, a pellet's or a blow's damage until it runs out, half of a blast's (a rocket, a grenade, a charge, a car blowing up), none of a fall's, a crash's, water's, lava's, a fire's or gas's (armourShare in packages/sim/src/gear.ts). A hit the armour soaks whole takes no hit point. Over 100 worn, its wearer runs at 85 % of their pace. A dead character comes back without armour. The HUD shows the player's armour as a bar beside the health (armour ███░░ 120); a round the armour stops strikes sparks off its wearer and pings.
| Item | Name | Price | Used (F) |
|---|---|---|---|
armour_light | Kevlar-ish vest | $350 | 50 armour on (“certified bullet-resistant by our marketing team”); used up; not with 200 worn |
armour_heavy | Plate carrier | $1100 | 150 armour on; used up |
juggernaut | Juggernaut suit | $6000 | “the CFO's casual Friday suit”: 200 armour, a gas mask of its own, never knocked down (a car, a punch, a dart, a flashbang) until it dies, 70 % pace; it comes off with its armour; used up |
gas_mask | Gas mask | $120 | on, or off again (it stays in the slot): no tear gas gets through, the world's sounds come dulled; dropped or taken, it comes off; drawn on the head |
adrenaline | Adrenaline shot | $150 | “wellness, but faster”: 25 hit points back, and for 8 seconds a quarter faster, no stun and no gas blur; used up |
The pace's parts multiply (gearPace and zonePace): the suit's 0.7 (or heavy armour's 0.85), a minigun's 0.6 in the hands, an adrenaline shot's 1.25, foam's 0.4 underfoot. The snapshots carry a character's armour and whether it is gassed, masked, in the suit or rushing (a flags byte of the gear), so the prediction runs the player at the server's pace and every client draws the mask.
The weapons
Appended to the weapon table (WEAPONS in packages/sim/src/weapons.ts, indices 7 to 16 and 18, the police's GPS dart at 17: never inserted, as the index goes on the wire), each an item of its own (COMMON_ITEMS), with its ammunition an item too.
| Weapon (item) | Kind | Numbers |
|---|---|---|
taser (taser) | dart | 8 m; stuns a person for 4 s, hurt by nothing; quiet (4 m); three charges, each back six seconds after it is spent (Character.drain), no ammunition; non-lethal |
molotov (molotov, its own ammunition) | thrown, on impact | breaks where it lands: a ground fire; never goes off in the hand |
smoke grenade (smoke_grenade) | thrown | 1.5 s after the pin: a cloud of smoke; non-lethal |
tear gas (tear_gas) | thrown | 1.5 s after the pin: a cloud of gas; non-lethal |
flashbang (flashbang) | thrown | 1.5 s after the pin: everyone on foot within 10 m who sees it (no wall, no smoke between) stunned for 3 s, a player's screen whited out and clearing over 2 s; heard 60 m off; non-lethal |
EMP grenade (emp_grenade) | thrown | 2 s after the pin: an emp event of reach 12 m; every car with an engine in reach stalls for 6 s (not an old car with nothing to hack, isAnalog, nor one with a FaradayCore™ ECU), every camera on goes off for 10 s, every drone falls |
charge (charge) | thrown, sticks | sticks to the car it lands on (riding it) or lies where it lands, and goes off 3 s later: a blast of 150 at the centre, 8 m, that hits the car it rode twice over |
grenade launcher (grenade_launcher, frag_rounds) | launched, on impact | a flat arc to where the cursor is (35 m/s), going off where it hits: 60 at the centre, 5 m; one every 0.75 s; frag rounds only (no smoke or gas rounds yet) |
anti-material rifle (anti_material_rifle, heavy_rounds) | hitscan | 120 hit points out to 120 m, one every 1.5 s; true standing still, 0.08 rad of spread on the move; a car wrecked in one shot |
minigun (minigun, belt_rounds) | hitscan | 0.8 s of spin with the trigger held before the first round, then twenty a second, 8 each, 0.07 rad of spread, 50 m; its carrier walks at 60 % |
foam gun (foam_gun, foam_canister) | launched, bursts | a glob lobbed at 22 m/s, one every 1.5 s, bursting where it lands (or after 3 s) into a patch of sticky foam (3 m, 25 s: 40 % of a person's pace, a car's grip); hurts nobody; non-lethal; the police's tool of six stars (M7) |
Everything thrown shares the grenade's hand (cookGrenade): the click pulls the pin, letting go throws it on a 30° lob to the cursor (at most 50 m); a canister, a flashbang and an EMP grenade cook in the hand and go off there if held past their fuse, a bottle and a charge are lit only as they land. What flies its own arc (a bottle, a charge, a launcher's grenade) has no body: a ray along each tick's flight finds what it meets. What bursts on impact goes off a hand's breadth short of it, and a cloud, a fire or a patch of foam is laid on the ground under that point (a person in the way skipped): a glob that splashes on someone or on a car's side leaves its foam at their feet, beside the car. The effects are ordnance.ts' (goOff, flashAt, empPulse): a flashbang's burst and each person it blinded are flash events, an EMP's pulse an emp event, both on the wire (a white burst and a bang; a blue ring and a zap). The pulse downs every drone in its reach itself (downFlyer: the police's, a swarm's, a scout); @foi/ai's tech division reads the emp event to shut its robots down for 10 s, reset a kill switch counting down on a car in reach and fry a jammer for 30 s (AI). Held in a ComplianceBot's claw, a player may still throw (or cook) what they hold: an EMP at their feet frees them.
Statuses. The taser's and the flashbang's stun is status.ts' stunned (the punch's), a knockdown with a reason and a length: the AI's NPC lies still, the client draws the down pose, and the player's camera sways while it lies (screen-fx.ts). Lying down, nobody fires. Gassed is a timer (Character.gassed), hashed and on the wire.
Non-lethal. nonLethal(weapon) says which weapons take people down without killing them: the taser, the tranquilizer, the flashbang, tear gas (and its sting's id, GAS_WEAPON), smoke, the foam gun. The police count a takedown by one as the lesser crime (copTakedown), and keep to their own tasers and foam until the player uses lethal force on them (M7, see AI).
The escape kit
Ways out at eight stars and beyond (GAME_DESIGN §12, "Escape kit & chaos"): items used with the use key (F), tagged escape (kit in packages/sim/src/items.ts; what each does is packages/sim/src/escape.ts, useEscape). The hook and the glide are states of the character (Character.hook, Character.glide, hashed), moved by the character system the same on the server and in the client's prediction; the snapshots carry them as the hook and glide acts.
| Item | Name | Price | Used (F) |
|---|---|---|---|
grappling_hook | Grappling hook | $250 | “vertical mobility as a service”, three charges a slot: aimed at a wall, it bites the edge of the roof or ledge on top of it within 25 m (HOOK) and pulls its user up the wall and over the edge in about a second, no gravity on the way; a charge spent a catch, nothing when it catches nothing (no roof to stand on, a taller roof over it, no room to stand) |
paraglider | Paraglider | $600 | “the last-mile solution”: while falling, or at a roof's edge looking out over it, it opens and flies on at 8 m/s sinking 2 m/s (GLIDER), turning toward where the movement keys point at 1.6 rad/s; no fall hurts under it; it ends on landing or in water; one use |
cash_cannon | Cash cannon | $300 | “trickle-down, but faster”: throws $500 of its user's Cash ahead as ten bundles, 3 to 12 m off (RAIN, a rain event); every pedestrian within 40 m runs to grab one (the crowd, see AI); kept, it needs the Cash each time |
crew_driver | Getaway driver (contract) | $1500 | “zero-emission conscience, full tank”: a driver comes to its user's side, takes the wheel of the car nearest them and waits; the key offers the seat beside them, and they drive away from the police |
crew_gunner | Gunner (contract) | $1200 | a gunner rides along (or walks beside a user on foot) and shoots the police it sees with a pistol, out of the window too |
crew_hacker | Hacker (contract) | $2000 | “a consultant, technically”: rides along, and every 30 s stalls the nearest police car within 60 m for 6 s |
The hook's tell is its line: the server sends a grapple event and every client draws it from the climber to the anchor (apps/client/src/renderer/chaos-view.ts). Its counter is the police's rooftop snipers, who live up there (AI). A paraglider's canopy is drawn over whoever glides.
The sewers (packages/sim/src/sewers.ts) are no item: a level's manhole blocks, linked under the street by their network (a name). On foot on a cover, the prompt says Down the manhole (E). Below, the body is out of the world: nothing collides with it, no ray finds it, the police lose sight of it and the stars cool as unseen. The HUD shows the drain (“Municipal drainage. Pick your manhole.”) and its exits, the other manholes of the network numbered with how far each is; a number key comes up at that one, E at the first. The walk takes the way between them at 7 m/s, at least 2 s (SEWER), and the player comes up beside that cover. The state is Character.sewer, sewerTo and sewerTicks, hashed; the snapshots carry it as the sewer act.
The hired crew is the AI's (packages/ai/src/crew-hire.ts, see AI): a contract used raises a hire event and a hand spawns at its user's side. A hire leaves when its employer is busted or leaves the room, or when it is killed.
Where to get them
- Shops (
GEAR_STOCKinapps/server/src/game-server.ts): the city's tailors sell the vest, the plate carrier and the juggernaut suit, its clothes shops the vest, its chemists and pharmacies the adrenaline shot, its hardware stores the gas mask and the vest, its parcel outposts the gas mask. The escape kit: outdoor, sports and bicycle shops sell the grappling hook and the paraglider, party and gift shops the cash cannon, and bars and pubs the crew contracts (a word in the right ear, a contract signed); Riverbend has bicycle and gift shops and twelve pubs. A level's own shop sells what itsitemssay (test-gear's outfitter: armour, masks, adrenaline). - On the ground, in crates, in pockets: like any item. A game's
rules.loadouthands them out (test-gear's gives one of everything,test-chaos's the whole escape kit); free roam's arsenal does not.
Tests
test-gear(built from its recipe,apps/level-editor/assets/kits/test-gear.recipe.json): a range of dummies north of the spawn (one behind a low wall, one 62 m off), a parked sedan, a corridor 24 m long its camera watches, an outfitter with Cash beside it, and the gear lying about; its game (packages/missions/content/test-gear.json) gives the player one of everything. E2E-970 (core: a smoke grenade's cloud) to E2E-979.- Unit tests:
packages/sim/src/zones.test.ts(every zone's effect, rain, dousing, the twin),gear.test.ts(armour's order of damage, the vests, the suit, the mask, adrenaline, the pace),ordnance.test.ts(every weapon's numbers, the sticky charge, the EMP, the flashbang behind a wall),packages/ai/src/gas.test.ts(smoke and the eyes, NPCs out of the gas, the cough heard); both determinism gates throw the gear about. test-chaos(built from its recipe,apps/level-editor/assets/kits/test-chaos.recipe.json; see Testing by hand): a house to hook onto, three manholes of one network, the chaos events' blocks and shops; its game (packages/missions/content/test-chaos.json) puts the escape kit in the pockets. E2E-1110 (core: the hook) to E2E-1114 for the kit. Unit tests:packages/sim/src/escape.test.ts(the hook's pull and its misses, the glide off a roof and in a fall, the sewers and their number keys, the cash cannon, a contract spent, a hired driver never carjacked, two worlds agreeing),packages/ai/src/chaos.test.ts(the crowd's rush for Cash, the three hires); the simulation's determinism gate hooks, glides and goes down a manhole.