packages/ai
The NPC brains: the chasers, the guards of stealth, the ambient traffic and the crowd. Isomorphic (no DOM, no Node) and deterministic, but run by the server alone: an NPC perceives, decides, plans within a budget and writes an ordinary input; the character and vehicle systems of packages/sim move it exactly like a player, and the clients see it in the snapshots like anyone else. The model is explained in the AI guide; the numbers live in profiles.ts.
| File | What |
|---|---|
profiles.ts | BehaviourProfile, the one place an NPC is tuned (perception, pace, approach distance, shooting, driving, how long it flees when carjacked, toughness), the presets tail, pursuit, aggressive, guard, and clampProfile; the police officer (POLICE, which boxes a stopped car in) and what the stars make of it (policeProfile: rams from two, gets out and shoots at three). |
pathfinding.ts | One deterministic A* with a budget of expansions (a spent budget gives the path to the node nearest the goal), the navigation grid and the road graph as search graphs, the cost per cell, and the string pulling that cuts the corners of a grid path; the plans the world grants per tick (grantPlan) and their count (planPath). |
perception.ts | Sight (a cone and a ray through the physics world: walls and cars hide people; lineOfSight alive or not, canSee for a live target), hearing (the last tick's shots and blasts), being hit (the shooter revealed). |
photo.ts | What a phone's camera frames (inFrame, PHOTO): a point within its range, within the frame along the character's aim, nothing in between but the subject; the server frames a mission's photograph with it |
steer-foot.ts | Walking a path: planning when due, following waypoints, three rays around cars and people, the stuck recovery (sidestep, jump, a plan around the spot); headings that the chaser writes into its input. |
steer-vehicle.ts | Driving: the route (a clear line, the road graph's lanes from the node ahead or after a U-turn, else the car grid), pure pursuit, the speed plan (bends, corners ahead, the gap behind a target, the end of the route), the ram and the PIT, backing round, the stall recovery. |
chaser.ts | The state machine: idle, alert, chase (leading a moving target, fanning out, keeping the approach distance, shooting in bursts), search, return; down, dead and seated handled; at the wheel the same modes drive; a wreck, a car parked where it can go no nearer a target on foot, or one with the target on foot and near is got out of, by the simulation's rules (a wreck's crew presses to leave and bails out once the car is below 35 km/h, any other waits for a crawl and steps out at its doors); a free car is taken after a target that drives off; carjacked, it flees (startFlee), then chases the carjacker; a profile that boxes in takes a side of a target's stopped car. |
arms.ts | An NPC's weapons as items in its pockets: armNpc (a weapon and a full stack of its ammunition), the slot it holds (npcSlot: its weapon's while the rounds last, else the fists), how near it fires (fireReach) |
awareness.ts | What a sentry notices: sight as a factor (the cone, a ray to the target, cover and crouching, the distance), the sounds of the last step with their radii (shots by the weapon's noise, blasts, breaking light pieces, doors, running), the alert phases and their ranks. |
visibility.ts | How far one sees and hears in the room's light and weather: visibilityAt (the dark of night where the room's sky darkens sight, lifted under a street lamp that stands: one knocked down is out; fog; rain), a share of every sight range (a sentry's, a camera's, a chaser's line of sight, a witness's), and hearingShare (rain masks noise). The crowd thins in the rain (crowd.ts), the traffic slows on wet roads (traffic-drive.ts), a skip of hours puts the people on schedules where their hour has them (schedule.ts's catchUpSchedule). |
sentry.ts | The guard or civilian of stealth (STEALTH, SENTRY): its rounds (patrol or post), suspicion, the phases normal, suspicious, alert, search and caution, the call to the guards near it, a body found, waking a sleeper, chasing and shooting; a civilian flees instead. |
stealth.ts | The book of stealth (StealthState in the AI world): the cameras watching (spotted after 0.6 s, the guards called), the bodies reported, the compound's alert, each player's seen, alerts and kills; stealthView for the server's frames, stealthReadout for the HUD and the tests, alertSentries and calmSentries for the missions. |
system.ts | createAi (the grids, the graphs, the scratch, the budget, the random stream), spawnChaser, spawnSentry, removeNpc, clearNpcs, aiSystem (every NPC in entity order, before the step, the ones the last step carjacked out of their car set fleeing first, a driver of a car with a siren running it, then the traffic and the crowd), hashAiState for the determinism gate (the NPCs, the police's book, the book of stealth, the traffic and the crowd), aiReadout for the HUD and the tests. |
traffic.ts | The ambient traffic (TrafficState in the AI world): setTraffic (how many cars, where they may appear and go), spawnTrafficCar, trafficSystem (run by aiSystem after the chasers: carjacks and gunfire read from the last step, every car driven or taken off the road, the drivers on foot, the cars left behind, one new car out of sight a tick, its class from CLASS_MIX: mostly cars, some taxis and buses, an occasional garbage truck), clearTraffic, trafficReadout, hashTraffic; viewersOf: who the traffic and the crowd are kept around, the players, or the AI world's standIns while nobody is in the room (a room warming up before its first player, apps/server's GameServer.warmUp, puts one at the players' first spawn point; none otherwise). Spawn points of the traffic (cars out of the level's points of that role, or a game's spawn areas) and junctions switched by hand (switchSignal, signalOverrides). |
traffic-drive.ts | One tick at the wheel of an ambient car: the lanes ahead (planLanes: a random way out at each junction), the acceleration it commands (DRIVE: the Intelligent Driver Model toward the road's share and behind what is in front, the braking a bend's cap or a stop needs at its point, the junction's light decided once at the amber or who came first, a box it could not leave; bounded at 2.5 m/s² pulling away, 4 braking, 8 in an emergency, the foot moving at 8 m/s³), the throttle and brake the class needs for it (pedal, inverted from vehicle-classes.ts), pure pursuit (a long vehicle's point never nearer than its axles), the lane change round something that never moves, the stall back-off, the nose-to-nose and in-box jams, when it is stuck; a bus's stops at the kerb, never at a junction. |
crowd.ts | The crowd of pedestrians (CrowdState in the AI world): setCrowd (how many, where they may appear and go), crowdSystem (run by aiSystem after the traffic: the far ones and the shopkeepers gone inside taken off the street, one new pedestrian a tick on a pavement node near the players (or the stand-ins, viewersOf) with a role from ROLE_MIX, a shopkeeper at a free door, up to the number the hour wants, crowdWanted), clearCrowd, crowdReadout, hashCrowd. How each walks is the simulation's (pedestrians.ts). By the hour (CROWD_HOURS, the room clock): fewer people at night (the extra ones going home out of sight), joggers mornings and evenings, shopkeepers in shop hours, commuters to work and home at the rush hours. The city’s routines (ROUTINE_MIX): a third of the newcomers, each at a free place. The dogs (listCrowdDogs, dogReadout): one with every dog walker, a few strays, no people. |
traffic-route.ts | The polyline an ambient car steers along: lane centres joined at the bends, cut at a junction's box and curved across it, a loop at a dead end; the stops on the way (stop line, box exit, turn), the speed each bend allows; locate, pointAt, projectAhead (what stands ahead, how far off the route). |
wanted.ts | The Wanted level: the crimes of the last step's events (gunfire near people, hurting or killing a pedestrian, carjacking, a lock picked in front of a witness, wrecking a car with people in it, breaking property in front of a witness (a light piece knocked loose, an empty car wrecked, a camera shot down), and dearer, hurting an officer or shooting at a police car) and speeding past an officer (speedingCrime), the Heat they raise, a respray clearing it unless the police saw the car go in (resprayClears), and the stars it crosses (10, 25, 45), the cooling out of sight and the stars lost one at a time (15 s unseen at two stars, 10 s at one), the police's book (every player's Wanted level, the units out, the strips laid, the arrests), hashed with the AI. |
police.ts | policeSystem, run before aiSystem: crimes charged (speeding seen by an officer among them), the police's sight and radio, the units sent (one car at one star, two at two, four crews of two at three, out of the player's sight on the roads around them), their officers' profile by the stars and their cars' pursuit tune at three, every officer's pistol, the units that give up and go, the nail strips, the arrests (on foot and still near an officer at one star; knocked down by the police at any); setWantedStars, wantedView, takeArrests, resetPolice. Their units out of the police's spawn points (setPoliceSpawns) or spawn areas when the level or the game has them. |
nail-strips.ts | The police's nail strips at three stars: where one goes (stripSpotAhead: the first junction 40 to 160 m ahead along the road graph, a few metres short of it on the player's road, else the first node past 40 m), laid one at a time ahead of a player who drives, each with its unit (a police car parked at the kerb and an officer who throws the strip, which unrolls across the road), reeled in once passed, after 30 s or below three stars, the unit gone once nobody is near; all of it taken away on a reset |
roles.ts | The NPC roles (GAME_DESIGN §5.4): a driver at the wheel on a route of places over the roads or following an actor, a mission's chase sending a target or a driver off (fleeRole: a target for its refuge, a driver flat out to its getaway), a racer (startRace: its gates in order at full speed, ignoring the lights), a target that runs to its refuge when threatened (a gun aimed at it, shots near it), a VIP that follows the player who comes up to it and gets into their car (spawnRoleNpc, updateRole, rolesReadout; the numbers in ROLES). |
schedule.ts | An NPC's day by the room clock: hours (goto, post, patrol, home, break), the plain one last begun in force and timed ones (for, every: a break, a round) interrupting it (createSchedule, followSchedule, entryAt, onBreak); home walks it to its door and indoors until the next hour, break off guard, its phone out. |
restricted.ts | A game's restricted zones (setRestrictedZones, restrictedSystem): a player entering one raises the guards' alert near it (suspicious at once, alerted four seconds on) or the Heat (trespass, when seen). |
Traffic lights: a plan, not a state. A junction's lights are a fixed plan, a pure function of the level and the tick (@foi/world's signals.ts): which junctions are ruled (a traffic light piece within 20 m), how their approaches split into two groups, and green 12 s, amber 3 s, all red 2 s, the other group's turn, each junction shifted by its own offset. The drivers on the server and the lamps on every client compute it themselves from the level the welcome carries and the tick the snapshots carry: no byte in the snapshot, no message on the reliable channel, the two always agree (the client colours its lamps at the tick it draws the cars at). A compact state in the snapshot (two bits per signalled junction, 25 on Riverbend) would cost little but buys nothing while the plan cannot change; once a mission or a phone app can switch a light (GAME_DESIGN §5.5), the switches go out as reliable events and the plan stays the default.
The budget. Measured on the server running the first hillside (now test-curves; the game it had then, its 32 ambient cars, 24 pedestrians) with eight WebSocket bots driving around for a minute: /health reads averageStepMs 4.6 (the whole tick: AI, simulation, missions, snapshots) against the 16.7 ms of a 60 Hz tick, the AI's ai.averageStepMs 0.56; with FOI_TRAFFIC=0, 2.6 and 0.01. In process (GameServer.tick with eight bots over the memory transport) the tick averages 3.4 ms, 10 ms at worst, against 1.9 ms without traffic. Snapshots carry what is within 250 m of each player (apps/server/README.md); at 20 Hz they are still whole: about 160 kB/s a client with the traffic, 75 kB/s without, before the delta compression of M7.