Skip to content

AI humans (brains, goals and actions)

Verified against: SLUS_212.15 (NTSC-U, SHA1 e9cb2cc49aa046b9e494313dce2f5038ed17b2f4). Static reading in Ghidra (2026-10-05); every claim is confirmed (code) at the cited address unless it says otherwise. The script calls of level99 were read from the disc's compiled scripts with coney-tools (ids, names and values only). The runtime checks were run in PCSX2 2.9.94 the same day (Runtime checks); their results are in the body, marked confirmed (runtime).

Purpose

How a human that no pad controls decides what to do and does it: the per-human brain, its goal stack, its reaction goal and its action queue; how a brain picks a target, chooses to attack, block or move, and how it moves; and which brains the first mission (level99) uses. With Tasks, it is what the next milestone needs: other characters that move in a level and fight back.

In one paragraph: every human has a brain (0x2f0 bytes) with a type taken from its character class (0 the player, 1 cops, 2 gang soldiers, 3 the Warriors, 4 civilians and bums, 5 dealers, 6 one civilian kind). Each brain has three handlers chosen by its type: an update run every character step, a think run one step in five, and an event handler. For every AI type the update is the same function: run the reaction goal if the human's state calls for one (grabbed, knocked down, stunned...), else the top goal of a stack of up to ten, then the front action of a queue of eight. Goals are long-lived intentions (fight this human, walk to that flag, block for a while); actions are short steps (attack once, move to the target, play a clip). An attack action does not animate anything itself: it writes a command id, the same id a pad press produces, into the human's per-player record, and the human's dispatcher plays it exactly as it would for the player (Tasks). The fight goal paces attacks with tokens, attack slots and a per-class delay, and picks each attack by weighted chance from the class's 45-entry attack table (Att_* in config_preload2.lua).

Original structure

The brain, goal and action code lies in Human/, in the stretch after cns/cnsplayertag.cpp (0x0028a360-0x00306630 at least, Source map, inferred); no path string names its files. The script bindings are on AI bindings. Names are ours.

Address Name Role Evidence
0x0021d408 / 0x0021d3e8 Human_GetBrain / Human_GetPlayerRecord brain 0x006d53f0 + i × 0x2f0, per-player record 0x00660f50 + i × 0x2c confirmed (code)
0x00293b28 Brains_Update step 5 of Humans_Update confirmed (code)
0x0028f6c0 / 0x0028f8b8 Brain_Think / Brain_Update call handler B / handler A confirmed (code)
0x0028f928 Brain_OnEvent perception, reaction goal, then handler C confirmed (code)
0x0028a360 Brains_RegisterTypeHandlers fills the per-type handler tables confirmed (code)
0x0028c1a8 Brain_InstallHandlers gives a brain its type's handlers confirmed (code)
0x0028fbb0 Brain_UpdateGoals handler A of every AI type confirmed (code)
0x0028f2b0 / 0x0028f240 Brain_UpdateReactionGoal / Brain_EndReactionGoal confirmed (code)
0x0028d758 / 0x0028d7d8 / 0x0028d910 / 0x0028d960 Brain_PushGoal / _PopGoal / _ClearGoals / _FindGoal the goal stack confirmed (code)
0x0028d9f0 / 0x0028da60 / 0x0028db20 / 0x0028fe28 Brain_AllocAction / _PopAction / _ClearActions / _RunActions the action queue confirmed (code)
0x00293950 / 0x002939b0 Goal_Alloc / Goal_Free confirmed (code)
0x0029ed58 / 0x0029ee30 / 0x0029edd8 / 0x0029eea0 / 0x0029eed8 Goal_Start / _Resume / _End / _Suspend / _Process confirmed (code)
0x0028d2e8 Brain_Fight enemy, target and fight goal confirmed (code)
0x0028cfe0 / 0x0028df30 / 0x0028de48 Brain_SetTarget / Brain_ClaimAttackSlot / Brain_HasAttackSlot confirmed (code)
0x0028d538 / 0x0028d190 Brain_AddEnemy / Brain_PushFightGoal confirmed (code)
0x0028e708 / 0x002911f8 Brain_PickAttack / Brain_GetAttackWeight confirmed (code)
0x0028e248 Brain_QueueAttack queues one attack kind as one or more attack actions confirmed (code)
0x002b2c20 / 0x002b3ab0 FightGoal_Init / FightGoal_Process goal type 0xf confirmed (code)
0x0029f098 Goal_TryBlock confirmed (code)
0x002b54d8 / 0x002b5520 / 0x002b5808 BlockGoal_Init / _Start / _Process goal type 0x1b confirmed (code)
0x002fa918 / 0x002fa9a8 / 0x002fad70 / 0x002fad30 AttackAction_Init / _Start / _Update / _Abort confirmed (code)
0x00231090 / 0x00231198 AttackKind_ToCommand / AttackKind_ChainDelay confirmed (code)
0x002fb9e8 / 0x002fc5c0 MoveAction_Init / MoveAction_Update confirmed (code)
0x002fcf50 MoveToHumanAction_Init confirmed (code)
0x00147ef0 PlayerRecord_SetCommand writes per-player +0x20 confirmed (code)
0x00223628 / 0x002235f8 / 0x00223800 Human_BlockChance / Human_CounterChance / Human_AttackDelay power-class reads confirmed (code)
0x0029a8c0 / 0x00251a70 / 0x0029aa88 Route_Request / Route_AStar / Route_Follow path planning confirmed (code)
0x00289138 Human_SteerAroundHumans steering confirmed (code)
0x002da3b0 / 0x002da588 MoveToFlagGoal_Init / _Process goal type 1 confirmed (code)
0x0016d170 / 0x00164c20 Gangs_Update / Gang_OnEvent gangs confirmed (code)
0x00306690 / 0x003067d8 Tactic_Start / Tactic_Process tactics confirmed (code)
0x00293c68 / 0x002956d0 Formations_Update / Formation_Plan formations confirmed (code)

The small helpers after Scripting/ScriptUtilities.cpp's anchor (0x003865d8-0x00386b30, before StringTable/), used mostly by the AI and the player's targeting. That they are the rest of ScriptUtilities.cpp is inferred from position (no stub lies between its anchor 0x003864e0 and StringTable/). Names ours.

Address Name Role Evidence
0x003868d0 SortByKeyAscending(items, n) qsort of 8-byte {item, float key} pairs, smallest key first (comparator 0x00386860): "the nearest wins" in the targeting code (22 callers) confirmed (code)
0x003868f8 SortByKeyDescending(items, n) the same, largest key first (comparator 0x00386898); used by Human_TeleportNear confirmed (code)
0x00386920 SortRecordsByKey(items, n) the same order on 32-byte records with the key at +0x04 (Player_PickTarget) confirmed (code)
0x00386950 KeyList_Contains(items, n, item) whether any 8-byte pair's first word is item (Brain_ScanEnemies) confirmed (code)
0x003865d8 Points_Cluster(radius, points, n, callback) sorts pointers to points by x then y (comparator 0x003867f0) and groups each point with those within radius of the group's first point (an x-window, then the squared distance), calling callback(group, count) per group; 0x00320c68 clusters a zone's live objects within 1 m confirmed (code)
0x00386798 Heading_RelativeTo(object, point) the heading from the object's position to the point minus the object's own heading (0x00335f48 of its transform +0x10), wrapped to 0-2π (0x00335d08) confirmed (code); argument roles inferred
0x00386980 HashTable_Construct(table) an SGI-STL hash_map with at least 100 buckets: the next prime of the list at 0x0057dfd8 (193), buckets zeroed, no elements; for Credits_Load (0x0050d33c) and the Rumble character data map (0x001f1748) confirmed (code)
0x003867f0 / 0x00386860 / 0x00386898 the comparators (made) x-then-y ascending; key ascending; key descending confirmed (code)

Data

The brain

60 brains of 0x2f0 bytes at 0x006d53f0, one per human slot. Confirmed (code) at the accessors cited and the script bindings on AI bindings; meanings marked inferred come from how the field is used.

Offset Type Meaning
+0x00 ptr the human
+0x04 int type (below)
+0x08 u8 enabled: Brains_Update skips a brain with 0
+0x09 u8 "dead" (BrDead): only the script's goals and actions run (Handlers)
+0x0a u8 suspended (BrSuspend)
+0x14 / +0x1c / +0x24 PMF handlers A (update), B (think), C (event), each {s16 delta, s16 vindex, fn}
+0x2c s8 the goal stack's top index (-1 empty)
+0x2e / +0x2f s8 the action count / the front action's slot
+0x30 u32 the time of the last update (ms)
+0x34 int update counter (every 90 updates 0x0028bec8)
+0x38 int think counter
+0x3c ptr the reaction goal (below); 0 when none
+0x40 ptr[10] the goal stack
+0x68 ptr[8] the action queue, circular
+0x90 / +0x118 vec4 / float the point a move aims at / its radius (Moving)
+0xa0 struct steering state (Steering)
+0xe0 struct route state (Path planning)
+0xf8 struct perception, refreshed at each think (0x00298f88)
+0x120 u8 targetable / attack-slot flags
+0x124 handle the target
+0x110 / +0x114 float the heading to move along / the speed (Moving)
+0x12c float field of view (BrSetFOV), radians: 1.92 (110°) when made; civilians π/2, dealers 2π, Warriors π
+0x130 float sight range: 30 m when made; civilians 15 m, Warriors 55 m (The enemy scan)
+0x134 / +0x138 float hearing radius: 50 m, and 20 m for event 20 (Brain_IsInHearRange 0x002935d8)
+0x13c / +0x140 float melee range near / far (BrSetMeleeRange): 3.0 / 5.0 m for every brain (below)
+0x144 u32 the enemy-scan interval: 2000 ms, cops 1000 ms
+0x15c / +0x160 fn / u32 the enemy-scan filter / the time of the last scan
+0x14a, +0x14b u8 attack spacing
+0x152 u8 hostiles: how many brains hold this human in their enemy lists, on every brain; raised by Brain_CountHostile 0x0028ef20 (from Brain_AddEnemy 0x0028d538 and the enemy scan), lowered by Brain_UncountHostile 0x0028ef30; a player's is also recounted every 300 updates (Fight mode)
+0x164 handle[16] the enemy list
+0x1a4 handle[16] the attack slots taken on this human; the maximum at +0x1e4 (BrSetNumAttackSlots)
+0x1e8 u32 the earliest time this brain may attack again (ms)
+0x1ec u32 the earliest time this human may be attacked again (ms)
+0x1f0 handle[4] active attackers
+0x200 u8 an attack is coming (set by the attacker's warning, cleared each update); the block's trigger
+0x208 ptr an attack-weight table that overrides +0x298
+0x20c ptr the human's gang (Gangs)
+0x212 s8 the formation this human follows in, −1 none (Formations)
+0x21c int threat response (GangSetThreatResponse): 0 never fights; 2 when the brain is made (0x0028a570, from Human_Init, the store at 0x0028a878); the civilian, dealer and shopkeeper thinks set it to 0 once no fight goal remains (Brain types)
+0x220 int damage response
+0x265 / +0x266 / +0x267 u8 wants a weapon / a hat / reacts to violence
+0x26c int ped type
+0x284 int why the last move failed: 1 no route, 2 or 4 an edge, 3 stuck
+0x298 u8[45] attack weights, one per attack kind (BrSetAttackWeight; filled from the class's Att_* table)
+0x2d1 / +0x2d2 u8 world-flag use allowed / a chance byte (1 / 10 when made; no reader of +0x2d2 found, World flags)
+0x2d4 / +0x2d5 u8 hidden in shadow (Brain_SetHiddenInShadow) / sees a hidden human within 2 m (The enemy scan)
+0x2d8 u8 join the player's gang

Types

Human_Init (0x00218008) sets the type from the character class's byte +0x11a (CfgChar's second argument, behaviour on Characters); a human with +0x19d set gets type 2 unless its class says 3. Human_MakePlayer (0x00229c40) sets 0; giving the human back to the AI (0x0022a2a8) sets the class value again. Brains_RegisterTypeHandlers (0x0028a360) fills the tables at 0x00715530 (A), 0x00715568 (B) and 0x007155a0 (C):

Type Who (classes on the disc) A: update B: think C: event
0 the player 0x003035d8 0x00303260 0x00303e90
1 cops (20) 0x0028fbb0 0x00300678 0x00302340
2 gang soldiers and thugs (249) 0x0028fbb0 0x00304608 0x00304fa8
3 the Warriors (48) 0x0028fbb0 0x003052f0 0x003063b0
4 civilians and bums (116) 0x0028fbb0 0x002fef40 0x002ffb30
5 dealers (15) 0x0028fbb0 0x00302cd8 0x00303178
6 the civl_co_di kind (441): shopkeepers, below 0x0028fbb0 0x00303f10 0x00304228

So type 3 is the Warriors' brain, which an ally Warrior runs (this answers Combat's "brain type 3"). The player's update 0x003035d8 only keeps books (target validity, attackers counted, the follower check 0x002d09f0); it pushes no goals.

Goals

Goals come from a pool of 170 of 0x90 bytes at 0x006e0430 (bitmap 0x006ceac0). Base fields: +0x00 the brain, +0x04 started, +0x05 resumed, +0x08 the time limit (−1 none), +0x0c vtable. The constructor stores a duration in +0x08; Goal_Start (0x0029ed58) adds the time it starts, and Goal_Process (0x0029eed8) ends the goal (returns 2, its Process not called) once the game time has passed it; −1 never passes. Confirmed (code) at both addresses; this corrects the earlier reading "wait-until time". Vtable function words: +0x0c the type id, +0x14 the class's name (a string, such as MoveToFlag), +0x1c destroy, +0x24 Start, +0x2c End, +0x34 Resume, +0x44 Process, +0x4c event (default 0x0029ef80). There are 149 goal classes, types 0-158 with ten unused, all on AI goal types with the bindings that make them; confirmed (code), each vtable read. The ones this page uses (names the game's):

Type Goal Constructor Notes
0x01 MoveToFlag 0x002da3b0 (vtable 0x00542130) GoalMoveToFlag
0x06 MoveToHuman 0x002dc4f8
0x08 Melee 0x002ade10 (vtable 0x00540450) below; Process 0x002aebf8
0x0b EngageEnemy 0x002af5b0 (vtable 0x00540330) the run-in, below; Process 0x002afa48
0x0f Fight 0x002b2c20 (vtable 0x005402d0) below
0x12-0x1a reaction goals below
0x1b Blocking 0x002b54d8 (vtable 0x0053feb0) below
0x1f GrabTarget 0x002bb458
0x21 PlayAnimation (a scene) 0x002e4980 pushed by GoalAddressPerson (rows)
0x22 PlayDynAnimation 0x002d2eb0 GoalPlayDynAnimation
0x29 JoinCinematic 0x002e53e0 (vtable 0x005421f0) GoalJoinCinematic (0x002e5300); Process 0x002e5618
0x30 TrackHuman 0x002df250 GoalTrackHuman
0x36 HoldPosition 0x002be640 (vtable 0x00540510) GoalHoldPosition (0x002be590), and the tactic code at 0x00313718; Process 0x002be818
0x41 FindEnemy 0x002c0430 (vtable 0x00540a50) below; Process 0x002c0748
0x4f BumLogic 0x002abef8 GoalBumLogic
0x57 AddressPerson 0x002cc408 GoalAddressPerson
0x69 / 0x73 Pedestrian / Patrol 0x002aae30 / 0x002c1338 FlagNetTraverse
0x6b / 0x72 PedReaction / Hostile 0x002aa558 / 0x002d55d8 pushed by the civilian think
0x80 Dealer 0x002c6d90 GoalDealer
0x85 BigFighter 0x002e9cd0 (vtable 0x00542940) GoalBigFighter (0x002e9c60); taunt, guard, attack, pick up (rows)
0x9b Backoff 0x002d92e8

Actions

A pool of 100 actions of 0x50 bytes at 0x006e63d0 (bitmap 0x006cead8). Base fields: +0x00 the brain, +0x04 s16 delay in ms before it starts (-1 = a random 0-500), +0x06 started, +0x08 vtable. Vtable function words: +0x14 Start (2 = already done), +0x1c Abort (0 = refuses), +0x24 destroy, +0x2c Update (2 = done). The base update 0x002f9e48 counts the delay down against the brain's +0x30. Brain_ClearActions stops at an action that refuses to abort, so an attack in progress is never cut by a new goal.

A normal end runs Abort too. When the front action's Update returns 2, Brain_RunActions (0x0028fe28) pops it with Brain_PopAction(brain, 0) (0x0028da60). That goes through Action_AbortAndDestroy (0x002f9de0), which calls Abort (+0x1c, the second argument passed through, here 0) whenever the started byte +0x06 is 1, then the destroy (+0x24), and frees the action. So every class's Abort is also its "ended" hook, and its clean-up (a restored boost, a released control) runs however the action ends. Confirmed (code) at the three addresses. An action whose Start returned 2 never set +0x06, so only its destroy runs.

Attack kinds and commands

An attack action carries one of 45 attack kinds; AttackKind_ToCommand (0x00231090, jump table 0x0055c170) turns it into the command id written to the per-player record (Commands):

Kinds Command
0, 32, 43 0x10 (cross)
1, 10, 31, 42 0xf (square)
2, 3, 6 0x12 (cross pressed, a chain step)
4, 5, 7, 27, 28, 38, 39 0x11
8 / 9 0x14 / 0x13
11 / 12 / 13 0x36 / 0x37 / 0x38
14, 21 0xe (circle held: tackle)
15 0x24
16, 26, 37 0x22 (cross + square)
17 0x23 (circle + cross)
18 0
19 / 20 0x20 / 0x21 (charge / dive)
22, 25, 29, 30, 33, 40 0xd (circle tapped: grab)
23 0x39
24, 35 0xf or 0x10 at random
34, 44 0x19
36 5 (L2 held)
41 0x31

Brain_QueueAttack (0x0028e248) turns a chain kind into several actions, each later press delayed by AttackKind_ChainDelay (0x00231198: the first event time of clip 11, 12 or 16, 0x001017a0), so the AI presses inside the chain window as a player would: kind 2 queues kinds 0 then 2 (X1, XX2), kind 6 or 8 queues 1, 5 and 6 or 8. The kinds follow the damage table's indices (0 X1, 1 S1, 2 XX2, 3 SX2, 4 XS2, 5 SS2, 6 SSX3, 7-9 SSS3 and its holds, 10 the snap); inferred from the commands and the queued chains. Every kind with its delay, command and anim ids, and every attack table: Attack kinds and tables.

Behaviour

The update

Brains_Update (0x00293b28), step 5 of Humans_Update, for each of the 60 brains that is enabled (+0x08) and whose human passes 0x0023d790:

  • Think (Brain_Think, handler B) when index % 5 == (counter >> 1) % 5: each brain thinks once every five character steps (6 Hz), staggered across brains. Confirmed (runtime): a hook on 0x0028f6c0 over 215 steps matched the formula on every step, with counter the tick counter 0x005104f4; brains whose human fails 0x0023d790 (in the street save, Vermin and the suspended Ash) do not think at all.
  • Update (Brain_Update, handler A) every step.

Then the waypoint queues. Formations (step 2) and gangs (step 4) run before the brains.

Handlers

Brain_InstallHandlers (0x0028c1a8): a brain with +0x09 set ("dead", BrDead) gets A = Brain_UpdateGoals and B, C = empty stubs (0x004edd40 returns, 0x004edd48 returns 0): no think handler (the type's B, which pushes the type's own goals, Think handlers) and no events, but A is the same update every live type uses, so the goal stack and the actions still run (one update; its skip tests read the gang's +0xd4 and the brain's +0x0a, never +0x09). BrDead itself (Brain_SetDead 0x00292330) only clears the actions and sets the byte (Brain_SetHeld, which reinstalls the handlers); goals already on the stack, such as a follow goal, go on running and queue new actions. To freeze a human a script also flushes it (BrFlush) or suspends its gang (GangSuspend, as kinghill does to its non-player gangs). Confirmed (code); a type-0 brain set dead also gives up the pad (per-player +0x1b = 0). Otherwise A, B and C come from the type's tables. level99 makes its enemy gangs dead (GangBrDead(true)) while it scripts them, then lifts it for the fights.

Brain_UpdateGoals: one update

Brain_UpdateGoals (0x0028fbb0), in order:

  1. Bookkeeping (0x0028ac18; for types 1 and 2 0x0028fff8; 0x002920e0; 0x0028bbf0).
  2. Skip (only the time is kept) while the human is airborne, its gang is suspended (gang +0xd4), or the brain is suspended and not held.
  3. Every 44 updates, a line-of-sight check on the target.
  4. The reaction goal (Brain_UpdateReactionGoal, below). When it returns 2 (none active), the goal stack: Goal_Process on the top goal; 2 = done (pop it and process the new top in the same update), 0 = stop for this update, 1 = run again.
  5. The actions (Brain_RunActions, 0x0028fe28): start the front action when its delay is over, update it, and pop it when it returns 2.
  6. The join-the-player's-gang flag.

A goal is started (or resumed after the goal above it was popped) only when the action queue is empty (Goal_Start 0x0029ed58, Goal_Resume 0x0029ee30). Brain_PushGoal (0x0028d758) suspends the old top (0x0029eea0), clears the actions and pushes; Brain_PopGoal (0x0028d7d8) ends and frees the top and, when the stack is then empty, sends the human event 0xd. BrFlush clears both.

Reaction goals

The brain's +0x3c holds a reaction goal that has priority over the stack. Brain_UpdateReactionGoal (0x0028f2b0) makes one from the human's state word (Combat) when none is active, and Brain_EndReactionGoal (0x0028f240) ends it and resumes the stack's top:

Goal When the state has Constructor
0x16 0x20000 0x002b6dc0
0x12 grabbing, 0xc0 0x002b5a98
0x13 tackling, 0x400 0x002b65f8
0x14 grabbed 0x30 or mugged 0x200 0x002b6818
0x15 tackled, 0x800 0x002b6b88
0x17 knocked down, 0x80000 0x002b49f8
0x18 stunned (0x100000) and not down 0x002b4b88
0x19 0x4000 0x002b4c00
0x1a 0x10000 0x002b5110

The stack's goals 0x1f, 0x92, 0x89 and 0x4f, and the character classes 0x77, 0x78, 0x4f, 0x80, 0xb2, 0x87 and 0x62, can hold it off. This corrects Combat: +0x3c is the reaction goal, not a tactic, and type 0x17 is the knocked-down goal, which is why a downed AI never counters.

Confirmed (runtime), a civilian fighting the player: the knocked-down goal 0x17 (vtable 0x00540210) appeared on the update after the state word took 0x80000 and stayed until the update after it cleared (88 updates), then the fight goal resumed from the stack; the stunned goal 0x18 came with 0x100000, the tackling 0x13 and grabbing 0x12 goals with the civilian's own tackle and grab. So the reaction goal lags the state by one update, because the brains run before the human's state update (Tasks).

A knocked-out human's brain stops (there is no reaction goal for state 0x40000). Confirmed (code) at the addresses cited:

  1. Human_KnockOut(h, 0) (0x00230328; from Human_OnHealthOut, HuSetConscious, Human_Destroy) sets state 0x40000 and, unless the human is already dead or wounded (0x100050000), first calls Brain_OnKnockedOut(brain, 0) (0x0028c3b0). Human_ReleaseInstance passes 1 and skips the brain part.
  2. Brain_OnKnockedOut ends the reaction goal, pops every goal (Brain_ClearGoals: each goal's End runs), clears the actions, drops the target (Human_DropTarget → Brain_SetTarget(brain, 0, 0), which releases his active-attacker place (Brain_ReleaseActiveAttacker, target +0x1f0) and attack slot (Brain_ReleaseAttackSlot, +0x1a4) on the old target, and restores that target's spacing when no slot holder is left), and, being knocked out, empties his enemy list and disables the brain (Brain_ClearEnemies: brain +0x08 = 0). It then sends his human event 0x12 with his foe.
  3. Brains_Update (0x00293b28) runs a brain only while +0x08 is set, so a knocked-out human neither thinks nor updates until Human_WakeUp(h, 0) clears the state and calls Brain_EnableAndResume. Its per-human gate Human_ShouldThinkThisUpdate (0x0023d790) guards both the think and Brain_Update; it is a cull, not a health test: it is false only for a human with no held flag, not paired or blocking, not in a scene state, at gait 0, whose brain +0x2d7 is clear and who is busy with a scene or whose gang has a flag in +0xd8's high half, when the human was handled within 500 ms (+0x2a0) and is off screen (model drawn more than 1 s ago, or no model and +0x334 ≥ 60).
  4. The claims do not test the holder's health: Brain_ClaimActiveAttacker (0x00291008) and Brain_ClaimAttackSlot only prune dead handles (HandleArray16_PruneDead). A downed holder is released by his own knock-out (step 2), not evicted by the claimants.

A wounded human (0x10000, Human_StartWounded) is not knocked out: it takes the Wounded reaction goal and keeps its brain.

The fight reaction goals

Each runs as the reaction goal while its state lasts, and ends (2, actions cleared) when it clears. They pick with Human_CanStartAttack and queue with the kind's chain delay (AttackKind_ChainDelay) unless said. Confirmed (code) at each address.

  • Grabbing (0x12, the grabber).
    • Start (0x002b5ad0) rolls a hand-over flag +0x14: Random_Int(100) < the gang's CfgGang value 5 (+0x6f) × 10. A FollowAndDefend (0x35) or HoldFlag (0x7d) goal on the stack clears it.
    • Process (0x002b60c0):
      1. Not grabbing: done.
      2. Near a train (Brain_IsNearTrain): write command 5 every update. In Player_UpdateGrabbing command 5 (L2 held, PlayerCmd_IsLetGo 0x0027bf08) lets go: grabber 95, victim 94 (Combat). Confirmed (code).
      3. The target is the held man (+0xc4). While actions are queued, wait.
      4. Presenting him. This happens in a rear grab when the nearest player is friendly to A, is not busy, his actions are not blocked and he is within A's near range (brain +0x13c, 3 m), and player 1 (game state +0x228) exists and his crew is not under the Defend command: his current Warrior command, game state +0x41a + his player index (human +0x1b0), is not 2 (Warrior commands; written by GameState_StoreWarriorCommand 0x0041c4c8). The timer +0x10 (now + 3000 ms, set on the first such update) must be unset or not yet run out. Confirmed (code); the command byte is read at 0x002b622c.
      5. A front grab with the flag, when the victim has two or more slot holders: queue a set-command action (SetCommandAction_Init(action, B, 0x19, 0x21): command 0x19 after a 33 ms start delay, the base field +0x04) and wait. Command 0x19 is the grab spin (PlayerCmd_IsGrabSpin 0x0027bd98, as R1 pressed): front to rear, 78 / 79 (0x0026d570), into the rear hold 84 / 85 (Combat). So he turns the victim round, and step 6 then holds him up facing the others. Confirmed (code) at 0x002b60c0 and 0x0027f3b0.
      6. A rear grab with the flag, when the victim has a number of slot holders other than 1, or while presenting. Find the nearest player among the victim's slot holders, else the nearest of his active attackers, and turn to face him (TurnToPointAction). Wait. (Holding him up for a friend to hit.)
      7. Otherwise pick a kind and act on it:
        • 45: wait;
        • 24 (a strike in the grab): 40 % of the time two kind-24 attacks, the second after the chain delay;
        • 25 or 29 (the throws): the stick angle from Grabbing_PickMove;
        • 26-28 (power strikes): no angle.
    • Grabbing_PickMove (0x002b5b98) picks a direction d (0 right, 1 ahead, 2 left, 3 behind the grabber). The angle returned is atan2(forward.y, forward.x) + d × 90° − 90°, wrapped to [0, 2π): the mathematical angle from +x, anticlockwise, so d = 0 is a quarter turn clockwise from his forward, his right (in Coney's headings, θ anticlockwise from +y: θ + d × 90°). The sector mappings agree: a flag on A's right (sector 2) sends him to 2, away from it. Confirmed (code) at 0x002b5fe8-0x002b6040. The first rule that gives one wins:
      1. With a HoldFlag goal (0x7d), away from its flag: flag in A's sector 0, 1 or 7 → 3; 2 → 2; 3-5 → 1; 6 → 0.
      2. When the grabber's power class has byte +0x39 = 1, a random choice among the walls next to them. The power class is the CfgPowerClass record at 0x006619a0 + class × 0x44, read through Human_GetClassRecord (0x00222b78); +0x39 is its argument 22, clamped to 0-1. The walls: the victim's sector 4 → 1, the victim's 6 → 0, the victim's 2 → 2, A's sector 4 → 3. Each test is Sectors_IsWall (0x0029ea10). That is the sector wall bit (0x4) from the lazy wall probe (Neighbour sectors): a straight walk 1.5 m out that Human_CanWalkStraightTo refuses on the navigation mesh, not a collision ray. Both sector sets are refreshed when older than 1 s (Brain_GetSectors(…, 1000)). Confirmed (code) at 0x002b5cf0-0x002b5ef0. Failing that (no wall), a random choice among the same four, each taken when the sector's flag 1 is set (someone within 1.5 m), its nearest human resolves and Brain_IsFriendly(A, him) is 0: the victim's sector 4 → 1, the victim's 6 → 0, the victim's 2 → 2, and A's sector 4 → 3. The last tests A's sector 4 flag but the friendliness of the victim's sector 4 human (it reloads the victim's handle, 0x002b5ea8, a0 = victim record + 0x20), a slip a faithful port keeps. Confirmed (code) at 0x002b5db4-0x002b5ee0. (Into a wall or into his mates.) This fallback, like the walls, runs only when the power class's +0x39 is 1.
      3. With the gang's tactic of type 2 (Defend), away from the defended human, as in 1.
      4. Otherwise Random_Int(3): right, ahead or left (0-2), never behind.
  • Mounting (0x13, the tackler on top, 0x002b6638).
    1. Not tackling: done.
    2. Near a train: command 5, which in the mount (Player_UpdateMounting 0x0027ec20) gets off: 244 / 245, the victim rises with 199 (Combat).
    3. The target is the held man. Pick a kind; 45 and 36 do nothing.
    4. Kind 35 (the ground punches): 40 % of the time two of them, chained.
  • Grabbed (0x14, 0x002b6848).
    1. Not grabbed and not being mugged: done.
    2. A grabber who is a friend: nothing.
    3. A civilian (type 4) not in a standing reaction calls for help every 30 updates (Gang_BroadcastHelpCall, 10 m).
    4. With threat response 0, other than a dealer or a class-221 human, he only waits.
    5. Otherwise the grabber becomes the target.
    6. Once he has been mugged, the latch +0x10 stops all struggling.
    7. Otherwise pick a kind:
      • 31 (the struggle strike) is queued with delay = chain delay × (1 − max(0, (health % / 100 − h) / (1 − h))), h being the class's hurt fraction (+0x04); a class-221 human uses 0. So a healthy man struggles at once and a hurt one waits the full chain delay.
      • Any other kind gets a stick angle of his heading or heading + π (50/50): push forward or back.
  • Mounted (0x15, tackled, 0x002b6bb8): as Grabbed (help call, threat-response rule), without the mugging latch. Every kind uses the health-scaled delay and no angle.
  • Grounded (0x17, 0x002b4aa8; Start 0x002b4a38 notes the time).

    1. While knocked down and someone holds an attack slot on him, queue kind 42 on himself. The delay is max(0, 1900 − min(time down, 2000)) ms.
    2. A target hidden in shadow is dropped.

    So about 1.9 s after going down, a man still under attack plays kind 42 (anim 250), which 0x002240e8 allows only from this goal. Kind 42 is the command 0xf (AttackKind_ToCommand, 0x00231090); a downed human never reaches the dispatcher, so no attack plays: the one command cuts his ground time as a press does (Combat; inferred). This corrects AI goals' "against the target".

Who backs off (FightBackOff)

The only caller of FightBackOffGoal_Init (0x002b7140) is GuardSubTactic_Order (0x003221e8), one of the Attack tactic's coordinated sub-tactics. Confirmed (code). With its back-off option (+0x17) the order does these things:

  • The gang leader gets FightBackOff with no time limit, after Brain_PopToGoalBase / Brain_MarkGoalBase. He keeps 1.75 × far plus 1-2 m from his target, faces him and taunts (FightBackOff).
  • Every other non-player member joins the leader's formation (set 3, min(2 × others, 9) slots, shape 3.0) and gets FollowAndDefend (3.0) on him. Without the option the members also get the attack table 0x00511438 as their override.

So FightBackOff is the leader hanging back while his men guard him. No script pushes it.

Events

Brain_OnEvent (0x0028f928) runs the perception's handler (0x00299100 on +0xf8), then the reaction goal's event slot if one is active (0x0028f988), then handler C. Event 0x10 is the attack warning (below). Handler C is where a brain answers the world: the civilian's (0x002ffb30) takes event 0x14 (violence nearby) and may start a fight through Brain_Fight; the type event handlers are among Brain_Fight's callers.

Brain types: think and event handlers

What each brain type does at its think (one character step in five, The update) and with each event (Events). Confirmed (code) at the addresses cited unless marked; "every n thinks" counts the think counter brain +0x38, so it is every 5 × n steps. R is the sight range (Brain_GetSightRange 0x0028bf00, The enemy scan). Goal ids are on AI goal types.

Human_CanSeeHuman and its squared near and shadow distances are under Sight.

The event record

Every handler takes the brain and a record: +0x00 the human who caused it (the attacker, the aggressor), +0x04 the other party (the attacker in event 1, the crime object in event 0x17), +0x0c and +0x11 flag bytes, +0x20 the event id, +0x24 a third object or human (the victim in event 0x14, the offender in event 7). Field roles inferred from the readers below.

Id Meaning Who answers it
0 triangle pressed on this human (the prompt) player, Warrior, dealer (buying), shopkeeper, bum
1 he was hit (+0x04 the hitter; +0x11 = 1 when it counts as an attack) cop, gang, civilian, dealer, Warrior, shopkeeper, shared
2 reset: target, actions, goals and enemies cleared shared (cops ignore it)
6, 7 an object taken or a theft (offender at +0x24 for 7; inferred) cop and civilian (7), shopkeeper (6, 7)
9, 0x11 (not read by any brain type; cops drop them) cop
10 a shopkeeper's customer check (inferred) shopkeeper; cops drop it
0xb a new enemy, sent by the enemy scan shared, Warrior; player and shopkeeper drop it
0xc (dropped by the player, the Warriors and cops) —
0xd the goal stack became empty (Brain_UpdateGoals) goals and tactics
0xf "use this flag" (a free tag spot nearby; inferred) sent by the gang think to itself
0x10 the attack warning every type
0x14 violence seen (+0x00 the aggressor, +0x24 the victim; +0x04 0 for a fight between humans) cop, gang, civilian, Warrior, shopkeeper; the player drops it
0x15 he has been put on the ground (a knock-down clip ended, or a throw or grab put him down; +0x24 himself): his attackers taunt (Gang fights rule 5) shared
0x17 a crime seen (+0x04 the crime object, which gives its position) cop, gang, civilian
0x18 danger near (a fire, a car; Brain_OnDangerNear 0x00291728) Warrior

Hearing: an event reaches a brain only within its hearing radius (Brain_IsInHearRange 0x002935d8: brain +0x134, 50 m when the brain is made; +0x138, 20 m, for event 20). Confirmed (code) at 0x0028a570 for the defaults.

Help calls

Gang_BroadcastHelpCall(range, caller, a, b, c, newEnemy) (0x00293640). Despite the name, a help call is not limited to the gang: it is event 0x14 (20, violence) sent to everyone nearby. Confirmed (code) at 0x00293640, 0x00293768 and 0x002935d8, and the arguments at each of the 16 call sites:

  1. The record: +0x00 = a (the aggressor), +0x04 = b, +0x08 = c, byte +0x0c = newEnemy, +0x20 = 0x14, +0x24 = the caller (the victim).
    • Every caller passes b = 0, so a help call always has +0x04 = 0.
    • c is 0 except where the shared handler's event 1 and the BigBrawler, BigFighter and BigFighterA hit handlers pass the hit event's +0x00 (with a = its +0x04, the hitter).
    • newEnemy is 1 only from the shared handler's event 0xb (below).
  2. Who hears it (AI_AlertNearby 0x00293768):
    • Humans_FindAhead lists up to 60 humans within range of the caller's position, in table order, skipping the caller and slots without bit 0 in 0x00715390. Players are included.
    • The filter Brain_IsInHearRange then keeps a human only when his squared distance is within his own brain's +0x138 (event 20's radius, 20 m by default). So the reach is min(range, the receiver's +0x138).
  3. The ranges at the call sites:
    • 5 m: Brain_CallForHelp.
    • 10 m: the Grabbed and Mounted goals (a civilian held).
    • 15 m: Scout's tactic.
    • 20 m: the shared handler, the Big* goals and the player's mugging.
    • 30 m: the cop's crime report, a civilian's attack warning, the dealer, the shopkeeper and a busy gang member hit by a player.
  4. What a hearer does: the event goes through his human's vtable +0x44 (Human_OnEvent): his script handlers, then Gang_OnEvent, then Brain_OnEvent.
    • A gang with a tactic consumes event 20 there, and some tactics act on it: Attack, Crowd, Scout, and HanginOut or Idle with respond (Tactics).
    • Otherwise his type handler takes it: a cop the crime path, a gang member or Warrior Brain_OnViolenceSeen (its +0x04 = 0 test always holds for a help call), a civilian his own handler. The player and the shopkeeper drop it.
  5. The fight that follows: Brain_OnViolenceSeen picks a side (Brain_PickSideInFight) and calls Brain_Fight(side, −1, 0). With no HelpRespond goal (0x1d) above his base, it also calls Gang_SendHelper(self, side, 0, −1) (0x002b75b8).

    • Despite its name, Gang_SendHelper sends nobody: it pushes a HelpRespond goal on the brain it is given (HelpRespondGoal_Init(goal, brain, side, 0, −1)). The hearer helps himself.
    • Only a tactic's "answering violence" picks another member, and then calls it for him.
    • HelpRespond's flag +0x14 (HelpRespondGoal_Init's fourth argument) is never 1: its only caller is Gang_SendHelper, and all seven callers of that pass 0 (AttackTactic_OnViolence, AttackTactic_Process, Brain_OnViolenceSeen, CopBrain_OnEvent, HanginOutTactic_OnEvent, IdleTactic_OnEvent, ScoutTactic_OnViolence). So HelpRespondGoal_Process always takes the "ends once the brain lists enemies" branch. Confirmed (code).
    • HelpRespond's Start (0x002b76a0), with the side still a valid target: within 1.1 × the brain's range +0x140 and walkable in a straight line (Human_CanWalkStraightToHuman) → a turn to him and a 3 s head look, nothing more: the look is not an action, so the goal ends when the turn does (HelpRespond). Within that range but not walkable → 60 % (Random_Int(100) < 60) a LookAround action first (type 10, LookAroundAction_Init 0x002fe248: clip 0x29e with the field of view widened to 4.71 rad while it plays, AI code: actions), then, as beyond the range, a move action to him (gait 2, or 4 beyond 10 m; with an empty enemy list he leaves the fight stance and goes at gait 4, 5 on level 0x53). Confirmed (code).
  6. The new-enemy flag (+0x0c) has one reader, the cop's handler (0x00302340, at its event 0x14). There, a cop victim gives crime 2 (assault on a cop) only when the side picked is not the caller and +0x0c is 0. So when a cop broadcasts that he has a new enemy, the cops who hear it do not report an assault on a cop. They still take a side and fight.

No other reader was found in Brain_PickSideInFight, Brain_OnViolenceSeen, the gang and civilian handlers, or the Attack, Crowd and Scout tactics' violence handlers.

Picking the side (Brain_PickSideInFight(brain, aggressor, victim), 0x002912b0; confirmed (code)). The callers pass the hearer's brain, the event's +0x00 (the aggressor) and +0x24 (the victim). It returns the human to fight, or 0:

  1. No aggressor: take the victim's brain target (+0x124); none → 0.
  2. Three tests, all from the hearer H: Fv = Human_IsFriendly(H, victim), Ta = Human_IsThreatTo(H, aggressor), Fa = Human_IsFriendly(H, aggressor) (all three computed first).
  3. Fv and not Fa and not Ta (a friend is hit by someone neutral): unless H's brain is a cop's (type +0x04 = 1), the two gangs become enemies (Brain_MakeGangsEnemies(H's brain, aggressor's brain): Gang_SetEnemyBit on brain +0x20c of each), and Ta counts as true.
  4. Ta → the aggressor.
  5. Otherwise Fa and not Fv (a friend hit someone H does not count as a friend) → the victim; else 0.

So a friend's attacker is always chosen once he is (or is made) a threat; a cop sides without making gang enemies, and only against an aggressor who is already a threat or the victim of a friend. It has no out-value: the "reason 2" of the civilian handler's event 0x14 is that handler's own disturbance reason (Civilians), not something Brain_PickSideInFight returns.

The shared event handler

Brain_DefaultOnEvent (0x00292d80) is every type's fallback (each type handler passes the ids it does not take):

  1. 1 with +0x11 set: a help call within 20 m (Gang_BroadcastHelpCall 0x00293640) naming the hitter.
  2. 2: target cleared, actions, goals and the enemy list cleared.
  3. 0xb (a new enemy), unless that enemy is throwing at him: a help call within 20 m flagged "new enemy"; an Investigate goal (0x5e) on top is popped; when the top is then Chase (0xc), its actions are cleared.
  4. 0x10 (the attack warning):
    1. The near radius is 3 m (9), or 0 when the attacker is hidden in shadow, throwing, and this brain's +0x2d4 is clear. Unless Human_CanSeeHuman(+0x12c, R, near, 2 m, self, attacker) and the attacker is not friendly, nothing more.
    2. Brain +0x200 + 1 (the block's trigger); unless Human_ReactToThreat already holds, the two gangs are made enemies (Brain_MakeGangsEnemies 0x00290328); a help call within 20 m.
    3. By the top goal: StationaryShooter (0x8d) with free actions: clear the actions and, with Goal_TryBlock(goal, 90), command 4 (block). BigFighter / BigFighterA (0x85, 0x86): the goal's own handler (0x002ea0b8, 0x002ebe20). Fight (0xf): when his target may be countered (Human_CanCounterTackle or Human_CanCounterGrab against it) and Random_Int(100) < Human_CounterChance, clear the actions and, on an empty queue, command 3 (the counter).
    4. When the attacker has a pad or is a Warrior (type 3): Brain_TryPatternBlock (0x00292158).
  5. 0x15: for each human holding an attack slot on him (brain +0x1a4), except classes 0x77, 0x6a and 0xb2, whose gang kind's field 0x6d is not 4, whose top goal is Fight (0xf) or BigFighter (0x85), and who is not a Warrior under a tactic that answers 2 at slot +0x34:
    • his own target: a taunt of kind 4 (id 0, or 0xf under BigFighter; class 0x80 only while it holds +0x364);
    • any other: skipped when nearer than the middle of his melee ranges ((near + far) / 2), else a taunt of kind 3, id 0x10 or 0x8f at 50 %. The taunter clears his actions, walks off (gait 0), turns to the human (start delay 50-100 ms) and taunts (TauntAction_Init); a non-target also delays his next attack by twice the taunt clip's length (anim 598 or 599). Under the Moe boss tactic (0xc), a BigFighter's taunt tells the tactic (BossMoeTactic_FireAnimDone).

Event 0x15 is sent to a man as he is put on the ground: by HitReact_OnSpecialEnd (0x0026a1f8, the end of a knock-down clip) and by Throw_ClearVictimLink (0x0022bdc0, a throw, grab or mount putting its victim down). Confirmed (code); the detail is in Gang fights rule 5.

Cops (type 1)

Think (CopBrain_Think 0x00300678):

  1. Brain_MaybeSpotPlayers (0x0028fae8).
  2. The radar blip: mode 2 when a player (player 2 too in co-op) is in his enemy list, 3 while his top goal is CallForBackup (0x79), else 0; kept in human +0x19e and brain +0x28c.
  3. Threat response set, no fight goal, no Chase goal (0xc) and a non-empty enemy list: brain +0x28f = 1 and Brain_PushFightGoal(brain, −1) (no time limit).
  4. Pursuit counter brain +0x148 (s16), when he has a target: + the target's gait when the target moves (gait ≠ 0) and either holds an object or moves along the cop's facing (velocity · facing ≥ 0), up to 36; else − 2, down to
  5. Its reader is not traced (inferred: how hard the target is running from him).
  6. With crime type 12 (Trespassing) enabled, every 3 thinks, the gang having no tactic: when he may respond (CopBrain_CanRespond 0x00302038) or already has a fight or chase goal, CopBrain_ChaseTarget (0x00301248).
  7. Every 17 thinks: the nearest store flag (activity 14) with group bit 0x10000 within 20 m; the nearest player to it within 10 m whose human byte +0x5b7 is set, a valid enemy, and he may respond → crime 1 (break and enter), below. Then the think ends.
  8. Every 20 thinks: each enabled tag spot (activity 12) within R with a human on it (flag +0xdc) who is not friendly and to whom a clear ray runs, if he may respond → crime 13 (vandalism), the spot passed along.
  9. Every 4 thinks: each player picking a lock or stealing a stereo whom he sees (Human_CanSeeHuman(+0x12c, R, 0.4 × R, 2 m), the 0.4 × R being squared too) → crime 1 (lock) or 11 (theft).
  10. Every 8 thinks: each player holding a world object of type hash 0x3c4e590b whom he sees (as 8) → crime 11.

Responding to a crime (steps 6-9 and the event handler): pop a MoveToUseFlag goal (4) on top; count the backup needed (CopBrain_CountBackup 0x00302118); brain +0x28f = 0; Brain_PushFightGoal(−1); when backup is needed, Goal_CallForBackup(cop, offender, crime, count) (0x002c4710, goal 0x79); then Goal_SpotCriminal(cop, crime, offender's position, offender, victim, flag) (0x002c4060, goal 0x78) on top. Crimes and the police has the report itself.

Events (CopBrain_OnEvent 0x00302340):

  • 1 with +0x11 = 1, and 0x10: CopBrain_ReportCrime(cop, attacker) (0x00301cf8): nothing for a friendly attacker; one throwing at him first calls for help (Brain_CallForHelp 0x00291d08, done if answered); +0x200 + 1. Without a fight goal and up: when the attacker is beyond R or out of line of sight, the cop treats it as a crime seen (Brain_OnCrimeSeen with event 0x17) and stops. Otherwise a help call within 30 m, the gangs made enemies, a crime report of type 2 (cop assault) at the attacker, MoveToUseFlag popped, backup counted; when not busy, Brain_Fight(attacker, −1) (brain +0x28f = the attacker hidden in shadow) or, already fighting, Brain_AddEnemy + Brain_SetTarget; CallForBackup (crime 2) when needed.
  • 2, 9, 10, 0xc, 0x11: dropped.
  • 7 (a theft; offender at +0x24): not blocked, no gang tactic, may respond, the offender not friendly, in his field of view and a clear ray to him → crime 11, as above.
  • 0x14 (violence seen), with threat response and up: the side to take (Brain_PickSideInFight 0x002912b0).
    • None (a fight between strangers): not blocked, no tactic, may respond, the aggressor not friendly; the backup for crime 6 (gang fight). None needed: when the aggressor's gang last called (gang +0x5f4) more than 30 s ago or never, and he is in line of sight, Goal_LeaveArea(cop, aggressor's position, 2, aggressor) (0x002c4ec8, goal 0x7a: warn, then walk away). Needed: fight goal, CallForBackup and SpotCriminal for crime 6.
    • A side, if he is a valid enemy: the crime by the victim's brain type: civilian, gang, shopkeeper or dealer (4, 2, 6, 5) → 8 (mugging) when the offender is mugging, else 0 (assault); a cop victim (when the offender is not the event's +0x24 and byte +0x0c is 0) → 2; else 14 (none). A civilian assault while two gangs fight within his far range of the victim (Humans_TwoGangsFighting 0x0029e168) is ignored; so is everything while he has a target, is blocked, or holds SpotCriminal, CallForBackup or LeftTurf (0x78, 0x79, 0x3f). Without a fight goal: MoveToUseFlag popped; with a Respond goal (0x74) above the goal base (brain +0x2d) only when Human_CanSeeHuman(+0x12c, R, 3 m, 2 m) sees the offender; gangs made enemies; +0x28f = 0; Brain_Fight(offender, −1). With one: gangs made enemies, Brain_SetTarget(offender). Then, with no gang tactic and (no fight goal before, or the offender's gang +0x5e8 clear): CallForBackup when needed and SpotCriminal for the crime. Last, with no HelpRespond goal (0x1d) above the base, Gang_SendHelper(cop, offender, 0, −1) (0x002b75b8).
  • 0x17: Brain_OnCrimeSeen(brain, event, 0) (0x00291668, then CopBrain_OnCrimeSeen 0x00301538).
  • Others: the shared handler.

Gang soldiers (type 2)

Think (GangBrain_Think 0x00304608):

  1. Brain_MaybeSpotPlayers.
  2. Unless human +0x19e is 4 or 5 (a blip a goal owns, such as CallGang's), the radar blip: 2 when a player is in his enemy list, else 0.
  3. Threat response set, no fight goal and a non-empty enemy list: Brain_PushFightGoal(−1).
  4. The pursuit counter, as the cop's (step 4).
  5. A weapon, every 6 thinks: wants one (+0x265), holds nothing, and his gang kind's pick-up factor f (GangConfig_GetField6E 0x001644f8) is not 0: with chance 25 × f %, when no human is within 1.5 m (his sectors refreshed if older than 1 s, 0x0028fe90; Sectors_AllClear 0x0029eaa0), the nearest pickable object within 2 m (Ai_FindObject 0x0029d5f0, mask 0x30000); for a gang of kind 1 any, else not one whose type class (+0x87) is 4: actions cleared and, on an empty queue, a pick-up action (0x002faf98, kind 0x21).
  6. A hat, every 12 thinks, when he may go for one (Brain_CanSeekHat 0x0028bfa8: allowed (+0x266), not busy, bare-headed (+0x364 empty), nobody attacking him, class 0x82-0x86, a top goal that is not EngageEnemy, GetItem or LeftTurf, and the gang tactic, if any, allows it): his class's hat (class +0x14c) within 10 m → Goal_GetItem(human, 4, hat) unless a GetItem goal is already above the goal base.
  7. Every 15 thinks, with no target, no held flags and none of the busy states 0x7bf9e9f7ff0: the first free (+0xdc none), enabled tag spot (activity 12) within 10 m with a clear ray → he sends himself event 0xf with the flag (inferred: go and tag it; the reader is the goal on top).

Events (GangBrain_OnEvent 0x00304fa8):

  • 1 with +0x11 set, the hitter not friendly: a hitter throwing at him first gets a help call (done if answered); the gangs made enemies unless already threats. By the top goal: GrabTarget (0x1f) → its broken-grab handler; BumLogic (0x4f) → the bum gives the hitter an item (Bum_GiveItem), done; the boss goals 0x8b, 0x8a, 0x85, 0x86, 0x89 → their handlers; a Riot goal (0x54) anywhere on the stack → Riot_OnAttacked. None of them, or one that declines: when not busy, Brain_Fight(hitter, −1, 1), or Brain_AddEnemy + Brain_SetTarget when already fighting; when busy and the hitter is a player, a help call within 30 m and the hitter's head turns to him for 3 s.
  • 0x14 with +0x04 = 0, his gang not of kind 0x17: Brain_OnViolenceSeen (below).
  • 0x17: Brain_OnCrimeSeen → GangBrain_OnCrimeSeen (0x00304c78): when he may react (Brain_MayReactToCrime) and, for a crime with an offender, the offender is a threat to him: without a Mark goal (0x9a), nothing when two gangs fight within the far range of the scene; then an Investigate goal (0x5e, InvestigateGoal_Init(5 m, point, offender, 2, 0, line 13, chance)) at the offender's (or the crime object's) position, the chance 0x00510ade for a lookout (Scout 0x6f or PathScout 0x70 on the stack) else 0x00510add; with a Mark goal, a ReactNoise goal (0x99) at the point instead.
  • Others: the shared handler.

Brain_OnViolenceSeen (0x00291960), used by gang members and Warriors:

  1. Without a Mark goal (0x9a): with threat response, away from trains, no target, no held flags, actions free, and a top goal other than EngageEnemy, GetItem and LeftTurf: the side to take (Brain_PickSideInFight).
    • A Warrior whose chosen side is busy (grabbing, held...) and within 1.1 × his far range: his head turns to him for 3 s and, at 10 % when he may gesture (Ambient_MayGesture), actions cleared, a turn to him and a taunt (kind 3, id 0x10); done.
    • Otherwise Brain_Fight(side, −1, 0) (or Brain_SetTarget when fighting) and, with no HelpRespond goal above the base, Gang_SendHelper(self, side, 0, −1).
  2. With a Mark goal: when a side is found, a ReactNoise goal on top (popping an earlier one) at the side's position.

Civilians (type 4)

Think (CivilianBrain_Think 0x002fef40):

  1. Brain_MaybeSpotPlayers; timer B (+0x2c8) cleared once past.
  2. Only while his gang has no tactic. Threat response set and no fight goal → target cleared, threat response 0 (a civilian calms down as soon as his fight ends).
  3. Reacting to the player, when all hold: the script's response byte +0x228 is 1 (binding), timer B is 0, class +0x11b is not 6, he may react (CivilianBrain_CanReactToCrime 0x002ff198), the nearest player is within 10 m, that player's brain +0x2d5 is clear, nobody in the player's gang is under attack (Gang_AnyMemberHasAttackers), and he has line of sight. A MoveToUseFlag goal on top is popped; then ped type 3 (brain +0x26c) pushes Hostile (0x72, HostileGoal_Push(10 m, …)), any other PedReaction (0x6b) saying line 0xaf.

Events (CivilianBrain_OnEvent 0x002ffb30):

  • 0: a bum (class +0x11b 6 or 7) under BumLogic (0x4f) takes the money (Bum_TakeMoney).
  • 1: a bum with BumLogic anywhere on the stack gives the hitter an item (Bum_GiveItem); then, with +0x11 = 1, the attack warning below.
  • 0x10 → CivilianBrain_OnAttackWarning(brain, attacker) (0x002ff898): one throwing at him first calls for help; a help call within 30 m; when he may react: MoveToUseFlag (4) or PlaySpecialIdle (0x23) on top is popped; ped type 3 and the attacker not friendly → threat 2 and Brain_Fight(attacker, −1, 0); otherwise PedReaction (0x6b, no line). A CallPolice goal (0x6d) or Peddler (0x50) on top hears of it too. A non-friendly attacker raises +0x200; when he is busy and the attacker has a pad, the attacker's head turns to him for 3 s.
  • 7 (a theft by the human at +0x24, within 15 m): when he may react, the crime can be reported (GameState_CanReportCrime 0x0041cf80), Random_Int(100) ≤ 60, the thief in his field of view and in line of sight, a spawner in state 1 exists and game state +0x29f is set: MoveToUseFlag popped, he becomes the crime's reporter and pushes CallPolice (0x6d, CallPoliceGoal_Push(self, 11, thief's position, −1, thief)).
  • 0x14 (violence seen), unless his brain is "dead" (+0x09) or he does not react to violence (+0x267):
    1. No side (strangers fighting): ped type 3 ignores it; others within 15 m of the victim who may react pop MoveToUseFlag and push PedReaction (no line) toward the human at +0x24.
    2. A side, unless the other party's top goal is Hostile (0x72): the crime kind as the cop's (4, 2, 6, 5 → 8 or 0; a cop victim → 2 with chance 100 %; else 14), the chance otherwise 0x00510adf. Only within 15 m of the offender and when he may react; MoveToUseFlag popped.
    3. Ped type 3 in a gang of kind other than 0x17: nothing more. Ped type 3 in a kind-0x17 gang, his human +0x3b8 clear and the victim's +0x3b8 = 1: threat 2, Brain_Fight(offender, −1, 0), line 0xcd; done.
    4. Calling the police, when the crime can be reported, the kind is not 14, the offender's gang last called 40 s ago or more (or never), Random_Int(100) ≤ the chance, line of sight, the crime is not an assault on a civilian while two gangs fight within his far range, and the crime has responders (game state +0x294 + kind): with a spawner in state 1 (and, for a cop victim, fewer than 4 humans within 10 m of the offender), he reports and pushes CallPolice (kind, the offender's position, victim, offender); a player offender may queue tutorial hint 7 once. With no spawner in state 1 but one in state 0: he reports and pushes CallGang (0x6e, Goal_CallGang(15 m, self, offender, 2, 0, 0)).
    5. Otherwise, any but ped type 3: PedReaction (0x6b) against the offender, no line.
  • 0x17 within 15 m of the crime object: Brain_OnCrimeSeen (→ CivilianBrain_OnCrimeSeen 0x002ff2b0).
  • Others: the shared handler.

Dealers (type 5)

Think (DealerBrain_Think 0x00302cd8): Brain_MaybeSpotPlayers; threat response set and no fight goal → threat response 0; every 5 thinks, while fighting (threat response set), wanting a weapon (+0x265), empty-handed and not blocked: the nearest pickable object within 2 m (mask 0x30000) → actions cleared, a pick-up action (kind 0x21).

Events (DealerBrain_OnEvent 0x00303178): 0 under Dealer (0x80) → DealerGoal_OnBuy (Buying); 1 with +0x11 = 1 and 0x10 → DealerBrain_OnHit (0x00302ff0, Buying); 0xb and 0x14 dropped; others the shared handler. Confirmed (code).

Shopkeepers (type 6)

Type 6 (the civl_co_di class kind) is the shopkeeper: its handlers serve the Shopkeeper goal (0x82).

Think (CivlCoDiBrain_Think 0x00303f10): the same as the dealer's think.

Events (CivlCoDiBrain_OnEvent 0x00304228); ShopkeeperBrain_OnDisturbance (0x00304070) is the shopkeeper's disturbance handler (brain, offender, reason, object, call help): a thrower first calls for help; with call help, a help call within 30 m; when he is free to fight (Brain_IsFreeToFight 0x00304030) and has a Shopkeeper goal, goals above it are popped and the goal is told (ShopkeeperGoal_OnDisturbed); a non-friendly offender raises +0x200.

  • 0 under Shopkeeper: the goal's prompt handler (0x002e63e0, the sale; AI goals).
  • 1 with +0x11 = 1: disturbance (the hitter, reason 1, no object, call help).
  • 6, 7, with a Shopkeeper goal: when the store's box (the goal's +0x3c handle) contains the object (event +0x04 for 6, +0x00 for 7): disturbance (the human at +0x24, reason 0, the object, no call).
  • 10, with a Shopkeeper goal whose +0x54 and +0x30 are clear: when a player is within the goal's range (+0x38 × 0.25, at least 16; compared with a squared distance, inferred), goals above the Shopkeeper goal are popped.
  • 0xb: dropped. 0x10: disturbance (the attacker, reason 1, call help). 0x14: disturbance (the side Brain_PickSideInFight picks, reason 2, no call).
  • Others: the shared handler.

The Warriors (type 3)

Think (WarriorBrain_Think 0x003052f0):

  1. Brain_MaybeSpotPlayers; threat response set, no fight goal and enemies → Brain_PushFightGoal(−1); the pursuit counter as the cop's.
  2. Pick-ups, every 6 thinks: The Warriors' pick-ups. The search is 1.5 m with mask 0x10000; the mask is 0x30000 (and the search's last flag 1) when his gang's leader is neither grabbing from behind nor tagging, the gang's +0x32 is 1, a member of the gang is under attack and his own enemy list is empty. The object must also pass WorldObject_CanBePickedBy.
  3. Outside co-op (game state +0x158 = 0): holding a weapon of kind 4 or 6 with an empty enemy list and no steal tactic (0x26) → he drops it.
  4. Every 8 thinks: holding a world object whose type byte +0x64 is 0x9a while his human +0x5b7 is clear → drop it (inferred: loot only inside a store).
  5. Hats for classes 0x12, 0x14 and 0xbc, every 6 thinks, under the follow (0x12) or hold (3) tactic and with no hostiles counted (+0x152 = 0): unless his +0x364 already holds the named hat object (0x0056a2d0), the dyn_warr_cb object within 20 m → Goal_GetItem(4) unless a GetItem goal is above the goal base.
  6. The pick-up window 0x00510a9c (AI_SetPickupWindow): once its time passes it is cleared; then, when he is free (not busy, not blocked, no held flags 0x1c1ee60), the first player carrying (+0x364) a world object of type hash 0xbbbef927 within 10 m and in line of sight: actions cleared, a turn to him, a wait action, and line 0x6d at him.
  7. The swap prompt (human +0x1b2): the nearest player within 1.5 m, not blocked, no held flags, gait below 3; one of them holding an object; this Warrior not sparring (+0x2e5 clear), not blocked, standing (gait 0), in the player's gang (or +0x121 set), with no attacker slots taken on him, and on the right side of the player (Human_GetSideOf = 0). The prompt string is 0xc (both hold), 0xd (he holds) or 0xe (the player holds); the swap itself is the event-0 prompt (The weapon swap).
  8. Timer +0x2dc: once past, cleared with byte +0x2e4.
  9. Getting out of the way, every 12 thinks, when he is out of the fight stance, not in shadow, not busy or blocked, standing, with no target, no attackers, and a top goal other than GetItem (0x2c) and PlaySpecialIdle (0x23):
    • when a ray toward a point ahead of him (his near range, 3 m, inferred) is blocked and a player is within 30 m: actions cleared (unless the front one is of class 8) and a turn (toward the player, inferred);
    • the nearest member of his gang within 1 m who is free, has neither Scatter (0x37) nor HoldPosition (0x36) and no actions is pushed aside (Brain_PushAside).

Events (WarriorBrain_OnEvent 0x003063b0): 0 → WarriorBrain_OnPrompt; 1 → first WarriorBrain_OnHitByChief (0x00306190); otherwise, with +0x11 set and a non-friendly hitter: gangs made enemies unless already threats and, unless one of the busy states 0x1f80974000 holds, Brain_Fight(hitter, −1, 1); 0xb: the new enemy noted (brain +0x27c, flag +0x280 = 1), consumed; 0xc consumed; 0x14 with +0x04 = 0 → Brain_OnViolenceSeen; 0x18 → Brain_OnDangerNear; then (except for consumed ids) the shared handler.

The player (type 0)

Update (PlayerBrain_Update 0x003035d8, every step; it pushes no goals of its own on him):

  1. Brain_UpdateAlertness; a target that is no longer up is dropped.
  2. Every 300 updates, +0x152 = how many AI humans of all 32 gangs hold him in their enemy lists.
  3. Every 2 s (brain +0x160), PlayerBrain_ScanNearEnemies(7.5 m, target) (0x0028af88).
  4. WarChief_AutoCommand (Automatic commands) every update; every 40 updates WarChief_CheckCrewInRange, quiet and forced, so it only refreshes the out-of-range flag (call at 0x003037c4).
  5. Brain +0x110 = his heading.
  6. While +0x2e4 is set (a Warrior is hitting back at him, WarriorBrain_OnHitByChief), every 13 updates each AI member of his gang within 15 m who is free, not sparring (+0x2e5 clear) and has no TauntPlayer goal gets TauntPlayer (0x63, TauntPlayerGoal_Push(member, chief, 1, −1) 0x002d09f0): the taunting ring (TauntPlayer).

Think (PlayerBrain_Think 0x00303260): in two-player play only (game state +0x224 = 2, +0x158 = 0) and unless his +0x3ac is 1: the swap prompt between the two players, as the Warrior's step 7 (within 1.5 m, both out of the fight stance, not blocked, standing, one holding something, the other player's attack slots empty).

Events (PlayerBrain_OnEvent 0x00303e90): 0 → PlayerBrain_OnPrompt (0x00303468); 0xb, 0xc, 0x14 dropped; others the shared handler (so the attack warning, 0x10, reaches a player's brain too).

The weapon swap

Triangle beside a Warrior (or, in two-player play, beside the other player) whose swap prompt is up (step 7; HUD: the talk prompt) swaps what the two hold in their hands. Confirmed (code) at 0x0024d810, 0x00306040, 0x00303468, 0x00233b08, 0x0021c0a8, 0x00101360 unless marked.

How the press gets there. The swap has no context record: it comes from triangle's pick-up search (Crimes: triangle, step 5; Human_PickUpSearch, 0x0024d810), which runs only while the presser has no pick target (human +0x33c). The search gathers world objects within 1.5 m and the humans ahead of him within 1.5 m (Humans_FindAhead), sorts them all by distance, nearest first, and sends each in turn message 0 with the presser as the subject; of the humans only brain types 0 (a player) and 3 (a Warrior) are asked. A human's message 0 reaches his brain as event 0: WarriorBrain_OnPrompt or PlayerBrain_OnPrompt, the brain's own human being the receiver. A handler that returns true ends the press; one that refuses (false) lets the search go on to the next candidate, so a refused swap can still pick up an object nearby or, with nothing to take and something in hand, drop the presser's object (inferred from the search's order; the refusal itself is silent).

A Warrior (WarriorBrain_OnPrompt, 0x00306040) refuses unless all hold:

  1. the receiver is talkable (his interface's "has a handler or +0x1b2", 0x0021d570);
  2. neither of the two has his actions blocked (HumanRecord_AreActionsBlocked, 0x00228228);
  3. neither has any of the held flags 0x5c0221f (busy: attacks and reactions, EngageEnemy);
  4. the way is clear: the receiver is not grabbed and a 0.2 m capsule swept from him to the presser hits nothing but the presser (Human_IsWayClearTo, 0x0021c0a8, collision mask 0x2803).

Then his actions are cleared, he turns to face the presser over 0.2 s (Human_TurnToFacePoint, 0x00221c20), and the swap runs (below). The presser does not turn.

The other player (PlayerBrain_OnPrompt, 0x00303468) checks only 1 and 2 (no held flags, no clear way), clears the receiver's actions and sets his rotation at once to face the presser (the heading from his position to the presser's, written straight into his transform: no turn over time), then the same swap. What puts the prompt up between the players is PlayerBrain_Think (two-player game state +0x224 = 2, not co-op Rumble, the receiver not a war chief).

The swap (Human_SwapHeldObjects(receiver, presser), 0x00233b08):

  1. Each side's held thing (human +0x338) counts only when it is a world object; a held human counts as nothing and stays where it is. Nothing else changes hands: not the hat (+0x364), not the inventory (items, money, flashes, Player state).
  2. Each side holding a world object drops it (Human_DropHeld, 0x00257f38, Objects: drop): its anim set is popped, it gets a physics body and a removal time.
  3. For each dropped object, the other human takes it: every task on his animation stack (+0xd8) gets slot +0xc8 with 0x10 (not traced), then the object is placed straight into his hand without a clip, as HuPlaceItemInHand does (Objects: scripted placement): Human_PlaceObjectInHandByPickupClip (0x0024c6d8) picks the low pick-up clip for the type's pickup_anim (CfgObj +0x65: 461 by default, 503 for 2, 481 for 3, 498 for 4, 463 for 5, 465 for 6, 549 for 7; the height is not tested), makes the object his pick target (+0x33c) and reads the clip's first type-9 event without playing it (Anim_SendPickupPlacement, 0x00101360), sending the object message 0x1b take at once with the event's bone and offset; the take puts it in his hand (+0x338) and applies its anim set (message 3). When the placement succeeds the object's record is unpinned. Then his state gets 0x20000000 (the anim state is rebuilt next update, so his idle and moves follow the new anim set) and the object's removal time is cleared (WorldObject_SetRespawnTimer(obj, 0)).
  4. A left-hand or hat object (pickup_anim 5 or 6) has no type-9 event in its clip (463, 465), so it is not attached: it stays dropped at the giver's feet (inferred from the placement path, as for HuPlaceItemInHand).

So the exchange is instant: no clip plays on either side, and both objects change hands on the update of the press; when only one holds something it simply passes to the other. Any held world object can be swapped (bats, bottles, knives, pipes, two-handed objects; the prompt's own test is only "one of them holds something").

The line. Afterwards, when the presser holds something and is not hidden in shadow (brain +0x2d4 clear), he says speech command 146 give_me (Human_SayCommand(1.0, presser, 146, no callback, no interrupt, uncut, no target, duckable), Speech). The handler then returns true.

Starting a fight and choosing the target

  • GoalFight(human, target, …) is Brain_StartFight (0x002b2b90): clear the actions, then Brain_Fight. The target is not kept: once the first fight goal ends (at once for a target beyond 5.5 m), Melee picks by score like any fight, the old target getting only the +3 of the score. Confirmed (code); seen at level80's last fight (After the order), confirmed (runtime).
  • Brain_Fight (0x0028d2e8) needs a threat response (+0x21c ≠ 0). It adds the target to the enemy list (Brain_AddEnemy, 0x0028d538, which tells the gang's tactic with event 0xb), takes it as the target (Brain_SetTarget, 0x0028cfe0) and calls Brain_PushFightGoal (0x0028d190) with its duration argument.
  • Brain_PushFightGoal(brain, duration) does nothing when the gang has a tactic (gang +0x40), the human is down or dead (0x00223b70, 0x00227dd8), or a type-3 brain holds goal 9 (AttackTarget). Otherwise it pops any goal 8 (Melee) and 0x41 (FindEnemy) already on the stack, with every goal above them, then pushes three: Goal_Melee (0x002add08, the GoalMelee binding's worker) pushes FindEnemy (0x41) and then Melee (8), both given the duration, and sets the gang's alert state to 1 when the gang has no tactic; then the fight goal (0xf, FightGoal_Init with the duration). The stack ends FindEnemy, Melee, Fight (top). So both earlier statements were half right: it pops and then pushes. Confirmed (code) at 0x0028d190 and Goal_Melee; the order confirmed (runtime), in lesson 12.
  • Brain_SetTarget releases the old target's attack slot and claims one on the new target (Brain_ClaimAttackSlot, 0x0028df30): the target's list +0x1a4 holds at most +0x1e4 attackers, and a closer attacker takes the slot of the farthest.
  • Re-targeting: once a second, the nearest threat within 1.5 m straight ahead, slot forced: Gang fights.

The fight goal

The fight goal attacks a target already within reach; Closing on the target brings it there.

The deadline: FightGoal_Init sets +0x20 = now + a random 750-1000 ms and +0x24 = now + its duration argument. Once +0x24 has passed, the goal ends unless the fighter is still one of the target's active attackers (0x002911a8), the target's byte +0x11f is set (0x00290ea8) and the fighter has no goal 0x35; a class-13 fighter without +0x29 ends at once (GoalRiot for the 8 s case). GoalFight passes −1, so its deadline (now − 1, unsigned) is already past and these checks run from the first update. This corrects the earlier reading of these checks as attack pacing.

The fight goal in order

FightGoal_Process (0x002b3ab0), every update. Confirmed (code); this corrects an earlier reading that put the block try before the range checks and left out the tackle try.

  1. The update counter +0x18 is raised by 1.
  2. Done when the target fails Brain_ValidateEnemy, or when it holds no attack slot for A.
  3. The tackle try (FightGoal_TryTackle, below). When it queued a tackle, wait.
  4. Done when the target is beyond 1.1 × far (5.5 m), unless A holds a throwable object or one of kind 4 or 6.
  5. Every 30 updates, done when there is no walkable straight line to the target (0x002221b0).
  6. Re-target at most once a second (+0x14), from A's sector record (re-targeting).
  7. The block try (Goal_TryBlock). When it pushed a block, A gives up his active-attacker place on T and waits.
  8. While actions are queued, wait. When the last attack ended (+0x28), give up the active-attacker place.
  9. The deadline (+0x24, The durations).
  10. When no kind is chosen (+0x10 = 45), pick one (Brain_PickAttack with Human_CanUseAttackKind).
  11. The stance. Drop the fight stance for kinds 19 and 20, and always for a class whose +0x11b is 6 or 7. Otherwise take the stance.
  12. May A attack now? Brain_CheckAttack(B, kind, T holds an object) (0x002906b8, below).
    • Any answer but 1: reposition and wait.
    • Answer 1 and the target is beyond the kind's reach (The reach of a kind): queue a move-to-human action and wait. The distance is the 3D distance of the two transforms (Vec_DistSq).
      • It holds between 2 × T's capsule radius and max(reach, that + 0.1 m).
      • Its limit is 2000 ms, or 1000 ms for a class-13 fighter or with a FollowAndDefend goal (0x35).
    • Answer 1 and in reach:
      1. The grab and snap try (below). When it returns 1, wait. It may change the kind and set the stick angle.
      2. When Human_CanStartAttack refuses the kind: for kind 22, also queue a move to between 0.8 × near and 0.8 × near + 1 m (1000 ms). Then pick again with Human_CanStartAttack and wait.
      3. Otherwise queue the attack (Brain_QueueAttack(angle, B, T, kind, 0)) and clear the kind to 45. Note that A holds an active-attacker place (+0x28 = 1).
      4. A class whose +0x11b is 13, 6 or 7, or a dealer (type 5), may say line 0x11 (a taunt) after it: 20 %, when gestures are allowed (Ambient_MayGesture).

Brain_QueueAttack's first argument is the stick angle in radians, −1 for none. It is written with the command (The attack action).

Facing the target in a fight

What turns a fighter toward his target once the run-in is over. Confirmed (code) at the addresses cited.

  • Nothing carries over from EngageEnemy. Its End (0x002af8d0) only clears the actions and restores the turn boost; its stop (0x002afa08) clears the actions, sets gait 0 and, once the human stands, turns him to the target with a timed body turn (Human_TurnToFaceObject 0x00221cd8, 0.3 s), which does not use brain +0x110. Whatever it left in the brain's move fields is wiped by the next update's Brain_StopMove (0x0028ac18, the first call of Brain_UpdateGoals): speed +0x114 = 0 and heading +0x110 = the human's own facing (Characters).
  • The fight goal never writes the move. FightGoal_Process (0x002b3ab0), its reposition, tackle and grab tries, the attack action (0x002fad70, which only asks gait 4 for a charge kind) and the block goal store nothing to +0x110 or +0x90.
  • So the facing follows the target only through these:
    • the move-to-human action (MoveMelee), which the fight goal queues to close to reach and the reposition queues every time it waits: while it moves him, +0x110 is the heading to the target and the speed a gait (above 0.1), so he turns toward the target by the stance's 24° per update while he strafes. Its Update writes nothing in the update it ends (in the band, no circling), and its Abort re-aims +0x110 only for a "no stance" class.
    • the attack's own turn: the attack starts (0x00261c80, 0x00262368, 0x00261a08, the grab 0x0026c548, the tackle 0x002707a8) turn him to his target (the brain's, Combat) with Human_TurnToFaceObject, and the steer onto the target (Attack_SteerToTarget, Combat) runs to the clip's first event.
  • Otherwise nothing. In a fight stance with no movement action running, +0x110 is his facing and his speed 0: the control neither turns him nor starts a turn on the spot, and he does not track the target (no lock-on turn: that is the player's stance control, which an AI never runs). Inferred from the above: a fighter who stands in his band with no side to circle to keeps his last facing until the next move or attack.

The reach of a kind

Attack_ReachSquared(d², A, kind) (0x00230d00) gives the square of how close A must be to start kind. The fight goal moves A in when the distance² is above it (step 12). Ten other goals ask it too (Boxer, BigBrawler, BigFighter, BigFighterA, HideAndSeek, ObjectPile, SaveHuman, FollowAndDefend, the dealer, and the fight goal's grab try). Confirmed (code), jump tables 0x0055c0e0 and 0x0055c110.

The reach is the near value of one anim id: AttackTable_GetReach(A, anim) (0x002544a0), the float at +0x4 of that anim's 16-byte row in A's attack table (record +0x160, Combat). It is used as is: no scale, no capsule radii, and not the far range (+0x8, which Human_CanStartAttack uses instead). The anim is chosen by kind:

Kind Anim id when A holds nothing
0 24 at a run or sprint; 23 at a walk; otherwise 194 when A's target is knocked down, else 11
1, 3, 5-9 12
2, 4 11
10 25
11 21
12, 13 193, 194
14 237
15, 16, 17 664, 653, 657
19, 20, 21 0, 1, 3
22 (grab) 72
23 (throw) none: 12 m always
32 104
18, 24-31, 33-44 none: 2 m (empty-handed)

When A holds an object, kinds 10-17 and 19-23 and 32 keep the anims above. The rest go by the held object's anim set (0x00231a80):

  • Set 1 (a throwable), kinds 0 and 1: 12 m when A's brain is not type 5 and the distance is above the melee near range (0x00510ab4, Melee ranges). Otherwise kind 0 anim 47, kinds 1 and 5 anim 45.
  • Set 2: kind 0 anim 41; kinds 1-5 anim 39 (the test is kind < 6, unlike sets 1 and 3).
  • Set 3: kind 0 anim 36; kinds 1 and 5 anim 34.
  • Set 5: kinds 0-9 30 m. Set 4: kinds 0-9 15 m.
  • Anything else: 2 m.

The far-reach test of Human_CanStartAttack maps kinds 1-9 to their own clips (12-20) and kind 22 to 70 (Combat). This one uses only clips 11 and 12 for kinds 1-9, and 72 for the grab.

Picking the attack kind

Brain_PickAttack(B, T, filter) (0x0028e708) returns one of the 45 attack kinds, or 45 for none. filter is a member-function pointer called as filter(A, T, kind). It is passed as one 64-bit value in the old GCC layout, s16 delta, s16 index (−1 for a non-virtual function; otherwise a vtable slot), then the function address; so the constants the decompiler shows, 0x002240e8ffff0000 and 0x00224778ffff0000 (read at 0x00563ff8 by 0x002b5768), are delta 0, index −1 and the function, not a kind mask. Brain_PickAttack applies the delta to A and calls through the vtable when the index is not negative (0x0028e708). Confirmed (code). Two filters are used:

  • Human_CanUseAttackKind (0x002240e8) by the fight goal's first pick, by FightGoal Resume and by EngageEnemy;
  • Human_CanStartAttack (0x00224778) by every other caller. It is the same per-kind test plus range, and the fight goal falls back to it when a kind cannot start.

Confirmed (code) at 0x0028e708 unless marked.

  1. Set-up.
    • n = the number of attack slots taken on T (T's brain list +0x1a4); m = T's maximum (+0x1e4, 4 by default).
    • c = the number of slots taken on A himself (B +0x1a4); M = B +0x1e4.
    • running = A at gait 4 or 5 with no held flags (0x00223a60, 0x00223a98).
    • armed = A holds an object.
    • rear = T is grabbed from the rear (state 0x20).
    • The pattern read: when T is pad-controlled, T's target is A, and A's class threshold t = (16 − class +0x37) / 16 (0x002236c8) is above 0:
      • f = the larger of T's two pattern bytes (+0x5d0, +0x5d1) × 0.05 (0x00418398);
      • pattern = (t ≤ f).
  2. T cannot be approached. This is when Brain_CanBeChased on T's brain (0x0028abc0) is false, or B +0x2d3 is clear. Return kind 0 (X1) when A holds a throwable object or one of kind 4 or 6; otherwise return 45.
  3. The armed bonus. bonus = 100, or 0 for a class whose +0x11b is 9, 10 or 13.
  4. Candidates. For every kind k from 0 to 44 that passes filter, these rules apply in order:

    1. Running. While running, only kinds 0, 19, 20 and 21 are candidates. When not running, 19 and 20 (the charge and the dive) are not candidates. Kinds 0 and 21 are always allowed. (AttackKind_IsCharge 0x00229b60 is true for 0 and 19-21.)
    2. The hurt rule. Kinds 31-34 and 42-44 (the moves made while grabbed or on the ground) are left out when A is hurt (0x00222ff8) and B is attackable (Brain_IsAttackableBy(B, none)).
    3. The weight. w = the weight: B +0x298[k], or the override table at B +0x208.
    4. Crowded. When c > 1:
      • kinds 10, 25 and 36: w += w × c / M (the snap, the throw from a grab);
      • kinds 14, 21 and 24: w ×= 1 − c / M (the tackles, a strike in a grab).
    5. A busy target. For kind 22 (the grab) when n > 1: w += w × n / m.
    6. Armed. When armed, kinds 0-9 with w ≠ 0 get w += bonus.
    7. A rear-grabbed target. When rear, kind 22 with w ≠ 0 gets w += 200.
    8. The pattern. When pattern, kinds 6-9 (the three-hit enders) get w += w × f.

    Each scaled weight is truncated to an integer. The kind and its weight are appended, and w is added to the total. 5. The draw. r = Random_Int(total). The answer is the first candidate whose running sum of weights exceeds r. With no candidates, or a total of 0, the answer is 45.

So a crowded AI favours the snap and the throw and gives up tackles. Several attackers on one target make grabs likelier. A weapon in hand makes the plain strikes likelier by a flat 100. A rear-grabbed target, held by a gang mate, invites a grab (+200).

The per-kind test Human_CanUseAttackKind(A, T, k) (0x002240e8), confirmed (code). "A free" below means A's state word has none of 0x7bf9e9f7ff0.

Kinds Allowed when
0, 1 A holds a world object whose type byte +0x87 is 1-3: A free. Otherwise as 2-9.
2-9, 11 A free, and T's state has none of 0xe3000
10 (snap) A free, and the near human in sector 2, 3, 4, 5 or 6 of A's sector record (flag bit 1, within 1.5 m) is in A's own attack-slot list (B +0x1a4, Brain_HoldsAttackSlot(B, him)): someone attacking A from his side or behind.
12, 13 (grounded strikes) T ≠ A. Either T is high or busy (Human_IsHighOrBusy 0x00225200), or T lacks state 0x2000 and has held flag 0x400000. Then A free.
14 T is A (1), or T is knocked down and A is free
15 A free; then A holds spray paint (1), or A is not pad-controlled
16-18 A free and T has none of 0x40100f0800; for class 0x77 only without an object of kind 4 or 6 in hand
19, 20 (charge, dive) A free, T not down (Human_IsNotDown 0x00225500), A at run speed
21 (tackle) A free, T's human flag +0xe0 bit 0x40 clear, T free, T without held flag 0x40
22 (grab) A free, A's power meter (record +0x148) ≥ his maximum power / 5, T's +0xe0 bit 0x40 clear, and T's state none of 0x7bfdc8f7fd0
23 A holds a throwable object
24-30 (moves in a grab) A grabbing (0xc0), none of 0x7bf9e9f7f00, and the human A holds (+0xc4) is T
31, 33, 34 A grabbed (0x30), none of 0x7bf9e9f7f00, and T holds A (T +0xc4 = A)
32 A grabbed from the rear (0x20), none of 0x7bf9e9f7f00, and T +0xc4 ≠ A
35-40 (moves while mounted) A tackling (0x400), none of 0x7bf9e9f73f0, A +0xc4 = T
41 the same, and A carries cuffs
42-44 A knocked down (0x80000) with the Grounded reaction goal (0x17) running: allowed. Otherwise A tackled (0x800), none of 0x7bf9e9f73f0, and T +0xc4 = A.
others never

Human_CanStartAttack(A, T, k) (0x00224778) first refuses in these cases:

  • T has held flags 0xc08200, unless T has state 0x2000 and A has state 0x1000;
  • A has held flags 0x5c7eee0;
  • A has damage pending (record +0x118);
  • the distance is beyond the kind's far reach.

It then runs a per-kind test, given in full on Combat. Confirmed (code), all kinds read. It matches Human_CanUseAttackKind above except here:

  • 10 (snap): A free only; no sector or attack-slot test.
  • 12, 13: T must be high or busy (Human_IsHighOrBusy); the other way in (T without 0x2000 holding 0x400000) is not accepted. A must also hold none of 0x48000. T = A still passes.
  • 14: T = A does not pass; T must be knocked down and A free.
  • 15: T's state must also have none of 0x40100f0800.
  • 22 (grab): T must also hold none of 0xc12200, and a T in state 0x20 (grabbed from the rear) only when A is in front of him (Human_GetSideOf(T, A, 1) = 0).
  • 23 (throw): A free, and the held object throwable with sets 1-3 counted for an AI too (Human_HeldIsThrowable(A, 1)); CanUse asks (A, 0), which counts sets 1-3 only for a player.
  • 27, 28 (in a grab): A in any of state 0x10c0 and holding T; no free test.
  • 38, 39 (mounted): A in any of state 0x1400 and holding T; no free test.

So for grabs (22), tackles (21) and ground strikes (12-14) it mostly mirrors the pick. The start test is stricter on the grab's target and on the ground strike's target, and looser for moves 27, 28, 38 and 39, which start even while A is busy.

May A attack now: Brain_CheckAttack

Brain_CheckAttack(B, kind, armed target) (0x002906b8) returns 1 for "go". Every other value is a reason to wait, which the reposition reads. The tests run in this order. Confirmed (code); the meanings in brackets are inferred.

Result When
2 B's own next-attack time (+0x1e8) has not come
0 the kind is 45
3 T may not be attacked by A (Brain_IsAttackableBy)
1 a charge kind (0, 19-21) while A is out of the fight stance (a charge needs no place)
4 The target holds an object, A's brain is not type 3, the game mode word is not 60-64, and neither T nor T's target is busy. Then, when A is empty-handed, his turn boost +0x0b < 1, and A and T face each other (facing dot < 0), and A is his gang's leader or within 15 m of him. (Hold back from an armed man near the leader.)
8 Only for an empty-handed A who is not yet one of T's active attackers (and not class 13 or 6). Another human in T's slot list, not A, not busy, without held flag 0x300000 and holding a heavy object (0x00224080: anim set not 1 or 5), is ready to attack (his cooldown passed). (Let him go first.)
5 The police have him. This applies when A is not a cop and T's target is not A. Either T's gang is wanted (+0x5e8) and a cop holds a slot on T, or T's target is a cop. For a cop A, it applies when T's target threatens him (0x00290138). Also for a cop when T is tackled or has state 0x10000000 or 0x10000000000 and is held by someone other than A.
6 A holds a throwable object or one of kind 4 or 6, and a gang mate in a 40° cone ahead (Gang_NearestInCone 0x0029df78) is tackling or is nearer than T. A shuffle action is queued. (Do not throw through a friend.)
7 A is a Warrior (type 3), T is class 13 and not targeting A, A is empty-handed and not yet an active attacker, and a player in T's slot list who is busy tackling, throwing or front-grabbing stands within 0.75 × near of T. (Do not hit the boss while a player has him.)
1 A takes, or already has, one of T's active-attacker places (Brain_ClaimActiveAttacker, below)
1 / 0 kind 22 only: 1 when A is the near human in sector 3, 4 or 5 of T's record (behind him), else 0
0 otherwise (no place free)

Attack slots, active places and spacing

A target limits his attackers twice. Confirmed (code) at the addresses cited.

  • Attack slots (T brain +0x1a4, at most +0x1e4, 4 by default from Brain_Init 0x0028a570) are taken when an attacker targets him (Brain_ClaimAttackSlot 0x0028df30, from Brain_SetTarget). A full list gives the slot of the farthest holder to a nearer newcomer, or to one forced in, and clears the holder's target.
  • Active-attacker places (T brain +0x1f0, four handles) are taken by Brain_ClaimActiveAttacker (0x00291008) only when an attack is about to be queued. Only the first s places are used:
    • s is T's spacing byte +0x14a;
    • while T is down, out of the fight or arrested (state any of 0xe0000), s is +0x14b.
  • The spacing bytes start at 1 (0x0028a570). Each slot claim raises them:
    • +0x14a to at least the attacker's gang's CfgGang value 2 (gang record byte +0x6c);
    • +0x14b to at least its value 3 (+0x6d);
    • a Warrior target (type 3) keeps +0x14a at 1.

Both return to 1 when T's slot list empties (Brain_SetTarget 0x0028cfe0).

So CfgGang's second and third values are how many of that gang may swing at one standing (or one downed) man at once. - A claim succeeds in three cases: - A already has a place; - a free place exists and either T's +0x1ec has passed or someone already holds a place; - A is T's own target and +0x1ec has passed, in which case A displaces the first holder.

A holder is released when his fight goal next waits (+0x28, Brain_ReleaseActiveAttacker 0x00291178), when he blocks, and when the fight goal is suspended. - When T may be attacked again (+0x1ec, Brain_SetAttackableTime 0x00290e78): the attack action's Start, when neither human is busy, adds t to max(+0x1ec, now). - t = Attack_GetNextAttackDelay(A, kind) (0x00231590) × 1.0 (0x00510ad0) / s, rounded. When s is 0, t is used unscaled. - Attack_GetNextAttackDelay is the playing time in ms of the kind's anim on A: - 11-21 for kinds 0-9 (kind 0: 11, or 194 against a downed target); - 25 for 10, 21 for 11, 193 / 194 for 12 / 13, 237 for 14, 664 for 15; - the longer of 653 / 655 for 16, and of 657 / 659 for 17; - 0 / 1 for 19 / 20; - 3 + 5 for 21, 70 + 72 for 22, 466 + 467 for 23; - the throw clips for 25-29; 118 for 30; the grab-escape clips for 31-34; 225 for 35-39, 248 for 40, 252 for 41, 246 for 43, 242 for 44; - the object set's clips when A holds a bat or bottle set (sets 2, 3), with kinds 0, 1 and 5; - 100 ms for anything else, and for kinds 0 and 1 against a downed target.

So the gap a target gets between attacks is one swing's length shared among the s attackers allowed.

The reposition

When Brain_CheckAttack says wait, the fight goal calls FightGoal_Reposition(d², goal, T, reason) (0x002b2fc8). It queues a fidget now and then, and always a move-to-human action that holds A in a ring round T. Confirmed (code).

  1. A is close-in when all of these hold:
    • T is empty-handed, or both are armed and A is one of fewer than two slot holders;
    • the reason is not 7, 8 or 3;
    • T's own target is A, or T has no target and A is first in T's slot list.
  2. A tactic ring applies when A's gang has a tactic of type 2 (Defend) or 0xd.
  3. The ring's radii:
    • inner = 2 × T's capsule radius when close-in or in a tactic ring, else (near + far) / 2 = 4 m;
    • outer = 0.95 × far = 4.75 m, and never less than inner + 0.1 m;
    • for reason 5 (the police have him), a non-cop uses inner = 3 × far = 15 m and outer = 16 m;
    • the limit is 2000 ms, or 6000 ms for reason 5.
  4. A taunt, once per random 1000-2000 ms (+0x20). All of these must hold:
    • T is at least 2.75 m away (a quarter of (1.1 × far)²);
    • fewer than two humans hold slots on A;
    • A is not close-in, not in a tactic ring, and T is empty-handed;
    • gestures are allowed.

For reason 7 it happens only 11 % of the time. It is a fidget action on anim 0x25b with speech line 0x11 (cheer2). A Warrior uses line 0xc3 (turf_defend) instead 31 % of the time, when the game flag 0x56e1 is set (CfgEnableTurfInvasion, Game-state functions).

The timer and the order (confirmed (code), 0x002b2fc8 and FightGoal_Init 0x002b2c20, read 2026-10-20): the fight goal's Init sets +0x20 to now + a random 750-1000 ms. Each reposition call with now past +0x20 first re-arms it (now + 1000-2000 ms, Random_IntRange on stream 0x006eb880), whether or not the taunt then happens; the tests run in the order above, Ambient_MayGesture (0x00291ed0, which takes a gesture slot) after the others and the 11 % roll after it. The fidget action is queued before the move-to-human action, so the waiter plays it standing and moves on once it ends.

Which clip (confirmed (code)): the fidget plays anim 0x25b (Human_PlayHeldIdle 0x00231ac8), whose clip the lookup (Human_GetDynamicAnim 0x00221a00) takes, with no override, from the gang's group 0x25b at a random variant: GangClips_Get (0x00163fc0) draws Random_Int(8), 0 to 8 inclusive, and returns nothing for 8 or for an empty slot. The fighting gang fills slots 0-3 with the four combat_fidget_* clips (alert state), so each of them plays 1 time in 9 and the character's own clip for the id 5 times in 9; a calm gang's group is empty and the character's own clip always plays.

At runtime (confirmed (runtime), PCSX2-MCP fork 2.9.129, 2026-10-21, scenario warriors_taunts with the say-command hook on Human_SayCommand 0x002205e0): in level99's sparring fight against a still player, the three Warriors said cheer2 from FidgetAction_Start (return address 0x002fa090) 11 times in the 1,198 updates from their run-in (updates 502-1700): one Warrior once, the other two five times each, each one's taunts 37 to 582 updates apart (the 1000-2000 ms timer allows one every 30-60 updates at most). Two earlier recordings of the same state end 12 updates sooner, before the eleventh taunt, and hold 10 each: the first ten, at the same game ticks, so the fight is deterministic from that state. Coney's count over the same span, printed by the level99 sparring disc test for its eight AI sequences (2026-10-21, two runs), is 0-2: 2 or 1, 0, 0, 0, 1, 0, 1, 0. Coney's waiters taunt far less often than the original's; why is an open question. 5. The move-to-human action (inner, outer, the limit above).

So waiting attackers circle in a band of 4-4.75 m (5 m far range), and the one T is fighting stays close. The "shuffle" is this band-keeping move, not a dedicated sidestep.

The tackle try

FightGoal_TryTackle(goal, T) (0x002b2e28), step 3 of the fight goal. Confirmed (code).

  • k = A's gang's CfgGang value 7 (gang record byte +0x70).
    • When k is 0, or B's tackle weight (kind 21) is 0: clear B's tackle meter (brain +0x148, s16) and do nothing.
    • When the meter ≤ (7 − k) × 6: do nothing. With k = 1 the threshold is 36, the meter's cap, so it never fires.
  • The kind is 21 (the tackle). A cop (type 1) uses kind 0 (X1) instead 75 % of the time.
  • It goes ahead only when all of these hold:
    • Human_CanStartAttack passes (in reach);
    • T may be attacked by A;
    • T's gang is not wanted with a cop already on him;
    • A takes an active-attacker place (not forced).

It then queues the attack (no angle), clears the meter, notes the place, and the goal waits. - The tackle meter is kept by the think handlers of cops (0x00300908), gang soldiers (0x00304840) and the Warriors (0x0030543c). These run once per 0.2 s. - It rises by T's gait (+0x1a8, capped at 36) while T moves and either holds a weapon (0x00224060) or moves away (T's velocity, 0x003a1f00, has a non-negative dot product with A's facing). - Otherwise it falls by 2 (not below 0).

So a target who keeps running off, or who swings a weapon, is tackled sooner, and a high k makes it faster.

The grab and snap try

FightGoal_TryGrab(d², goal, T, &angle) (0x002b3360) runs once A may attack and is in reach. It returns 1 to make the fight goal wait. Confirmed (code). Sectors are the eight 45° sectors of the sector record (sector k at record +8k, flags at +8k + 4). The stick angle of a sector k is A's heading − k × 45°.

  • The angle starts at −1.
  • Kind 10 (the snap): the first of A's sectors 4, 5, 3, 6, 2 whose near human (flag bit 1) is in A's own attack-slot list (B +0x1a4: he is attacking A) gives the stick angle: heading + π for 3-5, heading + π/2 for 6, heading − π/2 for 2. The kind becomes 1 (square), so the press is square with the stick to that side or back. This is how the player's snap is made (Combat).
  • Kind 22 (the grab): when A is in T's sector 2 or 6 (at his side) and T is grabbed from the rear, A moves in (move-to-human, 2 × T's radius to the grab reach, 1500 ms) and returns 1. At his side otherwise, the grab becomes kind 0 (X1).
  • Kinds 12, 13 (strikes on a downed man): when T is tackled, or has state 0x2000 or 0x10000000000, and a friend of A holds him (T +0xc4), A moves in to 0.6 × near to 0.6 × near + 1 m (1500 ms) and returns 1.
  • Other kinds: a rear grab. All of these must hold:
    • A is empty-handed;
    • A's gang's CfgGang value 8 (+0x71) g is above 0, and B's grab weight is above 0;
    • A is the near human in T's sector 4 (behind him);
    • Random_Int(100) < g × 25;
    • the grab can start.

The kind then becomes 22. - Then, for all kinds: when T is grabbed from the rear, the kind is not 22, and the grab is usable (Human_CanUseAttackKind), the kind becomes 22 and the try returns 1. "Usable" needs one of these: - B's grab weight is above 0; - B has goal 0x88; - the game word +0x158 is set.

A man held from behind by a mate gets grabbed in front. - A heavy object in hand (0x00224080): - Take the nearest gang mate who is not T and not busy. - Kinds 1, 3 and 5-9 with that mate within near (3 m): the kind becomes 0 and the try returns 1. - Kinds 0, 2 and 4: the try returns 0 when any of these hold: - the mate is tackled, or has state 0x10000000 or 0x10000000000; - T is beyond far; - the mate is both farther than T and farther than 1.6 m. - Otherwise: - against a rear-grabbed T, the kind is picked again with Human_CanStartAttack; - against T grabbing from the rear or mugging, the try returns 0; - else A steps in to the kind's reach (1500 ms).

  The try then returns 1. (Inferred: a swing with a heavy object is shortened or held back when a mate stands
  close.)

The fight goal's own time limit (goal +0x08) is always −1 (FightGoal_Init); its duration goes only to +0x24.

Closing on the target

Below the fight goal sit the Melee goal (8) and the FindEnemy goal (0x41) that Brain_PushFightGoal pushed (Starting a fight). When the fight goal ends, most often at once because the target is beyond 1.1 × the far range, Melee runs; it sends the fighter in with an EngageEnemy goal (0xb) and pushes a fresh fight goal once he is in range. Confirmed (code) at the addresses cited; the sequence confirmed (runtime) below.

Melee ranges

Every brain starts with near +0x13c and far +0x140 from two globals (0x0028a650, 0x0028a664, reading 0x00510ab4 / 0x00510ab8), which CfgSetMeleeRange sets from config_preload2.lua to 3 and 5 m (1 and 4 in the executable's image, before the config runs). Nothing per class or power class changes them: the only other writers are BrSetMeleeRange (no script calls it) and GoalBossDiego (0x002a1b50, 1 and 2 m). So the far range is 5 m for the sparring Warriors (power class 40) and every other AI. Confirmed (code) at the writes above; confirmed (runtime), PCSX2 2.9.94, read from the user's save states 1 and 6 (all 19 brains 3.0 / 5.0) and in the lesson-12 trace below.

The Melee goal

Goal_Melee builds it with 0x002ade10(goal, brain, duration, 4000): +0x08 the time limit (the duration), +0x10 the target's handle (−1 at first), +0x14 1.1 × far, +0x18 4000, the duration of every fight goal it pushes, +0x1c a step counter. End (0x002ade60) sets brain +0x2d3 and calls 0x00226f70. Brain +0x2d3 ("may approach", 1 when the brain is made) is cleared below when the target cannot be chased (0x0028abc0: the target brain's reachable byte +0x11e clear, a train near it, or on fire), when the last move failed (+0x284), or when the target is outside the gang's turf (0x0028ff58).

Process (0x002aebf8), when the goal's time limit has not passed:

  1. While actions are queued, wait.
  2. Choose the target. With threat response ≠ 0, the best-scoring valid enemy in the enemy list +0x164 (Brain_PickBestEnemy 0x0029f230, the score), set as the target. With threat response 0, only the current target, kept while valid and holding an attack slot on it (else the target is cleared and the goal is done).
  3. No target: done when the enemy list is empty, or when its nearest enemy is hidden in shadow (+0x2d4) or fails 0x002225d8; otherwise push a Spectate goal (0x10) and wait.
  4. With a target (0x002ae2e8), with d the distance and R = 1.1 × far: a new target resets the counter and sets +0x2d3.
    • Armed (holding a weapon, 0x00223ea0 or 0x00224000), or unarmed, +0x2d3 set and d ≤ R: when the line of sight (0x00222288, eye heights 1.7 m) holds, either walk to him in the fight stance (a move action, gait 2, radius about 1.0 m) when d > 1 m and the straight line to him is not walkable (0x002221b0), or push a fight goal with duration 4000 and run again (returns 1); without the line, a move-to-human action (2000 ms), an EngageEnemy goal, or a 100 ms wait.
    • Unarmed, +0x2d3 set, d > R: push an EngageEnemy goal on the target when 0x00290588 allows it (below), else a move-to-human action to 2 × far (4000 ms) and a 100 ms wait.
    • Without +0x2d3 (the target cannot be chased): pick up a weapon nearby (0x002ade90, brain +0x265), a throw of a held object (kind 0x17) at a target within 4 × R, at most one in the game every 6 s, or a positioning move (0x002ae028, four modes by distance and a draw); not traced further.

0x00290588(brain, target) allows the chase unless the brain is not type 1, the target's own target is someone else, and the gang's wanted timer (gang +0x5e8) is set and 0x0028de98 refuses; a type-1 brain (police) also checks 0x00290138. With no wanted timer it allows it.

The FindEnemy goal

Built by 0x002c0430 with 90.0, 30.0, the byte at 0x00510adb and the duration (+0x1c); its time limit is its own +0x20 = the push time + duration (none for −1). Process (0x002c0748): past +0x20, done. Target still valid → Brain_Fight(brain, target, duration, 0), which pops Melee and FindEnemy and pushes the three goals again. No valid target → fight stance off (0x0022fed0) and done, except for a brain with +0x28d set and +0x264 ≥ 0 in a gang whose +0x18 is 1, which may push a Chase goal (12, 0x002b04f0) on a new enemy. So FindEnemy runs only when Melee has ended and restarts the fight while the target lasts.

EngageEnemy: the run-in

EngageEnemyGoal_Init (0x002af5b0): +0x10 the target's handle, +0x20 the attack kind (45 = none chosen), +0x2c a taunt (0), +0x30 2.56 (a squared distance: 1.6 m), +0x34 "start slowly" (the human's gait +0x1a8 below 3 and not class 13), +0x36 1. Start (0x002af670): +0x35 (charge armed) is set when the target is at least far (5 m) away; a type-2 brain may taunt (anim 0x47); next shout +0x18 = now + 4000, next re-target +0x1c = now + 2000; raises brain +0x0b by one (restored by End, 0x002af8d0, which also clears the actions). It has no time limit. Process (0x002afa48), each update:

  1. Actions blocked → done; 0x00228428 → wait. The fight stance is dropped (it runs, not shuffles).
  2. Stopping (+0x39, set by the stop below): wait until the human's +0x1a8 is 0, turn to the target (0x00221cd8, 0.3) and end (2).
  3. Target no longer valid, hidden in shadow, or 0x00290588 refusing → stop.
  4. Every 2 s (not class 13): the nearest enemy replaces the target when it differs, the target is farther than far and the new one is seen (Human_CanSeeHuman, near 3 m, in shadow 2 m).
  5. The target brain's +0x1ec is cleared (he may be attacked at once).
  6. Re-plan when the target's heading turned more than 45°, he is within 1.6 m, he slowed from a run, or 250 ms have passed; a shout every 4-4.5 s (0x002af928). Otherwise wait while the move action runs.
  7. The last move failed (+0x284) → stop.
  8. Gait 4 (run); 5 (sprint) when the target runs (gait > 3) and the fighter's stamina is above 50 % (Human_GetStaminaPercent 0x00222fa0, Sight).
  9. Stop (when the target is in sight, 0x00222288) if he cannot be attacked by this human now (Brain_IsAttackableBy, 0x00290ea8), at any distance; and, within 0.75 × far (3.75 m), if his actions are blocked (he is busy, as when another Warrior is hitting him) or the charge is not armed and he walks or stands (when he runs, it arms the charge instead).
  10. Give up (done) when the target is out of sight, at least 10 m away, and either 20 m away or brain +0x28d set, and his gang's +0xdc is 0.
  11. Charge: within 1.6 m with the charge armed or the target running, below.
  12. Move: aim at the target's position led by his facing × his speed (+0x1ac), turned 9° per attack slot index, alternating sides, when he faces away (sector 3-5 of 0x0029eb38; inferred: to fan the attackers out); aim straight at him when no lead applies or the led point is not reachable in a walkable line (0x002221e0). The running move action gets the new point and gait; else a new move action (radius 0.5 m, a random 0-500 ms start delay the first time when "start slowly").

When EngageEnemy ends, Melee is on top again and, now within R, pushes the fight goal.

The charge in EngageEnemy

Step 11 of EngageEnemyGoal_Process (0x002afa48) runs within the run-in distance (+0x30, 2.56 → 1.6 m) and when T runs (gait > 2) or the charge is armed (+0x35). Confirmed (code).

  1. A taken sector. Take the sector of T's record that A approaches from (Human_GetSectorOf 0x0029eb38 of A's bearing relative to T's heading). When another human is the near one there, the charge is disarmed and A only moves (step 12). When A himself is that human, A waits this update.
  2. The kind (once; +0x20, 45 = none):
    • A cop (type 1) takes kind 0 (X1) with 50 % (80 % against a gang member, class +0x11b 10), else 21 (the tackle). It is forced to 0 when T's +0xe0 has bit 0x40.
    • Anyone else uses Brain_PickAttack(B, T, Human_CanUseAttackKind). When that is not a charge kind (AttackKind_IsCharge 0x00229b60: 0, 19, 20, 21), the engage goal stops (step 2 of the goal: halt, turn to T, end). Melee then pushes a fight goal.
  3. The start test. When Human_CanStartAttack(A, T, kind) (0x00224778) passes:
    1. Clear the actions; when an action refuses, wait.
    2. Queue one attack action directly (not Brain_QueueAttack), no delay. Kind 0 carries A's heading as the stick angle.
    3. Restore the move gait, disarm, and clear the kind.

When it fails, A keeps running in (step 12).

So a running AI's charge is the run-in X1, the L2 charge or dive (19, 20 when its table has weight there) or the tackle. Anything else ends the run-in and becomes a fight goal.

The durations

The duration given to Brain_Fight (2000 or 8000 by the AI's own fights, −1 by GoalFight) reaches three goals:

  • the first fight goal's deadline +0x24 (−1: already past, The fight goal step 5);
  • Melee's time limit: it counts from when Melee first runs (after that fight goal ends), and once passed Melee ends the next time it is on top, without running. −1: no limit;
  • FindEnemy's +0x20, counted from the push. −1: no limit, so it re-starts the fight for as long as the target is valid.

Every later fight goal, pushed by Melee, gets 4000 whatever the duration was, so under GoalFight the second and later fight goals have a 4 s deadline. The engage goal has no time limit. Confirmed (code) at 0x0029ed58, 0x0029eed8, 0x002ade10, 0x002ae2e8 and 0x002c0748.

Choosing among enemies: the score

Brain_PickBestEnemy(goal, flags) (0x0029f230), called by Melee (flags 0xfffffffd, every term but 0x2) and by 23 other goals with their own masks: the current target is taken as "the previous one", the target is cleared, and the 16 slots of the enemy list (brain +0x164) are walked. A slot whose human fails Brain_ValidateEnemy is skipped; every other gets a score (Brain_ScoreEnemy, below) and then the goal's own adjustment (goal vtable +0x54; every goal seen uses the default Goal_AdjustEnemyScoreDefault 0x0029f3a8, which adds 3 when the candidate is the previous target). The best score wins only when it is above 0 and above every earlier one (ties keep the first); after every 5 slots the walk stops early if a winner exists. The winner (or none) becomes the target (Brain_SetTarget). Confirmed (code).

Brain_ValidateEnemy(brain, human) (0x0028d358) returns the human when he may be chosen: not dead or out of the fight (state flags 0x100000000, 0x80000000, 0x40000), not being mugged (0x200), his brain's targetable byte +0x120 set; when he is under arrest (0x20000, 0x00223b70) only for a scorer of brain type 0 or 3; a human with an interrogation set (+0x5a0) who is grabbed from the rear or at 50 % health or less only while his +0x360 is empty; and for a scorer whose +0x14d is set and whose gang has a turf (gang +0x17c), only inside it (0x0028ff58). Confirmed (code).

Brain_ScoreEnemy(scorer, enemy, flags) (0x0029ce98) returns −999 for none, an enemy near a train this update (Brain_IsNearTrain 0x0028ab30: brain +0x2e0 equals the game time) or one the scorer may not take (Brain_CanTakeSlotOn 0x0028dc80: the enemy's attack slots are full, the scorer holds none, and no current attacker is farther from the enemy than the scorer). Otherwise the sum of these terms, w being the weights that CfgSetTargetingPoints / CfgSetTargetingPointsEx set (values from config_preload2.lua; the executable's defaults in brackets where they differ):

Term (flag) When Points
base the scorer cannot chase him (Brain_CanBeChased 0x0028abc0 false on the enemy, or scorer +0x2d3 clear, or the enemy outside the scorer gang's turf) −5 (0x00510b94)
0x1 distance always with this flag: beyond the scorer's sight range R (brain +0x130; a gang of kind 1 uses at least 30 m, 0x0028bf00) the score is −999 + 4.0 × (R − distance) (0x00510b4c) [3.0]
0x2 near the leader the scorer gang's leader (Gang_GetLeader 0x00165678) is not the enemy and is within R of him + 3.0 × (R − that distance) (0x00510b50)
0x4 enemy leads his gang the enemy is his gang's leader and the scorer's brain is not type 3 + 6 (0x00510b54) [3]
0x8 stunned 0x100000 set and 0x80000 clear + 1 (0x00510b58)
0x10 out of view outside the scorer's field of view (Human_IsInFieldOfView 0x00222710 with brain +0x12c) −5 (0x00510b5c)
0x20 down knocked down (0x80000) + 1 (0x00510b60)
0x40 grabbed grabbed (0x30) + 1 (0x00510b64)
0x80 grabbed from the rear 0x20 + 6 (0x00510b68)
0x200 targets me the enemy's target (brain +0x124) is the scorer + 4 (0x00510b6c) [1]
0x400 running gait 4 with record +0x08 clear + 3 (0x00510b70)
0x800 train Brain_IsNearTrain × 6 (0x00510b74); always 0 here, since such an enemy already returned −999 0
0x1000 tagging 0x2000000 + 3 (0x00510b78)
0x2000 targets me armed as 0x200, and he holds a weapon (0x00224060) + 3 (0x00510b7c)
0x4000 a player the enemy has a pad (+0x1b0 ≠ −1) + 20 (0x00510b80) [3]
0x8000 arrested 0x20000 −10 (0x00510b98)
0x10000 police the enemy's brain is type 1 + 30 (0x00510ba0)
always no walkable straight line from the scorer to him (0x002221b0) −5 (0x00510b90) [−5]
always he may not be attacked now (Brain_IsAttackableBy(enemy brain, none) false, Sight) −10 (0x00510b9c)
always he is the scorer gang's chosen target (gang +0x10) + 400 (0x00510ba4)

The last four, and 0x8000 and 0x10000, have no script setter; the sixth argument of CfgSetTargetingPoints is read and dropped, and its ninth (0x00510b84, 3) is the previous-target bonus above. Since a winner needs a score above 0, an enemy beyond the sight range is never picked, and a near one that is out of view, unreachable in a straight line and not attackable may not be either. Confirmed (code) at the addresses cited; the values confirmed (disc) from config_preload2.lua.

Sight and "may be attacked now"

  • Human_HasLineOfSight(a, b, hit, exclude) (0x00222288): a ray from a's position + 1.7 m (z) to b's position + 1.7 m (Ray_IsClear 0x0024df40 → WorldManager_RayCast, Collision, mask 0); if it hits, a second ray to b's position + 1.0 m. True when either reaches b. Without an exclusion list the rays pass through materials 30 LOW_FENCE, 2 GLASS, 187 STOREDOOR_GLASS, 122 RAILING, 107 CHAINLINK_NOCLIMB and 1 NONE (the list built in Ray_IsClear), so an AI sees through fences, railings and glass. Its distance is not limited; callers test range themselves. Through hit it returns a word of the cast; Melee reads it as the material crossed and, when it is one of 30, 2, 187, 122, 107, walks straight at the target (gait 4, out of the fight stance) instead of fighting (inferred: to get round the fence or glass between them). Confirmed (code) for the rays; the meaning of hit inferred.
  • Human_IsInFieldOfView(fov, human, point) (0x00222710): true when the angle between the human's facing and the direction to the point is within fov / 2 (dot product against cos(fov × 0.5)). Confirmed (code).
  • Human_CanSeeHuman(fov, range, near², shadow², looker, target) (0x002223e8): false beyond range; a target hidden in shadow (his brain +0x2d4) only when the squared distance is below shadow²; within near² (squared distance) the field of view is skipped, else Human_IsInFieldOfView(fov) must pass; then the line of sight decides. EngageEnemy's re-target passes 9 and 4 (3 m and 2 m). Confirmed (code).
  • Brain_IsAttackableBy(target brain, attacker) (0x00290ea8): normally the target brain's byte +0x11f ("attackable": 1 when the brain is made, 0x0028a570; set by GangSetAttackable through Gang_SetAttackable 0x0016bb58 and by the end of a scripted goal, 0x002d4248; cleared by GoalGrabTarget while it holds, 0x002bb560). Two exceptions: a civilian (type 4) in a gang of kind 23 attacked by a Warrior (type 3) whose own target is not that Warrior, and who has no target or an AI one, may be attacked only when he is the Warrior gang's chosen target (gang +0x10); and a human of class 221 with +0x11f clear is still attackable by an attacker that has no goal 0x1f. Confirmed (code).
  • Sprint check (EngageEnemy step 8): Human_GetStaminaPercent (0x00222fa0) = stamina (record +0x14a) × 100 / Human_StaminaMax (0x00223188, Sprint); above 50 the AI sprints after a running target. AI stamina drains and refills by the player's rules (the drain is keyed on gait 5, not on the pad). Confirmed (code).

The enemy scan

How an AI finds enemies on its own: the enemy list (brain +0x164, 16 slots) is rebuilt by a periodic scan, and the fight and Melee goals pick from it (the score). Confirmed (code) at the addresses cited.

When (Brain_MaybeScanEnemies 0x0028fa18, from Brain_UpdateGoals each update): when the gang changed since the last scan (gang +0x34 ≥ brain +0x160), or when +0x160 + interval has passed. The interval is brain +0x144 (2000 ms, cops 1000 ms) × 4 in the fight stance or when the human's detail level +0x333 is above 0 (× 1 otherwise), so a calm AI near the camera scans every 2 s and a fighting one every 8 s. A scan also needs a token (AI_TakeScanToken 0x00293cd0): at most 5 scans run in one update across all brains; a refused brain tries again on the next update.

The detail level human +0x333 (Human_UpdateLod 0x0023d660, each move): from the distance d to the nearest camera, plus 0.01 m per ms since the time at the human's draw record +0xd8 +0x2c (inferred: time since last drawn): 0 below 30 m, 1 below 60 m, 2 below 100 m, 3 below 115 m, else 4 (thresholds 0x005102f0; the switch 0x005102d4 is 1, 0 would force level 0). The AI uses it to do less far away: steering is off above 2, the corner speed and the riot scale with it.

The scan (Brain_ScanEnemies(brain, filter) 0x0028b358). A human who is down or dead empties his list. Else, with R the sight range (Brain_GetSightRange 0x0028bf00: +0x130, at least 30 m in a gang of kind 1) and c = cos(field of view / 2):

  1. For each of the 32 gangs that is active (gang +0x18) and an enemy of his (Gang_IsEnemyOf 0x00168fe0: the enemy bit of the other's id in gang +0x38; never between kinds 1 and 23, nor during a global truce), each of its 16 members within R:
    1. A member hidden in shadow (his brain +0x2d4) is skipped beyond 2 m, and within 2 m unless the scanner's +0x2d5 is set.
    2. Unless the member's gang is always seen (gang +0xdc): Human_MaySpectate (0x002225d8) must allow him: always for a Warrior and for a brain that may not start a fight (unless its class +0x11b is 10); otherwise, with fewer than 3 fights (+0x2d0), an AI scanner lists only a member less than 1.9 m above him.
    3. The filter +0x15c must accept him: Brain_IsValidEnemyOf (0x0029bf60, Brain_ValidateEnemy on the scanner's brain) for every type but the Warriors, whose filter is Filter_IsThreatTo (0x0029bf98): a cop only within 10 m and when Brain_IsThreat (0x00290138, below) calls him a threat; a civilian (type 4) only of ped type 3 with a threat response and not grabbed from the rear; any other only when he is such a threat; each then Brain_ValidateEnemy.
    4. Unless his gang is always seen, sight: within the near radius (3 m; for a cop scanning a running member 5.2 m, for a member walking or standing 1.5 m) only the line of sight counts, unless the member is a player sneaking in shadow (Human_IsHiddenFromBrain 0x00223c50); beyond it, or when sneaking, he must also be in front (direction · facing > c). Then Human_HasLineOfSight must pass.
  2. The scanner gang's chosen target (gang +0x10) is added when it passes Brain_ValidateEnemy.
  3. Old entries not found again are kept while within R, still valid, and within 3 m or in line of sight; others are dropped. Each old entry's hostile count (his brain +0x152) is lowered first.
  4. More than 16: sorted by distance, the 16 nearest kept. Each human new to the list is sent to the scanner's own event handler as event 0xb (a new enemy; a tactic hears of it through Brain_AddEnemy); every listed human's brain +0x152 is raised by one (Brain_CountHostile 0x0028ef20), and the radar marks the list (HUD_RadarMarkEnemies).

Brain_IsThreat(brain, other) (0x00290138; also Human_ReactToThreat 0x00222a48, which the type event handlers call): never himself; a Warrior whose +0x2e5 is set (hitting back at his chief) and the chief are threats to each other; otherwise a threat when the gangs are enemies or both are players.

The Spectate goal

Type 0x10 (SpectateGoal_Init 0x002b4098, vtable 0x00540270), arguments (keep distance, goal, brain, join, may engage, shortest and longest pause (ms), time limit, taunt). Fields: +0x10 the watched human's handle, +0x14 the next look, +0x18 the next re-pick (now + 3 s), +0x1c / +0x20 the pause range, +0x24 the keep distance, +0x28 join, +0x29 may engage, +0x2a taunt, +0x2b may pick up a weapon (rolled at Start), +0x2c chase when far. Confirmed (code).

  • Start (0x002b4200): picks whom to watch (SpectateGoal_PickTarget 0x002b4158): with join, the nearest human of his own enemy list; otherwise the nearest member of the nearest other gang (0x0016c808). +0x2b = Random_Int(100) < 25 × (a per-gang-kind number, 0x001644f8); +0x2c = 50 % chance, when no goal 0x98 is on the stack. End (0x002b42f0): target cleared, actions cleared, brain +0x284 = 0.
  • Process (0x002b4330), each update: the target is cleared; a failed last move clears +0x2c. With join: fight stance; for a brain of type other than 1 and 4 that may pick up a weapon and is empty-handed, every 30 updates the nearest pickable weapon within 15 m (0x0029d5f0) is fetched (Goal_GetItem). Every 3 s the watched human is picked again. Then:
    1. No valid watched human: done with join, else wait (until the time limit).
    2. With join he is made an enemy (0x0028ff78); tackling → an attack action of kind 0x24.
    3. Unless the watched human's gang has +0xdc set, out of sight → done; in shadow (+0x2d4) and beyond 3 m → done.
    4. With join and may engage, not after a failed move, when 0x00290588 allows a chase: an EngageEnemy goal (taunt 0x8f) when the target runs (one update in five) or +0x2c is set and he is beyond the far range.
    5. Otherwise, with his actions free: fight stance; every pause (random between the two lengths) an optional taunt (0x002fb0f0, with taunt); beyond 10 m a move toward him (gait 2, 4 when he runs or with join, radius far + 2 m); else with join, between the keep distance and 2 m more, 51 %: a watch action (0x002feaf8, 3 s); else a move-to-human action that holds him between 0.95 × and 1 × the keep distance (4 s limit). The keep distance 0 means the far range.

Melee's Spectate (no target, The Melee goal step 3) is SpectateGoal_Init(k, goal, brain, join 1, may engage 1, 1000, 3000, 2000, taunt 1), with k = 0 (the far range, 5 m) or, when goal 0x98 is on the stack, a random distance between 0.75 × near and far: a 2 s spell watching the nearest enemy, with a taunt every 1-3 s, joining in with EngageEnemy when he runs. The dealer's wary goal is (8 m, join 0, 0, 2000, 4000, 8000, 0): he backs off to 8 m from the nearest fighter for up to 8 s.

Fetching an object: GetItem and the object search

The object search Ai_FindObject(A, needRay, filter, claim, needReach, centre, allowLow) (0x0029d5f0) is the general search behind every AI pick-up and smash (21 callers: Spectate, Melee, the Warriors' think, Riot, Destroy, Steal and others). Confirmed (code).

  1. List the world objects round centre (A's position by default; ObjectManager_FindObjects on the object list +0x840, at most 384), nearest first.
  2. Take the first object that passes all of these:
    1. It is not attached to a task (not held or carried).
    2. Its flags (vtable +0x54) lack bit 26.
    3. It is not claimed by A's own gang: object +0xec is the gang and +0xf0 the claim's end.
    4. The caller's filter object accepts it. This is where each caller's radius and kind test live, for example 15 m and pickable for Spectate, and 20 m and breakable for Riot.
    5. For an AI A, no player stands within 1 m of it.
    6. With needRay, a clear ray from A to it (Human_HasClearRayTo, mask 8). Unless allowLow, the ray must not end in result 2: the ray crossed a GLASS triangle (material 2, an intact pane), which the ray's exclusion list lets through while still recording its material (GetItem below). Confirmed (code) at 0x0024df40, 0x00350778.
    7. With needReach, a navigation polygon under it (within the larger of the type's half-sizes +0x78, +0x7c) that A can reach (Nav_CanReach).
  3. With claim, the object is claimed for A's gang for 5000 ms, so gang mates do not run for the same object.

GetItem (type 0x2c; Init 0x002dc7a0).

  • Fields:
    • +0x20 the object's handle;
    • +0x28 the gait;
    • +0x10 a standing point beside the object;
    • +0x24 its type record;
    • +0x2c tries;
    • +0x30 the saved turn boost;
    • +0x31 picked;
    • +0x32 the point is valid.
  • Start (0x002dc7f8) drops an object of kind 4 or 6 already in hand, raises the turn boost by 1, drops the target and runs Resume.
  • Resume (0x002dc8b0) finds the point beside the object: the polygon under it within its half-size. With none, the goal will end.

Process (0x002dc9f8), each update. Confirmed (code).

  1. The object is gone: done. Otherwise look at it (500 ms).
  2. The object's bit 26 is set: wait. A pick-up is in progress (held flag 0x4000): note it (+0x31) and wait.
  3. Someone holds the object.
    • A himself: done.
    • A friend: done.
    • An enemy: he becomes the target.
      • Within 1.1 × far, or A armed, with a line of sight: push a fight goal (1000 ms).
      • Else, when Brain_MayEngage allows: push EngageEnemy.
      • Else: done.
  4. Nobody holds the object.
    1. When it was picked (+0x31): done.
    2. While actions are queued, wait.
    3. Without the point, after a failed move (brain +0x284, then cleared), or after 11 tries: done.
    4. Clear the way. Take the nearest non-friend among A's slot holders who is nearer the object than A is. When he is within 1 m of A, not class 13, and kind 17 can start, A targets him and queues kind 17 (the circle + cross special).
    5. Within 1.5 m:
      • Wait while A has any held flag but 0x40000000.
      • With a clear ray to the object (Human_HasClearRayTo, mask 0):
        • When the ray result is not 2: queue a pick-up action (PickUpItemAction_Init, 0x21) and count a try.
        • When the ray result is 2: step to the point (when beyond 0.4 m; gait 3, radius 0.36 m), turn to the object, and queue attack kind 0 (X1) on himself (Brain_QueueAttack(−1, brain, self, 0, 0x21)), counting no try. Result 2 means the ray from his feet + 1.7 m to the object + 0.1 m crossed an intact pane: Ray_IsClear's default list (30, 2, 187, 122, 107) lets those materials through, but CollisionMesh_RayTestCell (0x00350778) still writes an excluded triangle's material into the result while nothing solid has been hit, so the ray is clear and its result is 2 (GLASS). STOREDOOR_GLASS (187) gives 187, so only material 2 makes him swing. Confirmed (code) at 0x002dce84-0x002dcf44. So an AI never takes an object through an intact pane: he swings at it instead and keeps swinging until the pane breaks (its triangles are disabled, Objects) or the goal ends. Confirmed (runtime), PCSX2 2.9.94, scenario repo:research/traces/scenarios/steal_store.toml (level80's pharmacy under the steal order, no stick input): both Warriors' claimed objects stood behind glass; each reached this branch every 1.2-1.3 s (23 times each in 60 s), no pane broke (hook glass-break), no owned object was taken (hook take-owned) and no money or loot was added. Why the queued kind 0 broke nothing there is not known (an open question).
      • Without a clear ray: turn to it when more than 15° off, else count a try.
    6. Walk to the point out of the fight stance: gait 3 within 3 m, else the goal's gait (+0x28; 4, a run, for WarriorVandalSteal's fetches, 0x002bee78), radius 0.36 m. Confirmed (code) at 0x002dd018-0x002dd080.

The AI's pick-up press itself: AI and the world: Picking up.

Objects in an AI's hands

Who fetches an object, and when an AI lets go. Confirmed (code) at the addresses cited.

Fetching (each pushes GetItem, above, on what Ai_FindObject returns):

Who When Search
Melee (Melee_TryPickUpWeapon 0x002ade90) brain +0x265 set, actions free, empty-handed; every 20 updates, or when asked weapons (filter 0x0053f2d0, mask 0x20000; also throwables 0x30000 when the target is within 15 m and asked), 20 m, or 10 m when asked; gait 4
Spectate joined, not a cop or civilian, 25 × gang factor % rolled at Start; every 30 updates pickable within 15 m (Spectate)
The Warriors' think The Warriors' pick-ups 1.5 m
FollowAndAttack / FollowAndDefend / FollowPlayer +0x265, not blocked; every 20 updates or within 15 m of the enemy smash or throw objects, 20 m
AvoidEnemies empty-handed throwables
Riot, DestroyCar (DestroyCar_GetWeapon 0x002ddd48, 10 m), Destroy, WarriorVandalSteal, the Wander tactic to smash something breakables / weapons
ObjectThrower, ManWeaponPile, ObjectPile, Steal their own objects below

Using. A held weapon changes the attack pick (the armed term) and the fight goal's range test (armed fighters fight at any range within 1.1 × far). A held throwable is thrown by pressing cross aimed at a point (human +0x128 holds the aim's handle): AvoidEnemies throws at an enemy close enough (attack kind 23, unless the object is of type class 6 with +0x5a 0, or class 1 within 2 m); VandalizeItem throws at the object.

ObjectThrower (type 0x4e, ObjectThrowerGoal_Init 0x002a3f50(goal, brain, maxThrows, search radius, pace s, stop radius, flag1, flag2, flag3, callback); Process 0x002a4230):

  1. Fight stance on; wait while actions run or a flag (other than 0x40000000) is held.
  2. Every 15 updates: done (2) when a hostile is visible within the stop radius (HandleList_FillVisible); done after maxThrows throws (0 = no limit). Every 45 updates he glances at his nearest enemy (2 s).
  3. Holding a throwable (or kind 4 / 6): turn to one of the up-to-three target flags at random, aim there (+0x128), queue cross (kind 0) after a random pace/2 to pace seconds; count the throw.
  4. Empty-handed: after every third throw (from the third on), 50 %: turn to the player's camera and taunt (kind 3, line 0x10) once. Else every 5 updates the nearest weapon within the search radius: drop what he holds, push GetItem (gait 2).

Letting go.

  • Speed: running faster than the run speed drops a kind-4/6 object (step 2 of Speed control, above).
  • A Warrior with no enemies drops a kind-4/6 weapon unless his gang's tactic is steal (0x26) (pick-ups).
  • GetItem's Start, DestroyItem's and DestroyCar's Start, Cower's Start, CallGang's init and RunFromTrain (RunFromTrainGoal_DropEverything 0x002f9a80) drop what is held.

World flags

An AI uses a flag (World flags) only when its brain's +0x2d1 is set (1 when made; Brain_SetWorldFlagUse, Gang_SetCanUseWorldFlagsById): Human_NearestUsableFlag (0x00417670) returns 0 otherwise. The chance byte +0x2d2 (10 when made) is written by the same setters but no reader was found. Confirmed (code).

  • Pedestrians (0x002ab4b8, the pedestrian goal's move step): every 20 updates, when he may chat and has walked far enough from his last stop, the nearest usable flag within 10 m that he can walk straight to in under 15 m gets a MoveToUseFlag goal (radius 0.3 m, his gait); a re-use timer at goal +0x6c (65 s, inferred) keeps him from going back at once.
  • HangOut, Patrol, the TravelPath and Wander tactics look up a usable flag the same way.
  • MoveToUseFlag (0x002dbc10): done when his actions are blocked or the flag is disabled. Walk to it (up to 3 tries); while walking, within 5 m, every 15 updates: a human within 0.75 m of the flag ends the goal. At the radius: turn to the flag's heading (beyond 15°, turn rate 0.2), then he is placed on the flag's point and heading and plays the activity's clips (enter, idle, exit, Activities; the clip states after this are not traced).

Cover, cars and trains

  • Hiding is only the hold command's hide tactic and Scatter (Warrior commands): Hide (0x58) walks to a wall point near a hiding flag (gait 3 near the leader, else 5), out of the shadow, then turns to face out. Shadow itself is the human's hidden state (Human_IsHiddenInShadow, state 0x200000) that the enemy scan reads; an AI never seeks shadow on its own (no other goal sets it). Inferred from the setters' callers.
  • Cars. No goal in the AI range drives or enters a car: cars run on their own paths (Cars). AIs meet cars three ways: DestroyCar (0x2e) smashes one from a spot round it; the Pursue tactic, on a crime event (23) at a car, has members within 30 m drop an Investigate goal and a chasing member push Investigate at the crime point (Chase_InvestigateCar 0x002b0c88, 5 m, line 13), unless the offender is friendly; the Wander tactic's Tactic_VandalizeCars (0x00316590). Confirmed (code).
  • Trains. The train (TrainRecord_Update 0x00413d90) pushes RunFromTrain (type 7) on a human near its track: he drops everything, turn boost 3, and sprints (gait 5) to a safe point beside the track, or along it when no side is clear (its rows). While a train is near, the enemy score rules the human out (Brain_IsNearTrain, the score).
  • Doors and breakables on a route. Doors are route links (Following a route); a follower stuck on the way to his formation slot smashes a breakable within 1.5 m (VandalizeItem, FollowFormation).

Which world uses an AI reaches (objects, cars, doors, climbs) through the player's own functions, and which it never reaches, is on AI and the world.

The attack action

The attack action (vtable 0x00542ce0, AttackAction_Init 0x002fa918):

  • Start (0x002fa9a8): checks the target and keeps the start time in the action's +0x18. It sets the brain's +0x1e8 (its next attack, 0x00290e48) to now + the attack delay (Human_AttackDelay, 0x00223800: CfgAttackDelay[kind] × the power class's +0x1c, or +0x20 when the target is down), halved when the target's own target is this human or the brain is type 3. CfgAttackDelay is a table of ms indexed by attack kind (0x00228870 reads 0x006b6658 + kind × 4); config_preload2.lua sets 200 for most kinds, 400 for 12 and 13, 500 for 19 and 21, 1000 for 20, 300 for 32-34 and 0 for 23, 31 and 42. When neither human is busy, it sets the target brain's +0x1ec (when it may be attacked next, 0x00290e78) from a separate per-kind time (0x00231590: the attacker's clip length for the kind, Attack slots, active places and spacing), scaled by 0x00510ad0 / the target brain's spacing byte +0x14a (or +0x14b, by 0x00223b48) when that is not 0; the attacking gang's CfgGang sets those bytes (Combat). This corrects the earlier reading that both took the attack delay. Then it writes the command (AttackKind_ToCommand) into the per-player record +0x20 (PlayerRecord_SetCommand, 0x00147ef0) and, when the action has an angle (+0x1c ≥ 0), a stick of magnitude 1.0 at that angle.
  • Update (0x002fad70): re-writes the command only while now < the action's +0x18, which Start set to now, so never: the command is written once (confirmed (runtime), below). It is done at once when the action's +0x14 is 1, otherwise once the record's +0x08 has none of 0x5c0221f: the attack's held flags (Tasks) decide when the AI is free again. This corrects the earlier "writes the command again while waiting".
  • Abort (0x002fad30): refused while +0x08 has any of 0x5c0221f.

So an AI attack is a press and a wait on the same flags the player's moves hold, and its chain timing is the player's.

Confirmed (runtime), the street civilian (PoizoCiv, class 417, Att_Normal, power class 2) made a type-2 brain and set on the player with GangMakeEnemies and GoalFight (no save reaches level99's CombatWarriors fight, so this is a stand-in with the same attack table). Hooks on 0x00147ef0 and 0x00147ef8:

  • The goal stack was 0x41 (FindEnemy), 0x08 (Melee), 0x0f (Fight); the front action was the attack action, the move-to-human action or, when far, a move action (goal 0x0b, EngageEnemy).
  • Commands seen: 0xd (grab, then a throw with a stick of 1.0), 0xf, 0x10, 0x11, 0x12, 0xe and 0x20 (charge). Each is written once, in the attack action's Start, and read by the dispatcher in the same step; the next step's record update clears it (Tasks).
  • Chains: the second press came 9 updates after S1's (0xf → 0x11 or 0x12), 12 after SS2's, 13 after X1's (0x10 → 0x12), inside the chain window as AttackKind_ChainDelay plans.
  • +0x1e8 was set to now + 700 ms at a chain's first press, + 1100 ms at the second, about + 1400 ms at the third, and + 1750 ms for 0xe and 0x20: each press of a chain pushes the next attack further.

Confirmed (runtime), the three sparring Warriors against a still player (warriors_passive, the sparring fight): with factor 20 (power class 40), +0x1e8 went to now + 4000 ms after a 200 ms kind, + 8000 ms after 0x37 / 0x38 (kinds 12, 13: 400 ms), and + 2000 ms when halved. So a sparring Warrior waits about 4 s between attacks; the still player took 22 attack commands from the three in about 40 s, many of them grabs.

Blocking and countering

  • The warning. +0x200 is a count of attack warnings since the last update: the default event handler (0x00292d80) adds one for each event 0x10, as do 0x002ff898, 0x00302ff0 and 0x00304070, and Brain_Update clears it (0x0028f914). Event 0x10 comes from 0x0021d5c0, called by Attack_Start (call at 0x0026267c) and the square path (0x00287594), each time to the attack's target alone (Combat: attack warnings), which it reaches only if 0x002223e8 passes: within the brain's range +0x130 and its field of view +0x12c (half-angle, radians), or 2 m when the attacker's brain +0x2d4 is set, and then a line of sight (0x00222288). Confirmed (code); the counting confirmed (runtime).
  • When (Goal_TryBlock, 0x0029f098): only when the brain's +0x200 is not 0, that is when an attack on the human was announced this update. The chance is Human_BlockChance (0x00223628: the power class's +0x08, or +0x0c while hurt, × the global 0x00510ac8), a quarter of it for brain type 3. A player attacker whose pattern meets the class's +0x37 threshold (0x002236c8, 0x00418398 on human +0x5d0) is always blocked. Yes pushes the block goal.
  • An AI human cannot block. Confirmed (code) at 0x0027c120 and 0x00147f98. The dispatcher starts a block only when R1 (mask 8) is held in a pad record (Combat): 0x00147f98(record, mask) reads pad record 0x005dd810 + pad × 0x50 with the pad index per-player +0x19, and returns 0 when that is −1, as it is for every AI human. Command 4 (R1 held) is looked at only inside the block branch, once a block runs; it cannot start one. So the block goal's command 4 does nothing for an AI: it never takes state 0x8000 or plays 606, and its hits land. What the goal does give an AI is the no-reaction flag and the counter below.
  • The block goal (0x1b). Confirmed (code) at 0x002b54d8, 0x002b5520 and 0x002b5808.
    • Init (0x002b54d8): +0x16 = 1 (active); +0x10 (the end time), +0x14, +0x15 and +0x17 (an update count) = 0.
    • Start (0x002b5520): +0x10 = now + a random 1000-3000 ms; +0x14 = (rand100 < Human_BlockChance), which allows the counter after a duck (Combat); +0x18 = whether human +0xe0 already had 0x800. With a target: +0x15 = 1 when the class's pattern threshold (0x002236c8) is above 0 and the target's pattern (0x00418398 on human +0x5d0) meets it, and human +0xe0 gets 0x800 unless +0x18.
    • Flag 0x800 on human +0xe0 means no hit reaction: Human_ApplyPendingDamage (0x00265f70) still takes the health but skips the reaction. Process clears it (unless +0x18) on the first update from its sixth on whose record +0x08 has none of 0x5c7eae0.
  • Process (0x002b5808), each update, in order:

    1. With a target while +0x16 = 1: when the target's record +0x08 has 0x400 (0x00228560) or its state has any of 0x7bf9e9f7ff0 (0x00228228), write command 4 and clear +0x16.
    2. While the brain has actions queued: return 0 (wait).
    3. +0x16 = 0: done (2), unless the human is still blocking or ducking (0x00223ad0), then wait.
    4. The block time is over (now > +0x10): when the target is still mid-attack (record +0x08 any of 0x5c0221f, 0x00228428) and its own target is this human, +0x10 = now + a random 1000-3000 ms and +0x15 = 1; otherwise +0x16 = 0, so the goal ends on a later update. Write command 4.
    5. The block time runs:
      • In its last second (+0x10 − now < 1000), when +0x15 = 1 and the human is not ducking (0x00223b28), queue a punishing attack (0x002b5718): Brain_PickAttack with the filter Human_CanStartAttack (0x00224778; the value 0x00224778ffff0000 is that member-function pointer, Picking the attack kind) (class 0x6a instead takes kind 0x10 or 0x11, class 0x59 kind 0x10), then Brain_QueueAttack and +0x16 = 0.
      • Otherwise, with a target and +0x15 = 0, roll the counter: rand100 < Human_CounterChance (0x002235f8: the power class's +0x24 × 100) and the counter test below passes → write command 3 (and set 0x800 again unless +0x18).
      • Otherwise write command 4.

    So the counter is rolled on every update while the block time runs, from the goal's first update, and never once +0x15 is set (a pattern read at Start, or a block extended in step 4). This corrects the earlier reading that the roll comes after the block time.

  • Command 3 for an AI is a grab or tackle counter. Confirmed (code) at 0x0027c120 and 0x0027d6e0. The dispatcher acts on command 3 only for a human that is not pad-controlled (per-player +0x1b = 0), through 0x0027d6e0, which refuses while the human's state has any of 0x7bf9e9f4300, its record +0x08 any of 0x100101f, or it holds an object (0x00231a38). Then, against its target (0x00226e60):

    • the target plays 69-71 (GRAB_MISS, GRAB_INTRO, GRAB_PLAYER_INTRO; 0x00258e88) → the human plays 76 GRAB_FRONT_COUNTER;
    • the target plays 2-4 (TACKLE_MISS, TACKLE_INTRO, TACKLE_PLAYER_INTRO; 0x002590f8) → 9 TACKLE_FRONT_COUNTER;

    each as a paired move (Attack_StartPaired, 0x00262ac8, flag 0x400000; Combat). Both tests also need: the human free (record +0x08 none of 0xfc7eaf7, state none of 0x7bf9e9f7ff0), not hurt (0x00222ff8), the target not knocked down (+0xe0 0x80000), the target's own target this human, 0x002672d0 0 both ways (inferred: face to face), a counter chance above 0, and, while the byte *(0x0051489c) + 0x56e3 is 0, a brain of type 3 or a class whose +0x11b is 13 (0x00223e20). So an AI counters grabs and tackles, never strikes; a strike is answered only by the duck counter (Combat).

At runtime (confirmed (runtime), PCSX2 2.9.94):

  • The street civilian (civ_block, civ_block_fov), at 1.2 m, the player pressing square every 20 updates. With its class's field of view +0x12c = 1.5708, the player's attacks, aimed from in front but outside that test, sent no event 0x10 and it never blocked. With +0x12c set to π, six events 0x10 arrived; Goal_TryBlock saw +0x200 = 2 once and pushed goal 0x1b (+0x10 = now + 1981 ms, +0x14 = 1, +0x16 = 1), which wrote command 4 every update; the civilian never took a block state and kept taking hits.
  • A sparring Warrior (warriors_block, the sparring fight), the player pressing square every 15 updates: Generic1 had 43 warnings and 5 block tries (with +0x200 = 2); the one block goal wrote command 4 every update for about 53 updates while its state stayed 0x3, it played hit reaction 272 and its health went from 1295 to 1278. No command 3 came (the player never grabbed or tackled).

New power-class fields (confirmed (code)): +0x08 block chance, +0x0c block chance while hurt, +0x24 counter chance, +0x37 the pattern-reading threshold (Power classes).

Moving

How an AI moves: gaits and speeds, turning, the move action, the corner speed, the move-to-human action (MoveMelee), path planning, following a route and its jump legs, neighbour sectors, steering round humans, giving way and waypoint queues. The section is on AI moving.

Civilians

The civilian think and events are under Civilians (type 4): toward a nearby player a civilian of ped type 3 pushes Hostile (0x72, 10 m), others a PedReaction (0x6b) with line 0xaf.

How the scripts drive the AI

How the level scripts drive the AI: the six levers and their order, the brain and gang bindings in effect, the strategy library and the usage counts by level. The section is on AI scripting.

Scripted goals and actions

What the first mission's scripts give a brain directly: GoalMoveToFlag, ActLookAt, GoalAddressPerson, GoalPlayDynAnimation, GoalBumLogic, GoalDealer, GoalRiot and GoalDevilRun. The section is on AI scripting.

Gangs, tactics and formations

The gang record, its events, update and deletion, the tactics (the scripted tactic kinds), attacking on sight, the boss fights, spawners, crimes and the police, the Warriors' commands, follow, automatic commands and formations, and the gang functions. The section is on AI gangs, tactics and formations.

Gang coordination in a fight

How the members of a gang share out a big fight (issue #273). There is no fight coordinator: nothing owns a brawl or hands out targets, and each brain meets five mechanisms on its own. Confirmed (code) on Gang fights, and checked at runtime on Rumble's biggest battle (Gang fights: runtime checks):

  1. The target's own limits: at most 4 attack slots on a man, of which only as many as the attackers' gang allows (CfgGang values 2 and 3) may swing at once, with a shared gap between swings (How many hit one man at once, Attack slots, active places and spacing).
  2. A shared score for choosing among enemies: −999 for a man whose slots are full spreads a gang, +20 for the player and +400 for the gang's shared target focus it (Who goes for whom, Choosing among enemies).
  3. Waiting: a slot holder without a place repositions round the man and taunts (What the waiting ones do, The reposition); every fight goal ends within 4 s unless its holder still swings, so members re-choose often (Target switches).
  4. Help calls (event 20): a member hit or warned calls within 20 m, and only the idle answer (Helping a gang-mate, Help calls).
  5. The gang's tactic and team-ups: TacticAttack's 3 s idle check and its seven leader orders, drawn by the gang type's strategy weights when GangEnableAttackStrategies is set (The gang as a whole).

Rumble's two CPU sides run different tactics (side 1 the Warrior attack command's, side 2 TacticConfront then TacticAttack; Rumble's CPU-controlled sides). The step-by-step units an implementer builds from are AI reimplementation, units 1-7; the recordings, their settings and the per-human trace are Rumble: gang battle trace.

The first mission's cast

From level99's scripts and the CfgChar calls (ids and values only). The brain type is the class's behaviour byte; a scripted gang overrides with GangBrDead, GangSetThreatResponse and the goals it pushes.

Script group Types Brain Health Damage / attack tables Gang
the player (Rembrandt) 30, or 32 at checkpoint 1 0 as the player (3 as a class) 1800 DamageFox / Att_Warrior 0 (Warriors)
Ash 38, or 40 at checkpoint 1 3 1800 DamageWarriors / Att_Warrior 0
the teachers (Vermin, Cleon) 27, 2 (Vermin is 26 in the opening) 3 1800 DamageWarriors / Att_Warrior 19 (CombatTeacher); Cleon also 0 (SCleon)
GenWarrior, then the second wave 275; 272-274, 276 (bum models) 4 300 DamageWeak / Att_Bum 19 (CombatEnemy, CombatEnemy2)
sparring Warriors (CombatWarriors) 58, 59, 60 2 1400 DamageNormal / Att_Normal 19
the fence Warriors (FenceWarriors1, 2) 58, 59, 60 2 1400 DamageNormal / Att_Normal 0
the street civilian (PoizoCiv) 417 4 600 DamageTough / Att_Normal 23
bums 271, 272 4 300 DamageWeak / Att_Bum 23
the dealer (FlashDealer) 430 5 800 DamageBoss / Att_Dealer 24

The sparring Warriors' numbers (classes 58-60), confirmed (runtime) on the three CombatWarriors in the sparring fight: power class 40 (GenWarrior uses 39), whose record gives block chance +0x08 0.2, while hurt +0x0c 0.1, attack-delay factors +0x1c / +0x20 20 and 20, counter chance +0x24 0.08 and pattern threshold +0x37 0. With the global 0x00510ac8 = 60 that is a 12 % block try (6 % hurt) and an 8 % counter roll; the threshold 0 means +0x15 is never set at Start, so the counter is rolled throughout a block. Their brains have field of view 1.92 rad, range 30 m, melee range 3 m / 5 m and threat response 2, and the player's brain allows 4 attack slots.

How level99_combat.lua runs a fight (call names and arguments only): the enemy gangs start dead to the AI (GangBrDead(true)) with threat response 0 and are walked into place with GoalMoveToFlag, ActLookAt, GoalAddressPerson, GoalTrackHuman and GoalPlayDynAnimation; a fight starts with GangMakeEnemies, GangSetThreatResponse(2), BrFlush and GoalFight(enemy, player, 0). Allies follow the player through follow slots (BrSetNumFollowSlots(player, 4), offsets (0, 1), (1, 0), (0, -1), (-1, 0), later (0, 2)…), and a crowd tactic (TacticCrowd) and a gang message handler (GangSetMsgHandler(…, 18, …)) frame the fence fight. level99_lesson2 uses GoalDealer and a run of GoalMoveToFlag for Vermin.

The attack tables of that cast, by attack kind (the table's Lua index − 1; weights are relative):

  • Att_Bum: kind 1 (S1) 10, kind 5 (SS2) 10, kind 7 (SSS3) 20; nothing else. Bums only throw square chains.
  • Att_Normal: kinds 0, 1: 10; 2-5: 30; 6, 7: 60; 10: 40; 12-14: 30; 19, 20: 30; 21, 22: 55; 24: 50; 25: 75; 28: 40; 29, 30: 60; 31: 100; 33-35: 50; 36: 10; 39: 30; 40: 65; 42: 200; 43, 44: 100.

The class damage tables

CfgChar's damage argument names one of eleven tables in config_preload2.lua (DamageWeak, DamageNormal, DamageTough, DamageBoss, DamageGhost, DamageCop, DamageRookieCop, DamageDestroyerLT, DamageMoonLT, DamageWarriors, DamageFox), 45 values each, indexed as the damage table (index = Lua index − 1). The values at the indices that write damage, for the tables level99 uses:

Index (anim) Weak Normal Tough Boss Warriors Fox
0 (X1) 23 25 27 30 23 23
1 (S1) 14 16 18 20 15 15
2 (XX2) 46 50 54 60 46 46
3 (SX2) 37 41 45 50 38 38
4 (XS2) 37 41 45 50 38 38
5 (SS2) 28 32 36 40 31 31
6 (SSX3) 51 57 63 70 53 53
7 (SSS3) 42 48 54 60 46 46
10 (snaps) 15 20 20 35 27 27
24 (grab strikes) 20 40 60 65 50 50
25 (throws) 30 50 70 75 57 57
29 (wall throws) 80 100 150 300 230 230

Rembrandt's S1 of 15 (DamageFox) at 115 % gives the 17 seen at runtime (Damage). Evidence: the values are read from the disc's compiled script (confirmed (code) for the reader); the index mapping is confirmed (code) on Combat.

What an implementer needs

Build in this order; each step is testable without the game.

  1. The shared input path. Give every human a per-player record (command +0x20, stick angle and magnitude) and run the dispatcher and the locomotion for every human from it (Tasks). An AI writes the record; nothing else about a human differs.
  2. The held flags (Tasks): without them an AI cannot know when its attack is over.
  3. The brain shell: type from the class's behaviour, think one step in five (staggered by index), update every step, brains before the dispatcher.
  4. Goals and actions: a goal stack of 10 with Start / Process / End / Resume and the return codes 0, 1, 2; an action queue of 8 with a delay, Start, Update and a refusable Abort; goals start only on an empty queue.
  5. The fight goal and the attack action for level99's fights: target, attack slots, the attack delay, the weighted pick from the class's Att_* table, move-to-human when out of reach, Brain_QueueAttack's chains, and the attack action's wait on 0x5c0221f. The command is written once, in Start (not every update); the attack delay is halved when the target targets the attacker; the target's +0x1ec takes a separate per-kind time, not the attack delay (The attack action). The move actions steer by a heading and speed in the brain (+0x110, +0x114), not by a stick (Moving).
  6. The block goal and the counter, driven by the attacker's warning: +0x200 counts events 0x10, sent only to a human whose range and field of view (+0x130, +0x12c) take in the attacker (Blocking). An AI human never blocks: the block goal's command 4 starts nothing, because the dispatcher starts a block only on R1 held on a real pad. Give it the no-reaction flag 0x800 (damage lands, no reaction; cleared from the goal's sixth update) and, on every update while the block time runs, the counter roll: command 3 at the counter chance, which counters only a grab (69-71 → 76) or a tackle (2-4 → 9) aimed at it.
  7. Reaction goals for the states a fight produces (stunned, knocked down, grabbed), one update after the state.
  8. Scripted goals for level99, in this order; each sub-step is testable on a synthetic level:
    1. The route planner (Path planning): the path polygons' inside test exists (Level loading); add the walkable-line test, the C / D records as a graph, A* with the edge costs and the 65000 cap, the retry and 0x80 detour, the route pool with its shortcuts and use counts.
    2. The move action on it (The move action, Following): straight when the line is walkable, else waypoints with the 0.25 m radius; the corner speed; the stuck test and brain +0x284. The search mask is 0xff, and a leg of kind 8 or 0x80 is a climb through the player's Climb_TryStart (link kinds): level99's Vermin crosses a fence that way. Steering round humans (Steering) is built; choke points and queues can follow later.
    3. GoalMoveToFlag (GoalMoveToFlag): offset target, arrival radius, the face-the-flag turn, re-planning when a move ends short, flag message 8 and the gang notice in End.
    4. The turn actions and ActLookAt (Turning): heading to brain +0x110, done within 15° or after 3 s; the fourth argument is a start delay.
    5. Lua callbacks scheduled 33 ms after a goal's End with (handle, completed) (Scripted goals), then GoalPlayDynAnimation (slot 668) and GoalAddressPerson with its scene goal 0x21.
    6. GoalFight, BrFlush, BrDead: flush ends goals (callbacks fire); dead clears actions only.
    7. Gangs (Gangs): records, enemy and friend masks, GangBrDead, GangSetThreatResponse on the current members, Gang_OnEvent before Brain_OnEvent, and GangSetMsgHandler's event 18 with the count still standing (level99 ends its fights on it).
    8. Formations and GoalTrackHuman (Formations): slot sets in 1/16 m, nearest-follower assignment, the 1 or 2 s re-plan, a slotless tracker that only turns.
    9. Tactics: the base (a tactic suppresses the fight goal), then TacticCrowd and TacticTrigger (Tactics); GoalDealer (GoalDealer) for level99_lesson2.

Runtime checks

All five were run (2026-10-05, PCSX2 2.9.94) with the scenarios civ_fight, civ_block, civ_block_fov, civ_approach, civ_knockdown and tick_split in repo:research/traces/scenarios/ and the call hooks in repo:research/traces/patches/. Brain i is at 0x006d53f0 + i × 0x2f0, its per-player record at 0x00660f50 + i × 0x2c. The results are in the body: the fight and attack action, think staggering, the block, moving and reaction goals.

The first checks used the street civilian as a stand-in (same Att_Normal table, type 2 set by hand); the sparring fight below then confirmed the Warriors' own numbers.

The sparring fight

The scenarios warriors_passive (no input) and warriors_block (square every 15 updates) record level99's CombatWarriors fight as the script runs it: the three Warriors Generic1-3 (classes 58-60, brain type 2, Att_Normal) each get GoalFight(warrior, player, 0) from P1.SendWarriors when the scene l99_c5 ends: in the recording, 481 updates (16.0 s) after the saved state, which was taken just after P1.SetupWarriors started the 16.7 s scene (Scenes). Results: the attack delay, the block and the cast's numbers.

Who fights in lesson 12. P1.SendWarriors (the scene's return function), in order: GangBrDead(false) for the sparring gang and the first fence gang; HuUseAnim(…, 0, nil) on each FenceWarriors1 and HuSetNoTarget(true) on each FenceWarriors2; TacticCrowd(fence gang, nil, true) (cheering) for the two fence gangs only; the tutorial text; GangMakeEnemies(Warriors, CombatWarriors); then for each of the three CombatWarriors: HuSetPushable(true), HuSetNoTarget(false), HuSetDemiGodMode(true, 0.25) and GoalFight(warrior, player, 0); then the 50 s stopwatch (P1.TimesUp) and the grab hint's anim callbacks (inferred from the disassembly of level99_combat.lua). The gangs are made in this order, so their ids are 0 Warriors, 1 CombatEnemy, 2 CombatEnemy2, 3 CombatTeacher, 4 CombatWarriors, 5 and 6 the fence Warriors, as read at runtime (the cast while the scene plays). So:

  • The sparring Warriors' gang (4) has no tactic, and no script sets its threat response: it keeps the 2 every brain gets when it is made (0x0028a570). The scripts set 0 only on CombatEnemy, CombatEnemy2 and CombatTeacher (and 2 on CombatEnemy2 when its fight starts). Their GoalFight therefore runs in full: Brain_Fight adds the player as an enemy, takes him as the target and Brain_PushFightGoal pushes FindEnemy, Melee and the fight goal (Starting a fight). The three take the player on together (the player's brain allows 4 attack slots), demi-gods at a quarter of their health, until the stopwatch ends the lesson. Confirmed (runtime): they fought in the warriors_* recordings.
  • How they reach him. They stand 8.6-9.0 m away, beyond 1.1 × their 5 m far range (Melee ranges), so each fight goal ends at its first update and the Melee goal sends them in with an EngageEnemy run; a fight goal (4000 ms) follows once each is in range (Closing on the target). Nothing else in the lesson gates them: brain type 2, threat response 2, no tactic, the player reachable (+0x11e = 1) and no wanted timer on the gang. Confirmed (runtime), PCSX2 2.9.94, the warriors_passive state with each Warrior's goal stack, position and +0x140 read every update over PINE (a contributor's run, not a committed scenario): 490 updates after the state, all three had FindEnemy, Melee, EngageEnemy (top) at 8.96, 8.59 and 8.77 m, far range 5.0. They ran in at up to 7.80 m/s (the run gait; one slowed to about 2 m/s for some 20 updates while steering round another). The first reached the player and stayed at 0.71 m with an action running (inferred: the run-in's charge, step 11); its EngageEnemy ended at update
  • The other two stopped at 2.1-2.6 m and swapped EngageEnemy for a fight goal at updates 546 and 562. Each stack then read FindEnemy, Melee, Fight.
  • Why the other two stop short (confirmed (runtime), PCSX2 2.9.94, the same state, the three Warriors' and the player's state word, gait, speed +0x1ac and clip read every update; EngageEnemy step 9 confirmed (code) at 0x002afa48). The first Warrior's run-in ends in the charge (clip 0), which hits the player and plays the extreme reaction 296 on him: his state word becomes 0x180000, inside the busy mask 0x7bf9e9f7ff0 that step 9 tests (HumanRecord_AreActionsBlocked), and stays so through 296 and the getting-up 198. Step 9 runs only on a re-plan (step 6: every 250 ms, or on the target's turn, slowing or coming within 1.6 m), so each runner stops at the first re-plan that finds the player busy within 0.75 × far (3.75 m): one 4-5 updates after the hit, at 2.12 m (the player had been free until then, so the 3.75 m line had passed), the other 5 updates after crossing 3.75 m, at 3.15 m. The stop sets the move speed to 0 (0x0028aac0) and the run (410, 7.80 m/s) ramps down by 1.067 m/s per update to 0 in 7 updates, about 0.83 m, with no run-stop clip (410 straight to the idle 388); they stood at 2.10 and 2.49 m, the player flung farther meanwhile. Then the stop's turn to face (0.3 s), EngageEnemy ends, and the fight goal walks them in with the combat-walk clips (380, 387; 3.43 m/s) while he is still down. So the 2.1-2.6 m is not a distance of its own: it is where a 3.75 m test, sampled every 250 ms and started when the player became busy, leaves a 0.83 m stop. A contributor's run (not a committed scenario), from 470 updates after the state.
  • The fence gangs (5, 6) get the crowd tactic and no GoalFight: they cheer from the fence (TacticCrowd). They are friends of the Warriors' gang.
  • Which wins, confirmed (code) at 0x0028d2e8 and 0x0028d190: a threat response of 0 stops GoalFight completely (Brain_Fight returns at once: no enemy, no target, no goal); under a gang tactic it adds the enemy and takes the target, but pushes no fight goal, and the tactic decides what the members do. A scripted GoalFight never overrides either. Neither applies to the sparring Warriors.

The second wave in the snap lesson

The second wave (CombatEnemy2's SecondWave01-04, bum models) is never sent to fight in lesson 7: it surrounds the player and stands there, so that whichever way he snaps, a bum is there to hit (the lesson).

  • Set-up (P1.SetupSnapAttacks, the return function of the scene l99_c7), inferred from the disassembly of level99_combat.lua: BrDead(player, false); GangSetTargetable(CombatEnemy2, true); the player's follow slots: BrSetFollowSlotSet(player, 0), BrSetNumFollowSlots(player, 4) and slots 0-3 of set 0 at (0, 1), (1, 0), (0, −1), (−1, 0) m (ahead, right, behind, left); Rudy (GenWarrior) gets a bottle and walks to fRudyStand, whose message 8 (P1.RudyAtFence) plays his drink and a line; then for each CombatEnemy2 member HuSetNoTarget(false) and GoalTrackHuman(member, player, 0.5); then EnableAllButtons(), EnableCommand(player, 1, 0) and EnableCommand(player, 40, 0), the lesson's text, and P1.StartSnaps 5 s later. No GangBrDead(false), GangSetThreatResponse or GoalFight touches the gang, so its members stay dead to the AI with threat response 0 (as set up, the cast) and never attack; GoalTrackHuman still runs.
  • Confirmed (runtime), PCSX2 2.9.94, level99 checkpoint 1 with P1.PowerMovesDone scheduled from lesson 2 (call hook): during the scene the four hold JoinCinematic (0x29); when it ends each has TrackHuman (0x30) on top, brain +0x09 still 1, and they stand 1.0-1.4 m from the player at about 0°, +104°, −95° and −175° from his facing, without actions. With the stick at 0.6 straight up for 3 s the player walked off; they followed and stood within 1.3 m of him again 3 s after he stopped. None attacked.
  • So the four slots and GoalTrackHuman's 0.5 m are what put a bum within the snap's 2 m (Attacks) on every side.

Reaching the fight without a playthrough

The state is not a quick-save slot; it is made once and named by the scenarios' state (Recording a trace). With the call-brains hook on a copy of a level99 state (slot 1 is checkpoint 3):

  1. Call 0x0041abc0(1) (SetCheckPoint's worker), then 0x00160d78(name) (MenuLoadLevel's worker) with "level99" written to free memory: the level reloads at checkpoint 1, and its tutorial starts about 60 s later.
  2. Schedule script functions by name with 0x003863d8(name, delayMs) (ScheduleFunc's worker), the name in free memory: AddFenceWarriors1; then GangMakeFriends's worker 0x0016ad80(0, fenceGang) (the fence gang's id, 5 here, is brain +0x20c's); then P1.SetupWarriors.
  3. Save while the scene plays (Driving PCSX2), and put the patched words back before using it as a source.

The cast while the scene plays

Confirmed (runtime), in that state (l99-warriors-before-fight) read both from the state file and over PINE; type ids read from each goal vtable's +0x0c function, confirmed (code) at the constructors in Goals:

  • Brains 0-18 are in use. Everyone in the scene (Rembrandt, Vermin, Cleon, Generic1-3) is dead to the AI (+0x09 = 1, think the stub 0x004edd40) with one goal, JoinCinematic (0x29), whose actions are a move (vtable 0x00542f20, MoveAction_Init's) and then a turn (vtable 0x005430e0, the one Action_TurnToTarget makes).
  • Ash (type 3) is suspended (+0x0a = 1) but not dead, holding one goal, HoldPosition (0x36).
  • GenWarrior and the second wave (type 4) and the two teachers have threat response 0; the sparring and fence Warriors 2. The fence Warriors (gangs 5, 6, type 2) are the only brains running their own think (0x00304608).
  • Gangs (record order = id): 0 the player and Ash, kind 0, alert 1, enemies 1 and 2, friends 5 and 6, with a tactic of type 3 (vtable 0x005439e0, init 0x00313490); 1 GenWarrior, 2 the second wave (both kind 19, enemy 0); 3 the teachers and 4 the three CombatWarriors (kind 19, no masks yet: GangMakeEnemies comes with the fight); 5 and 6 the fence Warriors (kind 0, friends with 0).
  • Inferred: the type-3 tactic is set by 0x00313400(…, gang id, …) from 0x0041c4e0, which Human_MakePlayer (0x00229c40), Human_SetWarChief and Gang_OnEvent call: the player gang's standing tactic, and the source of Ash's HoldPosition (0x00313718 pushes it).

Game-state functions

Functions of the game-state module (0x00417af0-0x00424e50: inventory, statistics, flags, configuration workers) that belong to this page, by address.

Address Name What it does Evidence
0x00419ee0 GameState_FindNearestPlayer the nearest listed player to a point, the squared distance out; 40 callers in the AI confirmed (code)
0x0041b4f0 GameState_SetWarriorCommandName the name at +0x2fc that GameState_CallWarriorCommandCallback calls with (human, command) (Warrior commands) confirmed (code)
0x0041b5e0 GameState_CallWarriorCommandCallback from WarriorCommand_Dispatch confirmed (code)
0x0041c2b0 / 0x0041c2d0 GameState_TurnWarriorCommands / GameState_IssueWarriorCommand the workers of TurnWarriorCommands (all on or off) and IssueWarriorCommand (for player 1) confirmed (code)
0x0041c338 WarChief_CheckCrewInRange for a war chief: when the nearest crew member is within 50 m clears byte +0x414 + player and returns 0; otherwise (unless told to be quiet) says command 0x12, or 0x5c when a member is cuffed or out, sets the byte and returns 1; from WarriorCommand_Dispatch and PlayerBrain_Update; GameState_CheckGameOver reads the byte confirmed (code)
0x0041c4c8 GameState_StoreWarriorCommand a player's last command (+0x41a) and its argument (+0x42c) confirmed (code)
0x0041d080 / 0x0041d088 GameState_SetDealerCustomer / GameState_IsDealerFree the handle at +0x284, one human at a time: a scout calling his gang (ScoutTactic_OnEvent) or a dirty dealer running off after a rip-off (DealerGoal_OnBuy); cleared by CallGangGoal_Destroy, DealerGoal_End and the level reset; the Warrior attack command's second fallback target (Gang fights rule 4) / whether it no longer resolves confirmed (code)
0x0041d738 / 0x0041d748 Cfg_SetEnemySpotting / Cfg_SetWarriorSpotting CfgSetEnemySpotting: +0x56f8; CfgSetWarriorSpotting clears +0x56fc whatever its argument confirmed (code)
0x0041d758 / 0x0041d770 GameState_SetWarriorVandalize / GameState_SetWarriorWeapons CfgSetWarriorVandalize (+0x5700) and CfgWarriorWeapons (+0x5704) confirmed (code)
0x0041d978 Cfg_SetExcludedVandalizeZone CfgExcludedVandalizeZone: 0x005148bc (0xffff at set-up) confirmed (code)
0x0041da50 / 0x0041da60 / 0x0041da70 Cfg_SetTurfInvasion / Cfg_SetGrappleCounters / GameState_SetPowerupPickup CfgEnableTurfInvasion (byte +0x56e1), CfgEnableGrappleCounters (+0x56e3), CfgPowerupPickup (+0x56e5) confirmed (code)
0x0041db08 / 0x0041dbe8 GameState_EnableAllWarriorCommands / GameState_EnableWarriorCommand the per-player allowed bytes +0x41e + player × 7 + command: all seven for every war chief / one for one human; both tell the command display (WarCommandDisplay_SetAllowed) confirmed (code)
0x0041dc80 / 0x0041dcf0 / 0x0041dd40 GameState_IssueWarriorCommandFor / GameState_LockWarriorCommands / GameState_SetWarriorCommandCallback WarriorCommand_Dispatch for a human / byte +0x418 + player / the callback name confirmed (code)
0x0041dd68 GameState_SetAutoSwitch WCEnableAutomaticSwitching: byte +0x431 (1 at set-up), which turns the war chief's automatic commands on or off confirmed (code)

Coney's implementation

Steps 3-8 of What an implementer needs are in repo:src/ai/ (the path data in repo:src/world/path_map.h), each original function tagged with @orig in the code; tests in repo:tests/ai/ and repo:tests/world/path_map_test.cpp.

  • Brains (Brains, Brain): a type from the class's behaviour byte, a think one step in five staggered by slot, an update every step at the brains' place in the characters' step (Humans::setBrains), so before every dispatcher. The player has a type-0 brain that only keeps his enemies, which feed Player::setNearestEnemy.
  • Goals and actions: a stack of 10 (Start, Process, End, Resume; Stop, Again, Done) and a circular queue of 8 (a delay, Start, Update, an Abort that can refuse); goals start or resume only on an empty queue. A goal leaves the stack before its End runs: an End can reach Lua (a flag's message), whose handler may push the next goal (level99's P1.ReachCenter pushes GoalAddressPerson), which must stay on the stack.
  • Fighting: FightGoal in the original's order (repo:src/ai/fight_goal.h): the valid-enemy and slot check, the tackle try, the 1.1 × far range, the walkable line every 30 updates, the re-target, the block try, the wait on the actions, the deadline, the pick, Brain_CheckAttack, then the ring, the move into the kind's reach or the grab and snap try and the press. Brain_QueueAttack's chains are timed by the chain clip's first event; AttackAction presses the command once in Start (the delay halved when the target targets the attacker or the brain is type 3, then a wait on 0x5c0221f), with a full stick along its angle when it has one, and, when neither human is busy, moves the target's +0x1ec on by the kind's swing time over his spacing. MoveToHumanAction sets a heading and speed in the record's move (no stick).
  • The attack choice (repo:src/ai/attack_choice.h, repo:src/ai/attack_views.h): Human_CanUseAttackKind's table and Human_CanStartAttack's guard as pure tests over two views of the humans, and Brain_PickAttack's draw with every adjustment (the running rule, the hurt rule, the crowd on A, the busy target for the grab, the armed bonus, the rear-grabbed bonus, the pattern read). attackerViewOf and targetViewOf fill the views from Coney's fighters. Stand-ins: "free" is on its feet in no pair and not reacting; no AI holds an object, spray paint or cuffs; the pattern read is not made; the kinds whose commands no Coney handler takes (8, 9, 11-13, 15, 18, 23, 41) are left out of every pick. Each kind's near reach (the walk-in) and far reach (the start test) are the anims of repo:src/ai/attack_reach.h, read from the attacker's Anim Range List; an anim without a record reaches 2 m.
  • Taking turns (repo:src/ai/attack_places.h, repo:src/ai/fight_book.h, repo:src/ai/fight_checks.h): each brain keeps four active-attacker places, the two spacing bytes (raised by each slot claim to the attacker gang's CfgGang values 2 and 3, a Warrior target keeping 1 standing, back to 1 when its slot list empties) and the tackle meter, kept by the cops', gang soldiers' and Warriors' thinks. CfgGang's values 2, 3, 5, 7 and 8 are read per gang kind from the scripts' calls. Brain_CheckAttack answers in the page's order; a waiting attacker holds the reposition ring. Stand-ins: the rows about held objects (4, 6, 8) never apply; the police row applies when the target's own target is a cop; the ring is a move to its outer edge, standing within it, with no taunt; the fight stance is not built (the charge and the dive count as out of it); the re-target's threat is a valid enemy on the brain's list; the swing time reads the attacker's own set, not the bat and bottle sets' clips (25 and 29 the throws, 26-28 the power strikes, 31 the struggle strikes 96 and 108, 32-34 the escapes 100 and 112).
  • A fight's three goals (Closing on the target, repo:src/ai/melee_goal.h, repo:src/ai/engage_goals.h): Brain::fight (with a duration, GoalFight's none, a rioter's 8000 ms) pops any Melee or FindEnemy goal with everything above it and pushes FindEnemy, Melee and the fight goal, whose deadline is the duration. The fight goal never closes a distance; the Melee goal (no limit or the duration from its first run) takes the best-scoring enemy (below), pushes a fight goal of 4000 ms within 1.1 × the far range (after a walk at gait 2 when the straight line does not reach him) and an EngageEnemy goal beyond it; FindEnemy starts the fight again while the target can be fought. EngageEnemy runs at gait 4 (5 after a runner), re-plans every 250 ms, leads a target facing away, stops and turns to him within 3.75 m when he is busy or the charge is not armed and he walks or stands, and attacks out of the run within 1.6 m with the charge armed: a cop's X1 (50 %, 80 % at a gang member) or tackle, anyone else's pick, which ends the run-in when it is not a charge kind, pressed as one attack action once it can start (the X1 along the runner's heading); another human the near one in the sector of the target's record the runner comes from disarms the charge. The grab and snap tries, the grab's "behind" and the lead read ai-core's sector records. EngageEnemy stops for a target in sight it may not attack (Brain_IsAttackableBy), re-targets the nearest enemy it sees within 9 m, and gives up out of sight 20 m away. Stand-ins: the gang's wanted timer is not kept (the chase always allowed); Melee's own line-of-sight branch is not built; with no target but a valid enemy Melee spectates (without the shadow and may-spectate tests); a human whose last move failed runs straight at the target for 2 s and may approach again (the weapon pick-up, throw and positioning moves are not traced); the type-3 AttackTarget gate and goal 0x35 are not built; past its deadline a fight goal ends only without an attack slot (the slot standing in for the target's active attackers). level99's sparring Warriors are sent from 8.6-9.0 m (the sparring fight) and run in this way: coney_tests "the disc's level99: the sparring Warriors*" checks that all three run in with EngageEnemy, close in and attack, that exactly one charges (faster than the run's 7.80 m/s) and that the other two stop short of the player, as in the original. The steering round each other puts those two a little behind the first, so his charge has made the player busy when their re-plan tests step 9; without it the three ran in abreast and all charged at once (clip 1, up to 9.6 m/s), ending 2.1-2.9 m past him. Their run-ins end at 2.5 and 4.0 m from the player; the original's ended at 2.1-2.6 m, and why one of Coney's ends farther is not traced.
  • Sight and targeting (repo:src/ai/perception.h, repo:src/ai/targeting.h, Sight, the score): the line of sight is the two rays (1.7 m, then 1.0 m) through the level's collision mesh, passing the six see-through materials; the field of view and Human_CanSeeHuman (range, the shadow's rule, then the line); the brains get the mesh from Brains::setCollision, and an attack warning needs the line too. Brain_ValidateEnemy, Brain_CanBeChased, Brain_IsAttackableBy (the attackable byte +0x11f, GangSetAttackable, and the street civilian and dog exceptions), Brain_CanTakeSlotOn, Brain_ScoreEnemy with every term and the weights of CfgSetTargetingPoints / CfgSetTargetingPointsEx (read from the scripts' calls), and Brain_PickBestEnemy with the goals' adjustment (the previous target's 3 points). Stand-ins: Coney has no trains, fires, muggings, interrogations, tagging or turf-only rule, so those tests pass and those terms give nothing; only the level's static collision blocks a sight ray; a gang's chosen target (+0x10) is kept but nothing sets it yet.
  • Hiding (repo:src/ai/hiding.h, repo:src/human/human_hiding.cpp, Stealth): the ground snap keeps its triangle's flags (Human::onShadowGround, bit 0x10); after the level's step ai::updateHiding runs the ground rule for player 1: on shadow ground with nobody hunting him (no other fightable brain with him as its target or enemy, Coney's reading of the hostile count) he enters the hidden state, off it or hunted he leaves it (kept 4 s when walking with a target), and stepping on shakes off hunters beyond twice their far melee range with no line of sight. Hidden, his target does not lock, a sprint ends it, his idle and walk take the stealth style (634 in, 630, 633 at its own speed, 631 starts, 394 out), the radar turns blue (HUD), and Human_CanSeeHuman sees him only within 2 m from shadow ground. Not yet: the other players' Warriors and their hide order, the molotov test, the camera's raised look-at and the half volume footsteps.
  • The enemy scan (repo:src/ai/enemy_scan.h, The enemy scan): scanEnemies lists the members of the enemy gangs in use that it sees (sight range, the shadow's 2 m rule, 1.9 m above an AI scanner at most, the Warriors' threat filter, the near radius of 1.5, 3 or 5.2 m all round and the view cone beyond it, then the line of sight), keeps old entries within the range, valid and near or in sight, keeps the 16 nearest, and tells the scanner of each new one as event 0xb (its script's handler, else its type's; a tactic through Brain::addEnemy). ScanSchedule runs it every interval (2 s, cops 1 s, × 4 locked on in the stance, or a goal's own) with five scans an update at most. Coney's reading: the gangs always seen, Human_MaySpectate's fight count, the civilians' ped type, the gang's chosen target, the hostile counts, the radar's enemy marks, the detail level's slowdown and the rescan on a gang change are not built. The Scout tactic (below) scans its members at their posts.
  • Blocking: BlockGoal never produces a block: Coney's block starts only on R1 held in the record's buttons, which only a pad writes, so its command 4 does nothing. It turns the human's hit reactions off (Fighter::setHitReactionsOff, bit 0x800) from its start until its sixth update with the human free; rolls the counter on every update of the block time (BlockGoal::counterRoll: the roll, then counterTest: a type-3 brain, free and not hurt, face to face with a target aiming at it in a grab's or a tackle's intro) and writes command 3 on success; extends the block while the target still attacks, which stops the rolls and queues a punishing attack in the block's last second.
  • The counter (repo:src/combat/ai_counter.h, Fighter::answerCounter): command 3 from a human no pad drives, free, unhurt and empty-handed, is kept for one update; the human in a grab's or tackle's intro at it takes it on its own update when the two face each other, and the pair plays: the hold ends, the counterer plays 76 (9 against a tackle) and the attacker 77 (10) from the counterer's set, takes the counter's damage from the counterer's Anim Range List and stands stunned after it. Coney readings: the class-13 way through the type gate is left out and its byte *(0x0051489c) + 0x56e3 taken as 0, so only the Warriors' brains counter; the tackle counter ends as the grab's (stunned); no power is spent; Coney's tackle holds its victim from the intro's start, whose reaction goal then holds the block goal off, so in play only grabs are countered.
  • Reactions: grabbing, tackling, grabbed, knocked down and stunned, one update after the state.
  • Configuration (aiConfigFrom): the class's CfgChar, power class 40's CfgPowerClass, CfgAttackDelay and CfgBaseChanceToBlock from the scripts' recorded calls. Without the disc the reference values stand in: power class 40's AI fields (block 0.2, 0.1 hurt, delay factors 20, counter 0.08), base block chance 60, the config_preload2 delays, Att_Normal.
  • Play: fighter lines in sandbox layouts (Sandbox), the debug menu's AI fighters page, and a level's scripted cast. Gameplay (repo:src/gamemodes/gameplay_mode.h) makes the level's Brains and ScriptedBrains before its script runs and holds every AI call on a human (ScriptedBrains::hold) until the play mode has loaded the level and made the humans; release then makes each HuCreated human (player 1 bound to the player's brain, the others AI humans of the class aiConfigFrom gives their type, drawn with their own model, repo:src/platform/play_level_cast.cpp) and replays the held calls in order. A later HuCreate of player 1 is an AI human until HuChangePlayerGang hands player 1 over (Characters), and GangDelete takes every member but player 1 out of the world at once (Deleting a gang). A teleport moves an AI human; GetPosition and the look-ats read a human's live position.
  • Routes (world::PathMap, RoutePlanner): the path data decoded (Path data), the inside test, the walkable-line test, the request (the human's polygon, the straight line, the ends' nodes within 30 tries), A* from the destination's node with the edge costs and the 65000 cap, the retry with 0x8c and the jump detour, and routes from a pool of 32 with the leading, shortcut (4 ahead) and trailing cuts and the nodes' use counts. A brain gets the level's planner from Brains::setPlanner (the play mode gives one from the level file's path data, PlayScenery::pathMap); with none, every move goes straight.
  • Moving (MoveAction, RouteFollower): straight when the line is walkable, else the route's waypoints (0.25 m, moving on when reached and every 6th call, skipping what is in a straight line); the straight re-check every 30 updates; the turn on the spot beyond 30° while standing; the corner speed; the stuck test; brain +0x284 (Brain::moveFailure). A human its brain drives moves by the AI's own rules (human::Human::aiLocomote, the player's locomotion unchanged): a constant turn step by gait (12° walking or standing, 6° jogging, 4° running, 2.5° sprinting, 0.375° wounded) times the turn boost plus one, or divided by one minus a negative boost (Brain::setTurnBoost, +0x0b), with no easing; a speed that starts at the asked speed up to 2 m/s, gains 8 m/s² (32 while more than 2 m/s short) and loses 32 m/s²; a wounded human asked to move walks. The move's corner speed predicts the turn with the same steps. Stand-in: a standing human more than 15° off turns at the standing step instead of playing turn clips 395-398. Brain::update clears the move first (Brain::stopMove, as Brain_StopMove starts Brain_UpdateGoals), so the human follows only what this update's goals and actions wrote. In a fight stance (the fighter locked onto a target) a human no pad drives runs Human::aiCombatWalk (An AI's locomotion), and so does a player whose pad a script took (per-player +0x1b clear, his brain set dead), so a script's turn steers a player locked on a foe, as the workouts' GoalMoveToFlag at the heavy bag needs (Workouts): the facing turns to the brain's heading at the AI's own stance word (24°, human::aiStanceTurn, which the player's tuning does not change) whenever the move asks for a speed, at the standing step (the stand-in above) when it stands more than 15° off, and 0.375° whatever the speed when wounded; the feet go along the line to the move's point (Brain::setMoveAim, +0x90) at the combat-walk speed (the walk when wounded) when it asks for more than 0.1. With no move the human keeps its facing: it does not track its target. The move-to-human action aims 2 m along its way, as MoveMelee does. Stand-in: the fight goal's reposition, standing within its ring, turns the human to its target (the move's heading, at speed 0), where the original's band-keeping move circles him facing him; without it the level5 bosses kept whatever facing their last attack left and the pad-driven boss-fight disc test lost the fight. EngageEnemy's stop turns the human to its target with the 0.3 s body turn (Human::turnToFace), not with the move. Only a pad's human faces its target every update. Before (found 2026-10-11) the stance faced the target, so a move that had to turn first asked for speed 0 for good: level5's Vargas, sent to his flag by his BigBrawler with the player beside him, stood at his 67 % cap. Tests: repo:tests/ai/move_action_test.cpp ("locked onto a target, ..." and the cases after it). Every search uses the mask 0xff; a leg of kind 8 or 0x80 is a climb: the human runs at the waypoint (gait 4) and tries the player's climb start toward it each update (PlayerRecord::climbToward), giving up after 31 failed updates; a leg whose avoid bit is set is refused, but for a charge. level99's Vermin climbs his fence this way.
  • Neighbour sectors (Sectors, repo:src/ai/sectors.h, Brain::sectors): eight 45° sectors per brain (0 ahead, rising anticlockwise in Coney's headings, the mirror of the game's clockwise order: to fix; an edge goes to the lower index on the left half), rebuilt when the caller's age (1000 or 500 ms) has passed: each human within 1.5 m counted, the nearest kept, the flags from his squared distance (3 below 1.5, 1 below 2.5), the free-player flag 8 for an AI within 5.5 m; the lazy wall probe 1.5 m out at the stored heading along the path data's walkable line; the readers (blocked, free, wall, cost, all clear, held by, the turn way) and the point in a sector. Coney choices: a record never built is always rebuilt (the original's clock is far past 0); a player is busy for flag 8 when not standing or with a busy record bit; with no trains, the probe's train test never fails.
  • Steering round humans (repo:src/ai/steering.h, Steering): every move action update, after the route's aim and outside climb, jump and charge legs, steerAroundHumans finds the blocker (the 0.63 m disc against the relative 0.75 s step) and passes a standing one 1 m beside him, steps 1 m aside from the contact head-on or overtaking (overtaking only with right of way), goes round behind a crossing one, or yields (stop for an update, match his speed, or 0.75 × for a same-way human off to the side); of two humans heading for the same route node the one held there follows at 0.75 × the other's speed. A steered aim is reached within 0.3 m; speed changes go through the state's override, which the move takes in place of its gait's speed from the next update. The held detour point is the move's own aim, as in the original. The detour raises the turn boost by one while it lasts; a move's start clears it, with the score, the detour's decision count and the slow counter seeded with the human's index. The corner check is Move_CornerSpeedLimit as traced (the circle tangent to the facing through the point, 2R tan(ω/2) per update for each gait, stepping down while a gait allows as much), and Steering_TryDetour refuses a point whose first ground within 2.1 m below is a triangle with flag 0x10 (groundProbeBelow). The sector a standing AI gives way into is chosen (s, s+1, s+2, s−1, s+3, s+4, s−2 from straight off the mover's path, in a record at most 500 ms old). Coney choices: the detail level is 0, so a decision is made every update; a speed tie goes to the lower brain slot, and the brain's slot stands for the human's index; the mover's line for giving way is unbounded. Stand-in: with no waypoint claims (Nav_ClaimWaypoint), "held at the node" (route state +0x16) is read as "he is nearer the shared node than I am", the human the claim would keep. Left out: the waypoint claims and queues.
  • Giving way (repo:src/ai/give_way.h, Giving way; the step in repo:src/human/step_control.h, Characters: Step control): pushAside (yes while a GiveWay lives, no inside another push-aside, both brains marked for the call) and giveWayTo (an AI's or a "dead" brain, no threat either way, the stander idle under his control); the first free sector gets a GiveWayAction on the facing + sector × 45° after 0-124 ms, dashing (turn boost + 1) from a player at a jog or faster, else the human nearest in each sector is asked in turn. TakeStepAction stops the move and enters the human's step control, which waits while he is busy, then plays one clip (the fight stance's shuffle while locked on, the step, or the dash with a boost) holding 0x80000 and turns him to its end facing over half of it; the action ends with the clip. The script's ActGiveWay asks a human with fewer than 8 actions to give way to the other along his facing. Callers so far: ActGiveWay only. Coney's readings: the side clip is the one toward the heading (so a step to the left plays the left clip); the step's heading goes to the step control directly, not through the brain's heading; grabbed, grabbing or in a hold stand for the step's refusing state bits 0x180f3ff0. Coney choice: every abort is the polite one. A finished action is aborted before it is freed (Action_AbortAndDestroy), so the boost goes back in Abort alone. Stand-ins: the 2 s look at the mover (no head look-ats); the push sphere stays on during the step; a Warrior's +0x2e5 mode in the threat test; brain flag +0xcc bit 2. Left out: the other callers (the route's busy links, Gang_ClearWayForLeader, WarriorBrain_Think, SaveHumanGoal_Process).
  • Scripted goals: MoveToFlagGoal (offset target, radius, the face-the-flag turn, a new move each time one ends short, message 8 and the gang's notice through FlagServices), TurnAction (look-at, to a point, to a heading; 15°, 3 s), PlayDynAnimationGoal with PlayAnimAction (slot 668), AddressPersonGoal with PlayAnimationGoal (its scene), TrackHumanGoal and DealerGoal. A goal's callback is scheduled 33 ms after its End with (handle, completed) (scheduleGoalCallback). Brain::flush ends the goals (their callbacks fire) and clears the actions; Brain::setDead clears the actions only, stops the think and the attack warnings, and hands a player's pad over (Brain::setPadControl, human::Player::setPadControlled), after which the player's brain runs goals like an AI's.
  • Bindings: the AI bindings (repo:src/scripting/ai_bindings.h) and the gang bindings (repo:src/scripting/gang_bindings.h, GangCreate in repo:src/scripting/script_bindings.cpp) hand their calls to the binding context's AI host. ScriptedBrains is that host and the goals' ScriptServices: it names brains by handle (bind, with HuCreate's gang), finds flags and look-at targets, and runs the callbacks, message handlers and tactic callbacks in a ScriptSystem. An event goes first to the human's own handlers (SetMsgHandler, Brain::services), and a flag arrival sends the flag message 8 with (flag, human) (Scripts: message handlers).
  • Gangs (Gangs, Gang, in Brains): 32 records with kind, name, enemy and friend masks, members (10, 16 for the police kinds), suspension, message handlers and a tactic; Gangs::friends and enemies; the whole-gang switches (GangBrDead, GangBrFlush, GangSetThreatResponse on the current members, GangSuspend). An event goes to the human's gang before its brain (deliverEvent): the handler for 18, 2 and 0x11 with (member, other, value), any other id used when the call returns true, then the tactic. Brains::update sends event 18 once when a member's health runs out, so level99's P1.BumDied gets the count still standing (Gang::standing).
  • Formations (Formations, Formation, stepped before the gangs): sets of 9 slots in 1/16 m turned by the leader's heading, the plan when he stops, every 1 or 2 s or 1 m from the plan point, the nearest follower per usable slot, the rest queued nearest-first behind, and the crossing-paths swap.
  • Tactics (Tactic, stepped by the gangs): started on the first update with each AI member's goal base marked over the goals he has (its goals go on top, and its end pops back down to them), 2 past the time limit, the callback with (gang id, code); a gang with a tactic gets no fight goal from GoalFight. TacticCrowd seats its members (idle and fightless when cheering, spectating 4-6 s when watching), gestures, cheers in turn and reacts (a clip, then the cheer) on its tick, on TacticTrigger and on violence nearby.
  • Events and help calls (repo:src/ai/brain_events.h, Shared events): a brain answers its human's events with its type's handler (gang soldiers and Warriors: a stranger's hit starts a fight, violence is Brain_OnViolenceSeen), then the shared one (a hit, a new enemy or an attack warning sends a 20 m help call; the reset clears the brain; an attack warning under a fight goal may be countered). A help call is violence (0x14) to everyone within its range and the hearer's help hearing; a soldier or Warrior takes a side (Brain_PickSideInFight), fights him and pushes HelpRespond. Coney stand-ins: the threat test is the enemy list or an enemy gang; the cop's, civilian's, dealer's and shopkeeper's handlers are not built (they take the shared one); the top goals' own hit handlers, the Mark goal's noise reaction and the look-around are not built.
  • The knock-out (Brain::knockOut, Reaction goals): when a human's health runs out his brain ends its reaction goal, pops every goal, clears the actions, drops the target (releasing the attack slot and active place on it) and the enemies, and stops until he is up again; then event 0x12 reaches his human with his foe.
  • The Diego and Vargas fight (BossDiegoVargasTactic, repo:src/ai/tactic_boss.h, repo:src/ai/boss_goals.h, the fight): TacticBossScenarioA gives class 120 a BigBrawlerGoal, class 119 a BigThrowerGoal in stage 2 and a BigBrawlerGoal in stage 3, the others StationaryThrowerGoal; holds the boss at 67 % and 34 % until he is tired, then starts the break (clips 671, 672, 673) and answers 18 while its clip plays and 1 once it is done. Six hits in the fight push TiredGoal, which stuns him for the stage's fatigue or damage limit. An attack warning names the attacker as the peer standing where it was announced (Coney reading). Coney stand-ins: no object appears in a boss's hand (the throw clip plays alone), the enemy scan is his gang's enemies within sight, the camera turn faces player 1, no event 24 or help call, and each tactic code is answered once.
  • Disc check (NTSC-U, counts only): coney_tests "[disc][routes]" decodes all 64 levels' path data; 42,373 of the 43,234 route nodes lie inside the polygon that owns them. Of 500 seeded pairs of level99's 415 nodes, 32 are a straight line, 269 routed (3,208 route nodes), 34 refused (a polygon off the graph), 162 not linked at all over the default mask's links, none lost by the search, and 3 lost at the ends (the check has no ground probe, so a node over another floor finds the wrong polygon). Of 300 seeded pairs of linked nodes on each of level80, level87 and level99, the search finds every one but level80's two routes of more than 128 nodes.

Coney choices. A fighter is class 58 (brain type 2, 1400 health) with the sparring Warriors' runtime brain values (4 attack slots, melee 3 / 5 m, sight 30 m, field of view 1.92 rad), drawn and animated as the player's character, in a gang of kind 19 made the enemy of the player's (the Warriors' kind), and an "engaging" toggle stands in for the script's GoalFight (an idle fighter takes the player on within its far melee range). Who fights whom in the characters' step is whoever's gangs are not friends (Humans::setOpposition); without gangs, the humans added pad-controlled and the others fight each other, and only the former fight the sandbox's passive targets. A move runs beyond 4 m and lasts 1000 or 2000 ms (2000 beyond twice the reach). Command 0x11 chains as square. A reaction goal clears the actions and the move. The fight reaction goals (grabbing, mounting, grabbed, mounted, grounded) are built in repo:src/ai/fight_reactions.h; their stand-ins: no trains, the presenting does not test player 1's Warrior command, the FollowAndDefend that clears the hand-over is TrackHumanGoal, no help call, and a held AI's presses reach no handler (its holder drives it). A block ends when its target is not on its feet (Coney has no state word). Each brain's generator is seeded by its slot. A think counts, keeps the tackle meter of a cop, a gang soldier or a Warrior, and for a Warrior runs step 7, the swap prompt (repo:src/ai/swap_prompt.h): talkable (ScriptState::talkable) with GSTRING.HUD 0xc, 0xd or 0xe while a player of his gang within 1.5 m, below a jog, has him standing in front, nobody attacks him and one of the two holds an object (Coney's reading: the hand's object, +0x338); the blocked and held-flag tests, the sparring byte and +0x121 are not modelled, nor the two-player prompt. The other steps are not built. The swap (PlayLevelMode::trySwap, ai::swapHeldObjects): player 1's triangle, after the dealers' offers, asks the Warriors ahead of him within 1.5 m nearest first; one that is talkable, not grabbed, with a clear line to him and both of them in the free combat mode (Coney's stand-in for the blocked and held-flag tests) clears his actions and turns to him over 0.2 s, and the two hands swap at once (a left-hand or hat object stays at its giver's feet); player 1 says 146 give_me when he now holds something and is not hidden. Coney's stand-ins: the Warriors are asked before the objects' search rather than among them by distance; the two-player swap is not built.

Coney choices for moving. The inside test counts an edge going down in y as +1 (the sign under which the route nodes lie in their polygons; the clockwise polygons then contain nothing). A polygon's A record takes the next nodes in order (the counts add up to the C records in every file; +0x08 does not always hold the running index). The search holds 1000 nodes open and 3000 closed, fails a route of more than 128 nodes and costs a step as the original does (Pathing: status and fixes, items 1 and 2). 0x0051059c is 1 and 0x005105a0 0. An end off every polygon counts on the nearest within 1 m and takes its nearest node; the shortcut takes any edge that links back. The walkable line crosses every edge of the two areas rather than the slab lists', finds the start's area from the point, and has no hazard spheres (Coney has no fire to add them). A point's area (the walkable line's ends and the request's start and end polygons) is found on the ground as 0x00250760 does, with the collision triangle's area byte as the "ground's collision byte" (the material byte finds no area under level80's upper walkway, where the area byte finds the walkway's) from a ray 10 m down; an area whose box holds the point but whose polygons do not is passed over, and with no ground found the area is looked up in plan. A start standing in a hole of the path polygons (an obstacle's padded footprint: level87's checkpoint 4 start is in a 2.3 × 1.7 m hole of flags 7) plans from the point its human's polygon would come from (RoutePlanner::navPoint()): the nearest point on an edge of its candidate area within 20 m (the first edge under 0.18 m ending the look), else the first of eight probes 1 m out that an area takes in; the walkable-line tests from such a start run from that point too, and the body is not moved. A destination in a hole fails the request. Coney choices: a probe keeps the point's height and finds its area by the ground probe (no drop to the collision, no ray test), and with no edge in reach there is no edge point. level87's checkpoint 4 start plans from a diagonal probe 1.41 m out; before, every line from it, and so every route, failed. A climb leg's climb that has ended moves the follower on past the leg's waypoint (the original's step there is open). The fast climber's early start is built (ai::earlyClimbDue): on a walked leg whose next leg is a climb, a human with flag 0x2 at the run or sprint tries the climb toward the climb's far waypoint while within 4.5 m (3D) of it, and when the climb starts the route moves on onto the climb's leg; the climb probes measure along a unit direction (Human::tryClimb), so a brain's request, the way to its waypoint, no longer scales the face's distance. Before (issue #232), a fast climber ran on to the climb's near waypoint, too close for the running climb ((r1 + r2) × 0.4), and ran into the fence for the leg's 31 tries; level99's Vermin then climbed standing, 85 updates. The corner speed looks only at the waypoints up to the first whose leg on has bit 0x4 or 0x80 (RouteFollower::cornerPoints, MoveAction_CollectPoints 0x002fbd18), so a run-up to a jump keeps its gait's speed (issue #104). The charge (0x40), the link's clear test and the waypoint claims are not built. A jump leg (kind 4) takes its take-off and landing points, the drop or the arc and the 1.5 m landing test as above; the brain does not run while the human is airborne, so the move notices the landing by the human's landing count, and the arc counts as having reached the landing point when it lands within the waypoint radius plus one update's travel at the launch's plan speed (the original tests the arrival in the air, every update). A corner is the turn at the next two waypoints, simulated as an arc at the gait's turn rate from the waypoint, the trial falling by 1 m/s; the braking distance is 0.5 s at the first corner's speed, within which the slower of the two corners' speeds is used. A move clears +0x284 at its start and waits while the human is busy (Human_IsBusy). The look-at's turn value is kept, not read; no turn is ever refused its abort.

Coney choices for the scripted goals, gangs and tactics. With no scene system a scene ends at once and its callback is scheduled with (handle, 1) after 33 ms. A play-anim action plays its clip (668 the level's dynamic clip by name, any other id the anim set's) faded in over 0.2 s, at rate 1 for a dynamic clip, then the idle, holding 0x80000 (busy and gated; which bits the original's clip holds is not traced); with no such clip it ends at once, so GoalPlayDynAnimation still completes. "Not on its feet" stands in for the state words (PlayDyn's 0x7bf9e9f7ff0, the crowd's free test, the standing count's three tests); the headcount's "living" is health left. AddressPerson's turn leads the target by one second of its velocity. BrSuspend clears the actions, then suspends. A full gang drops its first member; a human with no gang is no one's friend. A handler's call counts as returning true when it runs (Coney's script system does not hand back the result). Event 18 is sent at the brains' next step after the health runs out, with no attacker. Stand-in: event 1 (damage taken, whose sender is not traced) is sent at the brains' step after a human's health falls, with the nearest other human that can fight as the attacker and the damage as its value. The fall is measured before the gangs' tactics run, since a tactic can give the health back at once (Coney's choice, inferred from the fight: the original's sender is untraced): TacticBossScenarioA holds a boss above his cap, and every hit he takes there must still reach his BigBrawler, or he never tires and the stage never ends (the boss fight). A formation slot is usable when the leader's planner finds the line to it walkable (always without a planner) and keeps the leader's height. The idle goal stands still (its Process is not traced); the spectate goal follows its research for Melee and the dealer's wary goal, the crowd's spectators standing still (their arguments are not traced). Its stand-ins: a spectator holds no target but for the EngageEnemy run-in; no fight stance, taunt or weapon pick-up; the watch action is a turn to face him; the keep-distance move is a move action to the band's edge; the chase is always allowed; the tackling man's kind-0x24 attack and the shadow test are not built; without join the watched man is the nearest member of any other gang. Both of a crowd reaction's clip actions are PlayAnimAction. The dealer rolls dirty at Start; his wary scan looks for members of enemy gangs. Player 1's triangle within 1.75 m of a dealer whose offer stands (after the objects' prompts and the stereos) makes the deal: the table's terms against the inventory, the item and money moved without notifying the inventory callbacks, and his line said at the buyer (96 at most every 5 s, 97, 101, 105). With both money clips loaded a sale starts the pair (money_take on the dealer, money_give on the buyer, both as 668) and completes when the dealer's clip passes its action event 0x41 (DealerGoal::finishPair()); without them it completes at once. His gestures are play-anim actions of the FDealer gang's clips (603 and 668 by variant, blended over 0.5 s; another gang's dealer plays his own 603 and 668) with his lines: the greeting (94), the offer (95), the refusals, thanks (98) or goodbye (99) as the buyer leaves, after which a new visit starts in state 1, and the wary look (100).

The Rumble tactics (src/ai/tactic_attack.*, src/ai/tactic_confront.*, Rumble mode). TacticAttack and TacticConfront follow the table above; TacticAttack is built from Unit 2 and gives the real Melee goal. Stand-ins: the confront goal (60) closes on the other gang's leader to its distance and waits. Coney choices: a gang's leader is its first standing member not a player's (else the first standing); the confront's gang radii are 0 and there is always a way between the leaders, so 9 never fires; the attack's team-ups and spot line, and the confront's postures and formation are not built. A tactic replaced from inside its own update or callback is freed after the gangs' update, as the original queues the free (0x00306630).

The character bindings' goals and gangs (src/ai/scripted_humans.*, src/ai/scripted_goals.*). GoalBackoff (0x9b), GoalBumLogic (0x4f) and GoalMoveToUseFlag (4) are built from their constructors; stand-ins for their unread Process: the back-off walks straight away from the other human while nearer than its distance (in a fight stance it turns its back to him; the original keeps a band with the move-to-human action and faces him, Backoff), the bum stands still and never calls back, and the use-flag goal walks to the flag as GoalMoveToFlag does, facing its heading, then stands there (the flag reserved until the goal ends). GangInvincible sets god mode on the members and on later ones; GangSetTargetable sets each member's targetable byte, kept on the human. GangAddSpawner keeps up to four spawners per gang, which spawn (below); GangClearWanted has no wanted state to clear; GangClearResponders deletes non-police gangs named Responder<n>. BrSetThugWantsWeapon and SetInterrogateParam are kept only. GoalTag pushes the tag goal (0x5a, TagGoal): the flag claimed until the goal ends, a run (gait 4) to it, done short of 1.5 m or when the flag is gone, the human is not alive or another holds the flag; at the flag the other tag is made blank and the spray starts as HuTag's does (Crimes), and the goal ends once the human is no longer tagging. Coney's reading: the Tag action's command presses are not built; the spray starts at once.

The spawners (src/ai/spawners.*, Spawners): after each characters' step every spawner in use whose state is ready (1, 6, 7, 8 and 10 always; 2 past its deadline; 3 and 5 by player 1's distance, taken in 3D) makes a human once its delay has passed, while fewer of its humans are alive than its limit and it has not made its total (-1 none). The human is made through the scripts' own HuCreate (named <spawner><count>, the spawner's model string as the fifth argument, in its gang), of the next type in turn (the index moved on first, as 0x0016d810 does), then the callback, when it names a function, is called with its handle, the gang's id and the spawner's name. 6, 8 and 10 place the human out of sight as above and the search say: from player 1's camera (Coney has one player, so no second camera refuses a node), the start node the planner's route ends use, 17 outward searches, the node then checked against the gang's turf boxes; no node, no spawn this update. Stand-ins: 7 is placed as 8 with a value of 0, not on a flag as its placement says; the others stand at the spawner; no door opens; the dispatch states 4 and 9 (no crimes are routed) and the top-up 11 (no gang limits kept) never spawn. A gang runs its spawners only while the level's count is under its cap (The level's cap): the police count under SetCopSpawnMax for a police gang, the spawned count under SetSpawnMax for any other, 16 each after the level reset, asked once per gang. A human counts off when he is down or deleted (the original: when his brain is torn down), and the counts start at 0 with each level's spawners; humans leaving their gang and a dispatch's completed squad are not taken off (neither is built). GangSetMaxConcurrent changes the limit (16 bits; a negative -n spawns waves of n as above says, from GangAddSpawner's limit too). An off-screen spawner (GangSetSpawnerMustBeOffScreen, or added in state 11) skips an update while player 1's camera sees the 0.3 m sphere 1.6 m above it (Off screen): within the far clip, in view and with no wall of the collision mesh on the ray to its centre; stand-in: the six-plane frustum is taken as a cone of half the field of view widened by the sphere.

The scripts' goals at one human (src/ai/engage_goals.*). GoalMoveToHuman (6) drops and re-issues a move (MoveAction, its gait and radius) to where the target is every second, waits 30 updates after a failed route, and ends within its radius in 3D, when the target is no longer alive in the world, or when its human is down. GoalEngageEnemy (11) is the fight's run-in (EngageEnemy, above) at the enemy by handle, taking him as its enemy and target; stand-in for the untraced wrapper 0x002af528: where the fight's goal would end, it pushes a fight goal of 4000 ms and goes on, so it fights the enemy until he is gone or down. The valid-target test 0x0028d4b0 is taken as alive and in the world. BrSetType sets types 1-6 (0 and past 6 are ignored, stand-in: 0's pad hand-over is not built), BrSetAttackWeight one kind's weight. CfgSetDefaultFollowSlotSet writes its sets into every formation in use and keeps them for those made later (Formations::setDefaults, through HumanBindingHost::setDefaultFollowSlots); every other rules binding (rulesCall in repo:src/scripting/human_bindings.cpp, among them WCEnableAllCommands, which EnableAllButtons calls) hands the rules back through ScriptedHumans::applyRules, which only keeps the defaults for formations made later (Formations::keepDefaults). So the four slots P1.SetupSnapAttacks sets survive its EnableAllButtons, and with the disc the bums settle 1.27-1.44 m from the player on four sides and three snaps pass lesson 7 (repo:tests/platform/disc_level99_snaps_test.cpp, the second wave). Coney's reading: that CfgSetDefaultFollowSlotSet itself rewrites formations in use is not traced. FlagNetAddLink builds the level's flag network (src/world_objects/flag_net.h, 128 nodes) and FlagNetTraverse pushes PedestrianGoal (0x69) on a human that is not a player's; stand-in for its untraced Process: walk (mode 2 jog, 3 run) to the node nearest the human, then on to a random linked node within 1 m, for ever, standing at a node with no links; the variant chance picks and the two flags are kept, not used.

The story's goals, gangs and Warrior commands (src/ai/scripted_story.*, src/ai/story_goals.*, src/scripting/story_bindings.*, for level80 and level87). GoalMoveToExitFlag (2) and HuExitWorld set the brain dead (not a player's) and walk to the exit flag; stand-ins for the camera tests: arriving within 8 m of player 1 picks the nearest other exit (activity 8), arriving farther is HuDelete, and every 8 s a human more than 60 m from player 1 is killed (HuKill). GoalTravelPath (0x38) pushes a GoalMoveToFlag per point of an AddPath path (a number handle, as Coney's VM has no user types), then stops, loops or turns round. GoalMelee pushes a finding goal (0x41) and fights a target it names; stand-in: with no target this FindEnemy searches for the nearest hostile within the sight range, once a second, and a fight started under it keeps a searching one. GoalThrowObject (0x5d) walks into range and turns; stand-in for the throw: what it holds is let go. GoalPlayDynIdle (0x23) walks to the flag, turns to its heading and stands for its time; the clips are kept, not played. GangExitWorld sends each AI member out; once none is alive the callback gets the gang's id and the gang is deleted unless kept. Turf boxes, leader, respond percentage, hear ranges, investigate response, world-flag use and attack strategies are kept for readers not built. Warrior commands (src/ai/crew.*, Warrior commands, follow): 0 follow gives the crew's gang the follow tactic (WarriorFollowTactic: nine slots on the chief's formation, each AI member that can act flushed and given GoalFollowPlayer(9 m, chief, 3), which joins the formation, stands while the chief is within 9 m in a walkable straight line and otherwise pushes FollowFormation (0.75 m; after a failed walk it waits 31 updates), turns to the chief's heading when more than 60° off it every 2-4 s (1-2 s after it takes up again), and in fight mode turns to or moves to (3 s) the nearest attacker of the crew within 20 m or the chief's target, never attacking). FollowFormation walks to its slot, or with no usable slot behind the human ahead in the queue (2.5 m), by the walk distance (straight, else the planned route's length) at FollowFormation_PickSpeed's speed; every formation plan retargets the running moves (follow retarget), and MoveAction::retarget keeps a route whose last node sees the new point, else starts the move again (or, in a jump, drop or fence climb, ends it on its next update). A move with no route slows for the turn onto its point (the circle part of the corner speed), and arrival is in 3D, in plan within 1 m. Stand-ins there: "someone's enemy" is any fightable brain holding the follower among its enemies; the +0x2d5 match, the trains' part of the straight line, fight stance, pick-ups, idle clips, the vandalising while the walk fails, the 8 s reshuffle, banter and lines are not built; a move with no route has only the corner speed's turn limit (the trial walk over the polygons and the 11-update period are fix list item 7) and its arrival does not take one update's travel off (item 5); FollowFormation's time limit is read as "done once that long has passed". 2 defend still tracks the chief (2 m) (stand-in for the defend tactic). 1 attack gives the gang the attack tactic (WarriorAttackTactic, type 1): each AI member that can act is flushed and given FollowAndAttackGoal on the chief, which joins his formation and fights the best enemy within 60 m of him: a fight goal (4000 ms) within 1.1 × the far melee range, else the fight's run-in while it may close on him, else a straight move to him; with no enemy it stays within 8 m of the chief, turning to his heading every 4 s, and farther walks back to its slot (FollowFormation, 0.75 m). Stand-ins there: its enemies are its own plus the nearest attacker of the crew; dogs get the same goal, not AvoidEnemies; pick-ups, the fight stance, the fidgets, the chief's clip, the gang's target and the answer line are not built. 3 hold gives the gang the hold tactic (WarriorHoldTactic, type 3): each AI member that can act is flushed and holds his own spot (HoldPositionGoal, 1.5 m), then turns to a heading spread 360° / (members − 1) round the chief's after 0-1 s with enemies about, else 2-4 s; events 17 (no other human), 19 and 22 (argument 1) hold again. The hold walks back (gait 2, to 0.5 m) when outside the radius, turns to an enemy more than 60° off, and against one after him fights (4000 ms) from inside the radius once he is within 1.1 × the far melee range and in sight. Stand-ins there: the enemy is the best-scoring one by the melee terms, "in sight" a clear straight line; the fidgets, the shuffle, the way round an object, the answer line and event 20's look and taunt are not built; a gang whose brain +0x2d5 is set gets this tactic too, not the hiding one. 4, 5 and 6 start nothing. The level's start resets each player's last command to 0, enables every command and unlocks the menu (InitLevel). Gameplay dispatches follow, forced, for a new player 1 and when a scene that held him ends, once no scene camera is up; it unlocks his menu every 10 updates and runs the automatic commands each update: under follow, attack 1.5 s after an attacker came within his far melee range (stand-in: Coney's player brain keeps no target, so his attackers stand in for it); under attack or defend, follow again 1.5 s after no member of the gang has an enemy. Defend on a rear grab, mug or tag, the player modes 2 and 3 and steal at a store are not built. GangStartSpawner switches a spawner (above).

Warrior commands, revive and the weapon swap: the gaps

Coney against the original, command by command (user checklist items 11, 16, 8 and 37), as of this page's commit. The menu itself (repo:src/hud/war_command_display.cpp, repo:src/gamemodes/gameplay_war_commands.cpp) matches HUD: every slot reaches the dispatcher (dispatchWarriorCommand, repo:src/scripting/story_bindings.cpp), which hands the tactic to ScriptedStory::startWarriorCommand (repo:src/ai/scripted_story.cpp). The original's columns: Warrior commands and the lines.

The menu's "???" (user playtest, level80 checkpoint 2) is GSTRING.COMMAND entry 7, the text the original shows for a disabled highlighted command (HUD). That checkpoint's script switches every command off and follow back on (WCEnableCommand on the player's human) as its first calls, before the level has made his brain; Coney found no player for the handle then and dropped the switch, so follow too stayed off, greyed and labelled "???", and the level start's forced follow was refused. Fixed (2026-10-11): the player index comes from the human's creation until his brain exists (playerOf, repo:src/scripting/story_bindings.cpp); a command the script leaves off still shows "???", as in the original.

Command (menu slot) Original tactic and members' goal Chief's line / answer Coney Where
0 follow (up) WarriorFollowTactic (0x12, 9 m): FollowPlayer mode 3; a repeat adds FollowFormation (0x003114e0) 2 follow (86 hidden) / 131 partial: tactic and goals built; no line, no answer, no repeat repo:src/ai/crew.cpp
1 attack (up-left) WarriorAttackTactic (1): FollowAndAttack on the chief; dogs AvoidEnemies; gang's shared target from the chief's 1 attack / 132; gesture 676 / 680 partial: tactic and goal built; no line, answer, gesture or shared target; dogs attack repo:src/ai/crew.cpp
2 defend (up-right) Tactic_Defend round the chief (2.25 m): FollowAndDefend, which fights and rescues a cuffed or knocked-out chief 3 defend / 130; gesture 675 / 679 stand-in: each member gets GoalTrackHuman (2 m), which never attacks and never rescues repo:src/ai/scripted_story.cpp
3 hold (down) WarriorHoldTactic (3): HoldPosition 1.5 m; hide (0x13): Hide at wall points 4 hold (70 hidden) / 133 partial: the hold built; the hide is not (the hold stands in); no line, no answer repo:src/ai/crew.cpp
4 scatter (down-right) WarriorScatterTactic (0x25, 75 m): Scatter to hiding flags 158 scatter / 159 missing: the tactic is cleared and each member flushed, so the crew stands still repo:src/ai/scripted_story.cpp
5 wreck (down-left) WarriorStealTactic (0x26): WarriorVandalSteal 5 steal / 141, or 6 vandal / 140 missing: as scatter, the crew stands still repo:src/ai/scripted_story.cpp
6 (no slot) nothing; recorded as last none works repo:src/scripting/story_bindings.cpp

Around the commands:

Behaviour Original Coney
Crew out of range unforced (and forced defend): nearest member beyond 50 m refuses the command; 18 range / 92 range2; the per-player flag (out-of-range flag) also refreshed quietly every 40 updates, shown as the menu's out-of-range text and read by the mission-failure check missing: always given
Forced and quiet orders forced commands and a chief already speaking say nothing silent always (no lines at all)
Repeating the current order unforced: line again, no rebuild refused silently (matches, minus the line)
Automatic commands follow → attack / defend / steal; attack, defend → follow; steal → follow beyond 15 m partial: follow → attack and back only (CrewOrders, repo:src/ai/crew.h)
Hiding in shadow forced hold (hide) after 500 ms or the run time; the old command back on leaving missing
Revive (Combat) d-pad left (command 0x27) near a revivable downed member with a flash: 351, 353 / 354, flash spent, healed, 76 / 75 partial: player 1 revives (repo:src/gamemodes/gameplay_revive.cpp) a revivable member, but no code flags the crew revivable (open question); no AI rescue (SaveHuman)
Weapon swap instant; the object is attached to the bone of the low pick-up clip's first type-9 event and drawn there; 146 give_me partial: the hands swap, the anim set follows, the line plays; the Warrior's object is not drawn

Item 37: the given weapon. In the original the swap puts the object in the Warrior's hand exactly as HuPlaceItemInHand does (Human_PlaceObjectInHandByPickupClip, 0x0024c6d8: the bone and offset of the low pick-up clip's first type-9 event, message 0x1b take, his anim set applied), so it hangs from that bone like the player's (Objects: in a hand). He fights with it, confirmed (code): his anim set is the weapon's, so the same pad commands his attacks press play the armed swings (Combat); the attack pick adds the armed bonus to the strikes (Picking the attack); armed fighters fight from anywhere within 1.1 × the far range (pick-ups: using). He keeps a knife, baton or bat; outside co-op an object of kind 4 or 6 is dropped at his next think with no enemies unless the steal tactic runs (Think, step 3). In Coney, ai::swapHeldObjects (repo:src/ai/swap_prompt.cpp) moves the handle and PlayLevelMode::trySwap (repo:src/platform/play_level_objects.cpp) sets his anim set, so his attack pick counts him armed (repo:src/ai/attack_views.cpp); but PlayLevelMode::drawWorldObjects (repo:src/platform/play_level_world.cpp) places only player 1's held object (heldPose takes the player's snapshot), so nothing is drawn in the Warrior's hand. Whether his swings then hit with the weapon's clips and wear it was not run (Coney reading); the kind 4 / 6 drop is not built.

Fix for the implementer, in the order the player notices:

  1. Scatter and wreck do nothing (the crew freezes): build WarriorScatterTactic with the Scatter goal and WarriorStealTactic with WarriorVandalSteal (Warrior commands, steal); add the steal automatic command and its return to follow.
  2. The voice lines (item 16): call the chief's line from the dispatcher for unforced orders (volume 1.5, interrupting; the hidden variants), note in each command tactic whether it plays, and have one free member answer when it ends (the lines); Coney's sayCommand service already takes volume and interrupt. Add the attack and defend gestures (676 / 680, 675 / 679).
  3. Defend: replace GoalTrackHuman with Tactic_Defend round the chief and FollowAndDefend, so the crew fights for him and (with SaveHuman) frees or revives him.
  4. Draw the Warrior's weapon (item 37): place every human's held object at his own pose, not only player 1's (the same bone and offset as the swap's placement); then add the kind 4 / 6 drop rule.
  5. Revive (item 8): built for player 1 (Combat); the crew's flag (Human_Init's) and the AI rescue remain.
  6. The 50 m range refusal with 18 / 92, the out-of-range flag with its menu text and its mission-failure shortcut (out-of-range flag), then the hide tactic and the hide-in-shadow order.

The fourth mission's brain and human calls (src/scripting/mission4_bindings.*, for level34). BrSetPedType keeps the low 16 bits at the brain (stand-in: neither the civilian brain nor the mugging reads it yet). HuSetWounded wounds once: the fight, grab or throw ends, health drops to a quarter and the brain is flushed; healing clears the mark (stand-in: no wounded clips, and nothing reads the 14 s stamp). ChangeBlocker sets or clears flag 8 on the path polygon holding the point whose bounding box's middle is nearest (Coney choice for the centre). GoalStationaryThrower (0x8c, src/ai/riot_goals.*) walks back within 0.5 m of where the goal began, turns to the nearest hostile within three quarters of its sight range, plays clip 0x225 and waits 1000 × delay to 1000 × delay + 1000 ms (stand-ins: nothing is thrown, the eight object types are kept; the one wait in five with another idle is not built). GoalRiot (84) follows GoalRiot: the free act at Init, the turf gate on the nearest player (pointInTurf over the gang's turf volume boxes), the one-draw decision, the roam (toward the player every 27th destination, the turf every 79th, else the 10 m wander, else a point 5 m away; arrival 0.5 m, gait 4, the move deadline rolling over each second, 30 failed moves to leave), the acts counting down, the fight pick (gang soldiers only, the sixth argument letting the player be picked) and the exit flag in the gang's turf at gait 4. Stand-ins: the smash and loot searches find nothing (no vandalisable objects or store loot are hooked), so an act only counts down; a turf box's centre and radius are its middle and half its diagonal; "on an area" and "reached in a straight line" are both the planner's straight-line test and no point is dropped to the ground; human +0x333 is 0; a running move is replaced rather than retargeted; the shouts, taunt and head glances are not made.

The story's tactics (src/ai/story_tactics.*; Attack and Confront are the Rumble's above). Group moves give the leader the moving goal and have the others track him (3, 1, 0.75 and 4 m; stand-in for Goal_FollowPlayer in the formation): MoveToFlag and WalkinTall fire 8 once the leader's walk ends, WalkinTall returns 7 while a member is within range of a hostile, TravelPath walks the AddPath path (mode 1 looping, else 2); Wander's leader and a pathless TravelPath's stand. HanginOut and UseFlag walk the members to the flag (HanginOut narrowing view and sight unless fully aware); UseFlag returns 7 once player 1 is within range of the flag and sends them off. Idle holds places, and with dynIdle breaks the dynamic idles off on events 1, 11 and 16, returning 15 when none is left. Defend tracks the human at range: 11 once he is gone or out of health, else 9 with no enemy. HoldTheLine sends min(line length, 60 % of the members) to the line's two flags in turn, the rest to the third: 9 with no enemy. Pursue makes the target gang's leader an enemy and melees: 9 once that gang is gone or leaderless, 7 while a member is within range of one of it. Scout (src/ai/tactic_scout.*) posts each member where he stands (the Scout goal: threat response 0, a 500 ms scan, back to his post at a walk or a jog and turned to his heading); the members at their posts scan (ScanSchedule), and every 200 ms one with an enemy and no fight is given one. A member at his post who is hit by someone he sees, warned of an attack or spots an enemy (events 1, 0x10, 0xb) fights him and, with a responder spawner (state 9 or 10), the enemy's gang's second wanted timer not running and no other caller, gets a CallGang goal: he runs (gait 5) to the farthest phone flag (activity 6) within range (twice his far melee range when negative), or calls where he stands, and after 1.5 s the call is made and the enemy's gang gets its second wanted timer. Stand-ins: ManWeaponPile, Vandalize, Steal and AvoidEnemies hold their places, their goals not traced; the scouts' radar blip, clips, glances, roaming, help call within 15 m, investigation of an unseen hit and the gang's alert state, the caller's lines, clips and call spot, and the responders themselves (the level only logs the call); banter, answering violence, the anim substitutions, HoldTheLine's 12 and 14 and Pursue's search time are not built.

Open in Coney. The pattern read at Start and in the pick; the reposition's band-keeping move and taunt; the move's sight checks; choke points and the waypoint queues; the dynamic obstacles; the legs of edges 8, 0x10, 0x40 and 0x80 (taken as plain walking, with +0x284 2 and 4 never set); the move's object to face; the turn clip (398) on the spot; GoalMoveToFlag's interval gesture, the fight stance's switch-off and the gang's notice; the scene system, the dynamic clip slot and clips by id; the head look-ats; the sender of message 1 and its attacker; the gang's alert state, bounds, return to calm and neutral rule; the anim substitutions; the crowd's cheer idles; the formation's ground ray, line of sight and assignment mode +0x275; the dealer's run to a flag, line of sight, shove and icons; the other tactics; the attack's steer, the post-block pause and the run-stop.

The hub's goals and gangs (level95; repo:src/ai/hub_goals.h, repo:src/ai/scripted_hub.h): GoalAreaWalker (0x47), GoalBoxer (0x9e), GoalGrabTarget (0x1f), GoalPeddler (0x50), GoalPlayGenAnim (0x26) and GoalShopkeeper (0x82) follow their binding pages; a fleeing gang (GangCanFlee) sends its class-11 members off with the pedestrian reaction goal (0x6b) and line 9 (its flight: the nearest reachable exit flag, until no camera sees him, else AvoidEnemies 7-14 m). Stand-ins: the boxer fights with his own attack weights; the grab target is held by standing at it, facing it, with no damage; the goals' lines (beckon, phone_gang...) are kept, not played; the flight runs 10 s at gait 4, 10 m legs away from the enemy; a gang's starting count is noted when GangCanFlee turns it on.

Gangs of a kind (2026-10-06): GangMakeEnemiesOfType makes a gang and every gang in use of a kind enemies both ways (Gangs::makeEnemiesOfType). GangSetAlwaysSeen keeps the gang's byte (Gang::alwaysSeen()); Coney stand-in: Coney's brains have no enemy scan or line-of-sight test yet, so nothing reads it.

The fifth and sixth missions' goals (2026-10-06; repo:src/ai/chase_goals.h): ActTurnToDir queues the turn to a heading (Turning); GoalGuardFlag (43), GoalLeadChase (73), GoalBigLedgeThrower (138) follow their binding pages and GoalDevilRun (152) its section: the hindmost segment, the chaser farthest back, the pace and a move action to the path's last point whose speed the pace sets (MoveAction::setSpeed()). Coney choices and stand-ins: the lead chase steers straight from point to point (no route planning) and counts a point reached within 1 m; the guard's LeftTurf on an event and its time word are not built; the devil runner's attack fights the chaser (the melee and engage-enemy goals are not built) and the urgency's brain byte is not kept; the ledge thrower has no objects to pick up or throw yet, so each throw is a turn to the nearest enemy (or a target flag) and the wait, and its animation name is kept, not applied.

Open questions

  • Why Coney's waiting attackers taunt about a tenth as often as the original's (the level99 sparring fight: 11 cheer2 taunts in 1,198 updates in the original, 0-2 in Coney; The reposition). To check, in this order: how long each Warrior waits in the reposition (Coney may let more of them attack at once); the gesture slots, which the original keeps per reaction kind (Brain_GetReactionKind) with as many as the brain's help level (Brain_GetHelpLevel, 1 or 2) in Ambient_MayGesture (0x00291ed0), where Coney keeps two for everyone; and the close-in test, which reads the player's own target.
  • Movement and world use: MoveToUseFlag's clip states once he is on the flag; the pedestrian's flag re-use timer (goal +0x6c, 65 s inferred); the gait AvoidEnemies keeps in its +0x3c; why ChaseSupport jogs within 3 m; ManWeaponPile's Process (0x002a3498, not read).
  • Who sends events 6, 9, 10 and 0x11 (0x15: Gang fights), and who reads event 0xf (the tag-spot message); what reads the tackle meter brain +0x148 besides the fight goal, and what brain +0x28f controls.
  • The target's pattern bytes +0x5d0 / +0x5d1. Gang fights: its page.
  • GoalBumLogic's begging (type 2's prompt, the chance, the callback, the 12 s timer) and what sets +0x36; what GoalBackoff, GoalMoveToUseFlag and the pedestrian goal (0x69, with its two variants and FlagNetTraverse's flags) do each update (Process), and the use-flag goal's two floats; what GangInvincible sets on a member (0x0016a000).

  • What sets polygon +0x02 in the level files (non-zero for most areas with route nodes, the link kinds), and the goals that search with the mask 0x13 (0x002aafd0, 0x002c1470). Whether the level-table ids 0xe, 0x33, 0x34 and 0x52 are level14, level51, level52 and level82 (follow).

  • Which record +0x08 bits the play-anim action's clip holds (0x0025a3e0), its rate for a dynamic clip and its blend out; Coney holds 0x80000.
  • Which climb clips Vermin's fence plays (tall or short fence, standing or running).
  • Which edge sign the inside test counts +1, and what the clockwise polygons (most of them, nearly all with polygon flag 1 or 2) are for if they contain nothing.
  • The formation's assignment mode +0x275, and GoalFollowPlayer's modes 1, 2 and 4.
  • What reads the turn action's +0x10 (ActLookAt's turn value) and the play-anim action's flag (loop or hold?).
  • What the rip-off's gen_push clip does to the buyer (Buying).
  • Who sends a gang's event 18 (a member down or dead), and with which attacker: Gang_OnEvent reads it, but its sender was not found (0x0022dd98 and 0x0022e020 are the arrest's).
  • What a scene's end passes to the callback Goal_PlayAnimation hands it (0x00353f40).
  • The tactic event codes (TacticGetString, 0x00315c58) passed to a tactic's callback.
  • Whether CfgSetDefaultFollowSlotSet (0x00294788) rewrites the slots of formations in use or only the pool's defaults for formations made later.
  • A turf box's radius +0x40 (Coney: half its diagonal). Human +0x333 is the detail level from the camera distance (Characters).
  • The hold's damage (0x00510acc) of GoalGrabTarget and the boxing attack weights (0x00511120) of GoalBoxer.
  • Which sound each hub goal line plays (beckon, store_greet, phone_gang, dead_meat, cower, mug_grunt).
  • The Warrior commands' tactics (0x00310e00, 0x00320530, 0x00313400, 0x00319570, 0x00320b60), the exit goal's on-screen test and the finding goal's search values (90, 30, 10).
  • The think handlers of types 2, 3 and 5 in detail; what goals the Warriors' think pushes for an ally.
  • Which class +0x11b value 13 is (Combat), and what the byte *(0x0051489c) + 0x56e3 that lets every AI counter is.
  • What stopped Vermin's route jump at the level99 window (x 47.76, by the panes at x 47.40) in the window-jump state, and whether a normal play-through clears it first; no run has seen a jump land on its point.
  • What reads RunCarrotGoal's hostile flag (goal +0x25, set by RunCarrotGoal_Init 0x002e1230, AI goals); Coney does not model it.