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) whenindex % 5 == (counter >> 1) % 5: each brain thinks once every five character steps (6 Hz), staggered across brains. Confirmed (runtime): a hook on0x0028f6c0over 215 steps matched the formula on every step, withcounterthe tick counter0x005104f4; brains whose human fails0x0023d790(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:
- Bookkeeping (
0x0028ac18; for types 1 and 20x0028fff8;0x002920e0;0x0028bbf0). - 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. - Every 44 updates, a line-of-sight check on the target.
- The reaction goal (
Brain_UpdateReactionGoal, below). When it returns 2 (none active), the goal stack:Goal_Processon the top goal; 2 = done (pop it and process the new top in the same update), 0 = stop for this update, 1 = run again. - The actions (
Brain_RunActions,0x0028fe28): start the front action when its delay is over, update it, and pop it when it returns 2. - 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:
Human_KnockOut(h, 0)(0x00230328; fromHuman_OnHealthOut,HuSetConscious,Human_Destroy) sets state0x40000and, unless the human is already dead or wounded (0x100050000), first callsBrain_OnKnockedOut(brain, 0)(0x0028c3b0).Human_ReleaseInstancepasses 1 and skips the brain part.Brain_OnKnockedOutends 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 event0x12with his foe.Brains_Update(0x00293b28) runs a brain only while+0x08is set, so a knocked-out human neither thinks nor updates untilHuman_WakeUp(h, 0)clears the state and callsBrain_EnableAndResume. Its per-human gateHuman_ShouldThinkThisUpdate(0x0023d790) guards both the think andBrain_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+0x2d7is 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).- The claims do not test the holder's health:
Brain_ClaimActiveAttacker(0x00291008) andBrain_ClaimAttackSlotonly 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'sCfgGangvalue 5 (+0x6f) × 10. A FollowAndDefend (0x35) or HoldFlag (0x7d) goal on the stack clears it. - Process (
0x002b60c0):- Not grabbing: done.
- Near a train (
Brain_IsNearTrain): write command 5 every update. InPlayer_UpdateGrabbingcommand 5 (L2 held,PlayerCmd_IsLetGo0x0027bf08) lets go: grabber 95, victim 94 (Combat). Confirmed (code). - The target is the held man (
+0xc4). While actions are queued, wait. - 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 byGameState_StoreWarriorCommand0x0041c4c8). 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 at0x002b622c. - 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): command0x19after a 33 ms start delay, the base field+0x04) and wait. Command0x19is the grab spin (PlayerCmd_IsGrabSpin0x0027bd98, 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) at0x002b60c0and0x0027f3b0. - 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.) - 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) at0x002b5fe8-0x002b6040. The first rule that gives one wins:- 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. - When the grabber's power class has byte
+0x39= 1, a random choice among the walls next to them. The power class is theCfgPowerClassrecord at0x006619a0+ class × 0x44, read throughHuman_GetClassRecord(0x00222b78);+0x39is 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 isSectors_IsWall(0x0029ea10). That is the sector wall bit (0x4) from the lazy wall probe (Neighbour sectors): a straight walk 1.5 m out thatHuman_CanWalkStraightTorefuses on the navigation mesh, not a collision ray. Both sector sets are refreshed when older than 1 s (Brain_GetSectors(…, 1000)). Confirmed (code) at0x002b5cf0-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 andBrain_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) at0x002b5db4-0x002b5ee0. (Into a wall or into his mates.) This fallback, like the walls, runs only when the power class's+0x39is 1. - With the gang's tactic of type 2 (Defend), away from the defended human, as in 1.
- Otherwise
Random_Int(3): right, ahead or left (0-2), never behind.
- With a HoldFlag goal (
- Start (
- Mounting (
0x13, the tackler on top,0x002b6638).- Not tackling: done.
- Near a train: command 5, which in the mount (
Player_UpdateMounting0x0027ec20) gets off: 244 / 245, the victim rises with 199 (Combat). - The target is the held man. Pick a kind; 45 and 36 do nothing.
- Kind 35 (the ground punches): 40 % of the time two of them, chained.
- Grabbed (
0x14,0x002b6848).- Not grabbed and not being mugged: done.
- A grabber who is a friend: nothing.
- A civilian (type 4) not in a standing reaction calls for help every 30 updates (
Gang_BroadcastHelpCall, 10 m). - With threat response 0, other than a dealer or a class-221 human, he only waits.
- Otherwise the grabber becomes the target.
- Once he has been mugged, the latch
+0x10stops all struggling. - Otherwise pick a kind:
- 31 (the struggle strike) is queued with delay = chain delay × (1 − max(0, (health % / 100 −
h) / (1 −h))),hbeing 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.
- 31 (the struggle strike) is queued with delay = chain delay × (1 − max(0, (health % / 100 −
- 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; Start0x002b4a38notes the time).- 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.
- 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
0x002240e8allows only from this goal. Kind 42 is the command0xf(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
0x00511438as 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:
- 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).
- Every caller passes b = 0, so a help call always has
- Who hears it (
AI_AlertNearby0x00293768):Humans_FindAheadlists up to 60 humans within range of the caller's position, in table order, skipping the caller and slots without bit 0 in0x00715390. Players are included.- The filter
Brain_IsInHearRangethen 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).
- 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.
- 5 m:
- What a hearer does: the event goes through his human's vtable
+0x44(Human_OnEvent): his script handlers, thenGang_OnEvent, thenBrain_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.
- A gang with a tactic consumes event 20 there, and some tactics act on it: Attack, Crowd, Scout, and HanginOut or
Idle with
-
The fight that follows:
Brain_OnViolenceSeenpicks a side (Brain_PickSideInFight) and callsBrain_Fight(side, −1, 0). With no HelpRespond goal (0x1d) above his base, it also callsGang_SendHelper(self, side, 0, −1)(0x002b75b8).- Despite its name,
Gang_SendHelpersends 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 isGang_SendHelper, and all seven callers of that pass 0 (AttackTactic_OnViolence,AttackTactic_Process,Brain_OnViolenceSeen,CopBrain_OnEvent,HanginOutTactic_OnEvent,IdleTactic_OnEvent,ScoutTactic_OnViolence). SoHelpRespondGoal_Processalways 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+0x140and 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_Init0x002fe248: clip0x29ewith 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 level0x53). Confirmed (code).
- Despite its name,
-
The new-enemy flag (
+0x0c) has one reader, the cop's handler (0x00302340, at its event0x14). There, a cop victim gives crime 2 (assault on a cop) only when the side picked is not the caller and+0x0cis 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:
- No aggressor: take the victim's brain target (
+0x124); none → 0. - 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). Fvand notFaand notTa(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_SetEnemyBiton brain+0x20cof each), andTacounts as true.Ta→ the aggressor.- Otherwise
Faand notFv(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 with
+0x11set: a help call within 20 m (Gang_BroadcastHelpCall0x00293640) naming the hitter. - 2: target cleared, actions, goals and the enemy list cleared.
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.0x10(the attack warning):- The near radius is 3 m (9), or 0 when the attacker is hidden in shadow, throwing, and this brain's
+0x2d4is clear. UnlessHuman_CanSeeHuman(+0x12c, R, near, 2 m, self, attacker)and the attacker is not friendly, nothing more. - Brain
+0x200+ 1 (the block's trigger); unlessHuman_ReactToThreatalready holds, the two gangs are made enemies (Brain_MakeGangsEnemies0x00290328); a help call within 20 m. - By the top goal: StationaryShooter (
0x8d) with free actions: clear the actions and, withGoal_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_CanCounterTackleorHuman_CanCounterGrabagainst it) andRandom_Int(100)<Human_CounterChance, clear the actions and, on an empty queue, command 3 (the counter). - When the attacker has a pad or is a Warrior (type 3):
Brain_TryPatternBlock(0x00292158).
- The near radius is 3 m (9), or 0 when the attacker is hidden in shadow, throwing, and this brain's
0x15: for each human holding an attack slot on him (brain+0x1a4), except classes0x77,0x6aand0xb2, whose gang kind's field0x6dis 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
0xfunder BigFighter; class0x80only 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
0x10or0x8fat 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).
- his own target: a taunt of kind 4 (id 0, or
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):
Brain_MaybeSpotPlayers(0x0028fae8).- 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+0x19eand brain+0x28c. - Threat response set, no fight goal, no Chase goal (
0xc) and a non-empty enemy list: brain+0x28f= 1 andBrain_PushFightGoal(brain, −1)(no time limit). - 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 - Its reader is not traced (inferred: how hard the target is running from him).
- With crime type 12 (Trespassing) enabled, every 3 thinks, the gang having no tactic: when he may respond
(
CopBrain_CanRespond0x00302038) or already has a fight or chase goal,CopBrain_ChaseTarget(0x00301248). - Every 17 thinks: the nearest store flag (activity 14) with group bit
0x10000within 20 m; the nearest player to it within 10 m whose human byte+0x5b7is set, a valid enemy, and he may respond → crime 1 (break and enter), below. Then the think ends. - Every 20 thinks: each enabled tag spot (activity 12) within
Rwith 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. - 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 ×Rbeing squared too) → crime 1 (lock) or 11 (theft). - Every 8 thinks: each player holding a world object of type hash
0x3c4e590bwhom 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, and0x10:CopBrain_ReportCrime(cop, attacker)(0x00301cf8): nothing for a friendly attacker; one throwing at him first calls for help (Brain_CallForHelp0x00291d08, done if answered);+0x200+ 1. Without a fight goal and up: when the attacker is beyondRor out of line of sight, the cop treats it as a crime seen (Brain_OnCrimeSeenwith event0x17) 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_PickSideInFight0x002912b0).- 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, goal0x7a: 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
+0x24and byte+0x0cis 0) → 2; else 14 (none). A civilian assault while two gangs fight within his far range of the victim (Humans_TwoGangsFighting0x0029e168) 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 whenHuman_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+0x5e8clear): CallForBackup when needed and SpotCriminal for the crime. Last, with no HelpRespond goal (0x1d) above the base,Gang_SendHelper(cop, offender, 0, −1)(0x002b75b8).
- 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
0x17:Brain_OnCrimeSeen(brain, event, 0)(0x00291668, thenCopBrain_OnCrimeSeen0x00301538).- Others: the shared handler.
Gang soldiers (type 2)¶
Think (GangBrain_Think 0x00304608):
Brain_MaybeSpotPlayers.- Unless human
+0x19eis 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. - Threat response set, no fight goal and a non-empty enemy list:
Brain_PushFightGoal(−1). - The pursuit counter, as the cop's (step 4).
- A weapon, every 6 thinks: wants one (
+0x265), holds nothing, and his gang kind's pick-up factor f (GangConfig_GetField6E0x001644f8) 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_AllClear0x0029eaa0), the nearest pickable object within 2 m (Ai_FindObject0x0029d5f0, mask0x30000); 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, kind0x21). - A hat, every 12 thinks, when he may go for one (
Brain_CanSeekHat0x0028bfa8: allowed (+0x266), not busy, bare-headed (+0x364empty), nobody attacking him, class0x82-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. - Every 15 thinks, with no target, no held flags and none of the busy states
0x7bf9e9f7ff0: the first free (+0xdcnone), enabled tag spot (activity 12) within 10 m with a clear ray → he sends himself event0xfwith the flag (inferred: go and tag it; the reader is the goal on top).
Events (GangBrain_OnEvent 0x00304fa8):
- 1 with
+0x11set, 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 goals0x8b,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), orBrain_AddEnemy+Brain_SetTargetwhen 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. 0x14with+0x04= 0, his gang not of kind0x17: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 chance0x00510adefor a lookout (Scout0x6for PathScout0x70on the stack) else0x00510add; 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:
- 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, id0x10); done. - Otherwise
Brain_Fight(side, −1, 0)(orBrain_SetTargetwhen fighting) and, with no HelpRespond goal above the base,Gang_SendHelper(self, side, 0, −1).
- 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 (
- 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):
Brain_MaybeSpotPlayers; timer B (+0x2c8) cleared once past.- 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).
- Reacting to the player, when all hold: the script's response byte
+0x228is 1 (binding), timer B is 0, class+0x11bis not 6, he may react (CivilianBrain_CanReactToCrime0x002ff198), the nearest player is within 10 m, that player's brain+0x2d5is 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 line0xaf.
Events (CivilianBrain_OnEvent 0x002ffb30):
- 0: a bum (class
+0x11b6 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 andBrain_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_CanReportCrime0x0041cf80),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+0x29fis 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):- 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. - 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 otherwise0x00510adf. Only within 15 m of the offender and when he may react; MoveToUseFlag popped. - Ped type 3 in a gang of kind other than
0x17: nothing more. Ped type 3 in a kind-0x17gang, his human+0x3b8clear and the victim's+0x3b8= 1: threat 2,Brain_Fight(offender, −1, 0), line0xcd; done. - 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)). - Otherwise, any but ped type 3: PedReaction (
0x6b) against the offender, no line.
- 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
0x17within 15 m of the crime object:Brain_OnCrimeSeen(→CivilianBrain_OnCrimeSeen0x002ff2b0).- 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
+0x3chandle) contains the object (event+0x04for 6,+0x00for 7): disturbance (the human at+0x24, reason 0, the object, no call). - 10, with a Shopkeeper goal whose
+0x54and+0x30are 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 sideBrain_PickSideInFightpicks, reason 2, no call).- Others: the shared handler.
The Warriors (type 3)¶
Think (WarriorBrain_Think 0x003052f0):
Brain_MaybeSpotPlayers; threat response set, no fight goal and enemies →Brain_PushFightGoal(−1); the pursuit counter as the cop's.- Pick-ups, every 6 thinks: The Warriors' pick-ups. The search is 1.5 m with mask
0x10000; the mask is0x30000(and the search's last flag 1) when his gang's leader is neither grabbing from behind nor tagging, the gang's+0x32is 1, a member of the gang is under attack and his own enemy list is empty. The object must also passWorldObject_CanBePickedBy. - 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. - Every 8 thinks: holding a world object whose type byte
+0x64is0x9awhile his human+0x5b7is clear → drop it (inferred: loot only inside a store). - Hats for classes
0x12,0x14and0xbc, every 6 thinks, under the follow (0x12) or hold (3) tactic and with no hostiles counted (+0x152= 0): unless his+0x364already holds the named hat object (0x0056a2d0), thedyn_warr_cbobject within 20 m →Goal_GetItem(4)unless a GetItem goal is above the goal base. - 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 flags0x1c1ee60), the first player carrying (+0x364) a world object of type hash0xbbbef927within 10 m and in line of sight: actions cleared, a turn to him, a wait action, and line0x6dat him. - 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 (+0x2e5clear), not blocked, standing (gait 0), in the player's gang (or+0x121set), with no attacker slots taken on him, and on the right side of the player (Human_GetSideOf= 0). The prompt string is0xc(both hold),0xd(he holds) or0xe(the player holds); the swap itself is the event-0 prompt (The weapon swap). - Timer
+0x2dc: once past, cleared with byte+0x2e4. - 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):
Brain_UpdateAlertness; a target that is no longer up is dropped.- Every 300 updates,
+0x152= how many AI humans of all 32 gangs hold him in their enemy lists. - Every 2 s (brain
+0x160),PlayerBrain_ScanNearEnemies(7.5 m, target)(0x0028af88). WarChief_AutoCommand(Automatic commands) every update; every 40 updatesWarChief_CheckCrewInRange, quiet and forced, so it only refreshes the out-of-range flag (call at0x003037c4).- Brain
+0x110= his heading. - While
+0x2e4is 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 (+0x2e5clear) 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:
- the receiver is talkable (his interface's "has a handler or
+0x1b2",0x0021d570); - neither of the two has his actions blocked (
HumanRecord_AreActionsBlocked,0x00228228); - neither has any of the held flags
0x5c0221f(busy: attacks and reactions, EngageEnemy); - 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 mask0x2803).
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):
- 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). - 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. - For each dropped object, the other human takes it: every task on his animation stack (
+0xd8) gets slot+0xc8with0x10(not traced), then the object is placed straight into his hand without a clip, asHuPlaceItemInHanddoes (Objects: scripted placement):Human_PlaceObjectInHandByPickupClip(0x0024c6d8) picks the low pick-up clip for the type'spickup_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 message0x1btake 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 gets0x20000000(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)). - A left-hand or hat object (
pickup_anim5 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 forHuPlaceItemInHand).
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, …)isBrain_StartFight(0x002b2b90): clear the actions, thenBrain_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 event0xb), takes it as the target (Brain_SetTarget,0x0028cfe0) and callsBrain_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) and0x41(FindEnemy) already on the stack, with every goal above them, then pushes three:Goal_Melee(0x002add08, theGoalMeleebinding'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_Initwith the duration). The stack ends FindEnemy, Melee, Fight (top). So both earlier statements were half right: it pops and then pushes. Confirmed (code) at0x0028d190andGoal_Melee; the order confirmed (runtime), in lesson 12.Brain_SetTargetreleases the old target's attack slot and claims one on the new target (Brain_ClaimAttackSlot,0x0028df30): the target's list+0x1a4holds at most+0x1e4attackers, 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.
- The update counter
+0x18is raised by 1. - Done when the target fails
Brain_ValidateEnemy, or when it holds no attack slot for A. - The tackle try (
FightGoal_TryTackle, below). When it queued a tackle, wait. - 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.
- Every 30 updates, done when there is no walkable straight line to the target (
0x002221b0). - Re-target at most once a second (
+0x14), from A's sector record (re-targeting). - The block try (
Goal_TryBlock). When it pushed a block, A gives up his active-attacker place on T and waits. - While actions are queued, wait. When the last attack ended (
+0x28), give up the active-attacker place. - The deadline (
+0x24, The durations). - When no kind is chosen (
+0x10= 45), pick one (Brain_PickAttackwithHuman_CanUseAttackKind). - The stance. Drop the fight stance for kinds 19 and 20, and always for a class whose
+0x11bis 6 or 7. Otherwise take the stance. - 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:
- The grab and snap try (below). When it returns 1, wait. It may change the kind and set the stick angle.
- When
Human_CanStartAttackrefuses 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 withHuman_CanStartAttackand wait. - 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). - A class whose
+0x11bis 13, 6 or 7, or a dealer (type 5), may say line0x11(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_TurnToFaceObject0x00221cd8, 0.3 s), which does not use brain+0x110. Whatever it left in the brain's move fields is wiped by the next update'sBrain_StopMove(0x0028ac18, the first call ofBrain_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+0x110or+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,
+0x110is 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+0x110only for a "no stance" class. - the attack's own turn: the attack starts (
0x00261c80,0x00262368,0x00261a08, the grab0x0026c548, the tackle0x002707a8) turn him to his target (the brain's, Combat) withHuman_TurnToFaceObject, and the steer onto the target (Attack_SteerToTarget, Combat) runs to the clip's first event.
- 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,
- Otherwise nothing. In a fight stance with no movement action running,
+0x110is 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.
- 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 (state0x20).- 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).
- T cannot be approached. This is when
Brain_CanBeChasedon T's brain (0x0028abc0) is false, or B+0x2d3is clear. Return kind 0 (X1) when A holds a throwable object or one of kind 4 or 6; otherwise return 45. - The armed bonus.
bonus= 100, or 0 for a class whose+0x11bis 9, 10 or 13. -
Candidates. For every kind
kfrom 0 to 44 that passesfilter, these rules apply in order:- 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_IsCharge0x00229b60is true for 0 and 19-21.) - 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)). - The weight.
w= the weight: B+0x298[k], or the override table at B+0x208. - 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).
- kinds 10, 25 and 36:
- A busy target. For kind 22 (the grab) when
n> 1:w+=w×n/m. - Armed. When
armed, kinds 0-9 withw≠ 0 getw+=bonus. - A rear-grabbed target. When
rear, kind 22 withw≠ 0 getsw+= 200. - The pattern. When
pattern, kinds 6-9 (the three-hit enders) getw+=w×f.
Each scaled weight is truncated to an integer. The kind and its weight are appended, and
wis added to the total. 5. The draw.r=Random_Int(total). The answer is the first candidate whose running sum of weights exceedsr. With no candidates, or a total of 0, the answer is 45. - Running. While
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 state0x2000and A has state0x1000; - 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 without0x2000holding0x400000) is not accepted. A must also hold none of0x48000. 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 state0x20(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));CanUseasks(A, 0), which counts sets 1-3 only for a player. - 27, 28 (in a grab): A in any of state
0x10c0and holding T; no free test. - 38, 39 (mounted): A in any of state
0x1400and 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 fromBrain_Init0x0028a570) are taken when an attacker targets him (Brain_ClaimAttackSlot0x0028df30, fromBrain_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 byBrain_ClaimActiveAttacker(0x00291008) only when an attack is about to be queued. Only the firstsplaces are used:sis T's spacing byte+0x14a;- while T is down, out of the fight or arrested (state any of
0xe0000),sis+0x14b.
- The spacing bytes start at 1 (
0x0028a570). Each slot claim raises them:+0x14ato at least the attacker's gang'sCfgGangvalue 2 (gang record byte+0x6c);+0x14bto at least its value 3 (+0x6d);- a Warrior target (type 3) keeps
+0x14aat 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).
- 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.
- A tactic ring applies when A's gang has a tactic of type 2 (Defend) or
0xd. - 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.
- 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'sCfgGangvalue 7 (gang record byte+0x70).- When
kis 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. Withk= 1 the threshold is 36, the meter's cap, so it never fires.
- When
- 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_CanStartAttackpasses (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
0x2000or0x10000000000, 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
CfgGangvalue 8 (+0x71)gis 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:
- While actions are queued, wait.
- Choose the target. With threat response ≠ 0, the best-scoring valid enemy in the enemy list
+0x164(Brain_PickBestEnemy0x0029f230, 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). - No target: done when the enemy list is empty, or when its nearest enemy is hidden in shadow (
+0x2d4) or fails0x002225d8; otherwise push a Spectate goal (0x10) and wait. - With a target (
0x002ae2e8), withdthe distance andR= 1.1 × far: a new target resets the counter and sets+0x2d3.- Armed (holding a weapon,
0x00223ea0or0x00224000), or unarmed,+0x2d3set andd≤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) whend> 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,
+0x2d3set,d>R: push an EngageEnemy goal on the target when0x00290588allows 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 (kind0x17) 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.
- Armed (holding a weapon,
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:
- Actions blocked → done;
0x00228428→ wait. The fight stance is dropped (it runs, not shuffles). - Stopping (
+0x39, set by the stop below): wait until the human's+0x1a8is 0, turn to the target (0x00221cd8, 0.3) and end (2). - Target no longer valid, hidden in shadow, or
0x00290588refusing → stop. - 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). - The target brain's
+0x1ecis cleared (he may be attacked at once). - 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. - The last move failed (
+0x284) → stop. - Gait 4 (run); 5 (sprint) when the target runs (gait > 3) and the fighter's stamina is above 50 %
(
Human_GetStaminaPercent0x00222fa0, Sight). - 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). - Give up (done) when the target is out of sight, at least 10 m away, and either 20 m away or brain
+0x28dset, and his gang's+0xdcis 0. - Charge: within 1.6 m with the charge armed or the target running, below.
- 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 of0x0029eb38; 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).
- A taken sector. Take the sector of T's record that A approaches from (
Human_GetSectorOf0x0029eb38of 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. - The kind (once;
+0x20, 45 = none):- A cop (type 1) takes kind 0 (
X1) with 50 % (80 % against a gang member, class+0x11b10), else 21 (the tackle). It is forced to 0 when T's+0xe0has bit0x40. - Anyone else uses
Brain_PickAttack(B, T, Human_CanUseAttackKind). When that is not a charge kind (AttackKind_IsCharge0x00229b60: 0, 19, 20, 21), the engage goal stops (step 2 of the goal: halt, turn to T, end). Melee then pushes a fight goal.
- A cop (type 1) takes kind 0 (
- The start test. When
Human_CanStartAttack(A, T, kind)(0x00224778) passes:- Clear the actions; when an action refuses, wait.
- Queue one attack action directly (not
Brain_QueueAttack), no delay. Kind 0 carries A's heading as the stick angle. - 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_IsClear0x0024df40→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 30LOW_FENCE, 2GLASS, 187STOREDOOR_GLASS, 122RAILING, 107CHAINLINK_NOCLIMBand 1NONE(the list built inRay_IsClear), so an AI sees through fences, railings and glass. Its distance is not limited; callers test range themselves. Throughhitit 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 ofhitinferred.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 againstcos(fov × 0.5)). Confirmed (code).Human_CanSeeHuman(fov, range, near², shadow², looker, target)(0x002223e8): false beyondrange; a target hidden in shadow (his brain+0x2d4) only when the squared distance is belowshadow²; withinnear²(squared distance) the field of view is skipped, elseHuman_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 byGangSetAttackablethroughGang_SetAttackable0x0016bb58and by the end of a scripted goal,0x002d4248; cleared byGoalGrabTargetwhile 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+0x11fclear is still attackable by an attacker that has no goal0x1f. 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):
- For each of the 32 gangs that is active (gang
+0x18) and an enemy of his (Gang_IsEnemyOf0x00168fe0: 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 withinR:- A member hidden in shadow (his brain
+0x2d4) is skipped beyond 2 m, and within 2 m unless the scanner's+0x2d5is set. - 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+0x11bis 10); otherwise, with fewer than 3 fights (+0x2d0), an AI scanner lists only a member less than 1.9 m above him. - The filter
+0x15cmust accept him:Brain_IsValidEnemyOf(0x0029bf60,Brain_ValidateEnemyon the scanner's brain) for every type but the Warriors, whose filter isFilter_IsThreatTo(0x0029bf98): a cop only within 10 m and whenBrain_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 thenBrain_ValidateEnemy. - 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_IsHiddenFromBrain0x00223c50); beyond it, or when sneaking, he must also be in front (direction · facing >c). ThenHuman_HasLineOfSightmust pass.
- A member hidden in shadow (his brain
- The scanner gang's chosen target (gang
+0x10) is added when it passesBrain_ValidateEnemy. - 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. - 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 throughBrain_AddEnemy); every listed human's brain+0x152is raised by one (Brain_CountHostile0x0028ef20), 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_PickTarget0x002b4158): 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 goal0x98is 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:- No valid watched human: done with join, else wait (until the time limit).
- With join he is made an enemy (
0x0028ff78); tackling → an attack action of kind0x24. - Unless the watched human's gang has
+0xdcset, out of sight → done; in shadow (+0x2d4) and beyond 3 m → done. - With join and may engage, not after a failed move, when
0x00290588allows a chase: an EngageEnemy goal (taunt0x8f) when the target runs (one update in five) or+0x2cis set and he is beyond the far range. - 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).
- List the world objects round
centre(A's position by default;ObjectManager_FindObjectson the object list+0x840, at most 384), nearest first. - Take the first object that passes all of these:
- It is not attached to a task (not held or carried).
- Its flags (vtable
+0x54) lack bit 26. - It is not claimed by A's own gang: object
+0xecis the gang and+0xf0the claim's end. - The caller's
filterobject 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. - For an AI A, no player stands within 1 m of it.
- With
needRay, a clear ray from A to it (Human_HasClearRayTo, mask 8). UnlessallowLow, the ray must not end in result 2: the ray crossed aGLASStriangle (material 2, an intact pane), which the ray's exclusion list lets through while still recording its material (GetItem below). Confirmed (code) at0x0024df40,0x00350778. - With
needReach, a navigation polygon under it (within the larger of the type's half-sizes+0x78,+0x7c) that A can reach (Nav_CanReach).
- 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:
+0x20the object's handle;+0x28the gait;+0x10a standing point beside the object;+0x24its type record;+0x2ctries;+0x30the saved turn boost;+0x31picked;+0x32the 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).
- The object is gone: done. Otherwise look at it (500 ms).
- The object's bit 26 is set: wait. A pick-up is in progress (held flag
0x4000): note it (+0x31) and wait. - 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_MayEngageallows: push EngageEnemy. - Else: done.
- Nobody holds the object.
- When it was picked (
+0x31): done. - While actions are queued, wait.
- Without the point, after a failed move (brain
+0x284, then cleared), or after 11 tries: done. - 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).
- 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, butCollisionMesh_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) at0x002dce84-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, scenariorepo: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 (hookglass-break), no owned object was taken (hooktake-owned) and no money or loot was added. Why the queued kind 0 broke nothing there is not known (an open question).
- When the ray result is not 2: queue a pick-up action (
- Without a clear ray: turn to it when more than 15° off, else count a try.
- Wait while A has any held flag but
- 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) at0x002dd018-0x002dd080.
- When it was picked (
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):
- Fight stance on; wait while actions run or a flag (other than
0x40000000) is held. - Every 15 updates: done (2) when a hostile is visible within the stop radius (
HandleList_FillVisible); done aftermaxThrowsthrows (0 = no limit). Every 45 updates he glances at his nearest enemy (2 s). - 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. - 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_DropEverything0x002f9a80) 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, state0x200000) 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_InvestigateCar0x002b0c88, 5 m, line 13), unless the offender is friendly; the Wander tactic'sTactic_VandalizeCars(0x00316590). Confirmed (code). - Trains. The train (
TrainRecord_Update0x00413d90) 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+0x20when the target is down), halved when the target's own target is this human or the brain is type 3.CfgAttackDelayis a table of ms indexed by attack kind (0x00228870reads0x006b6658 + kind × 4);config_preload2.luasets 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 by0x00510ad0/ the target brain's spacing byte+0x14a(or+0x14b, by0x00223b48) when that is not 0; the attacking gang'sCfgGangsets 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+0x14is 1, otherwise once the record's+0x08has none of0x5c0221f: 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+0x08has any of0x5c0221f.
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 (goal0x0b, EngageEnemy). - Commands seen:
0xd(grab, then a throw with a stick of 1.0),0xf,0x10,0x11,0x12,0xeand0x20(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→0x11or0x12), 12 afterSS2's, 13 afterX1's (0x10→0x12), inside the chain window asAttackKind_ChainDelayplans. +0x1e8was 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 for0xeand0x20: 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.
+0x200is a count of attack warnings since the last update: the default event handler (0x00292d80) adds one for each event0x10, as do0x002ff898,0x00302ff0and0x00304070, andBrain_Updateclears it (0x0028f914). Event0x10comes from0x0021d5c0, called byAttack_Start(call at0x0026267c) and the square path (0x00287594), each time to the attack's target alone (Combat: attack warnings), which it reaches only if0x002223e8passes: within the brain's range+0x130and its field of view+0x12c(half-angle, radians), or 2 m when the attacker's brain+0x2d4is set, and then a line of sight (0x00222288). Confirmed (code); the counting confirmed (runtime). - When (
Goal_TryBlock,0x0029f098): only when the brain's+0x200is not 0, that is when an attack on the human was announced this update. The chance isHuman_BlockChance(0x00223628: the power class's+0x08, or+0x0cwhile hurt, × the global0x00510ac8), a quarter of it for brain type 3. A player attacker whose pattern meets the class's+0x37threshold (0x002236c8,0x00418398on human+0x5d0) is always blocked. Yes pushes the block goal. - An AI human cannot block. Confirmed (code) at
0x0027c120and0x00147f98. The dispatcher starts a block only when R1 (mask 8) is held in a pad record (Combat):0x00147f98(record, mask)reads pad record0x005dd810 + pad × 0x50with 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 state0x8000or 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) at0x002b54d8,0x002b5520and0x002b5808.- Init (
0x002b54d8):+0x16= 1 (active);+0x10(the end time),+0x14,+0x15and+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+0xe0already had0x800. With a target:+0x15= 1 when the class's pattern threshold (0x002236c8) is above 0 and the target's pattern (0x00418398on human+0x5d0) meets it, and human+0xe0gets0x800unless+0x18. - Flag
0x800on human+0xe0means 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+0x08has none of0x5c7eae0.
- Init (
-
Process (
0x002b5808), each update, in order:- With a target while
+0x16= 1: when the target's record+0x08has0x400(0x00228560) or its state has any of0x7bf9e9f7ff0(0x00228228), write command 4 and clear+0x16. - While the brain has actions queued: return 0 (wait).
+0x16= 0: done (2), unless the human is still blocking or ducking (0x00223ad0), then wait.- The block time is over (now >
+0x10): when the target is still mid-attack (record+0x08any of0x5c0221f,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. - 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_PickAttackwith the filterHuman_CanStartAttack(0x00224778; the value0x00224778ffff0000is that member-function pointer, Picking the attack kind) (class0x6ainstead takes kind0x10or0x11, class0x59kind0x10), thenBrain_QueueAttackand+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 set0x800again unless+0x18). - Otherwise write command 4.
- In its last second (
So the counter is rolled on every update while the block time runs, from the goal's first update, and never once
+0x15is 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. - With a target while
-
Command 3 for an AI is a grab or tackle counter. Confirmed (code) at
0x0027c120and0x0027d6e0. The dispatcher acts on command 3 only for a human that is not pad-controlled (per-player+0x1b= 0), through0x0027d6e0, which refuses while the human's state has any of0x7bf9e9f4300, its record+0x08any of0x100101f, 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 76GRAB_FRONT_COUNTER; - the target plays 2-4 (
TACKLE_MISS,TACKLE_INTRO,TACKLE_PLAYER_INTRO;0x002590f8) → 9TACKLE_FRONT_COUNTER;
each as a paired move (
Attack_StartPaired,0x00262ac8, flag0x400000; Combat). Both tests also need: the human free (record+0x08none of0xfc7eaf7, state none of0x7bf9e9f7ff0), not hurt (0x00222ff8), the target not knocked down (+0xe00x80000), the target's own target this human,0x002672d00 both ways (inferred: face to face), a counter chance above 0, and, while the byte*(0x0051489c) + 0x56e3is 0, a brain of type 3 or a class whose+0x11bis 13 (0x00223e20). So an AI counters grabs and tackles, never strikes; a strike is answered only by the duck counter (Combat). - the target plays 69-71 (
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 event0x10and it never blocked. With+0x12cset to π, six events0x10arrived;Goal_TryBlocksaw+0x200= 2 once and pushed goal0x1b(+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 stayed0x3, 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):
- The target's own limits: at most 4 attack slots on a man, of which only as many as the attackers' gang allows
(
CfgGangvalues 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). - 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).
- 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).
- Help calls (event 20): a member hit or warned calls within 20 m, and only the idle answer (Helping a gang-mate, Help calls).
- 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
GangEnableAttackStrategiesis 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.
- 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. - The held flags (Tasks): without them an AI cannot know when its attack is over.
- The brain shell: type from the class's
behaviour, think one step in five (staggered by index), update every step, brains before the dispatcher. - 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.
- The fight goal and the attack action for
level99's fights: target, attack slots, the attack delay, the weighted pick from the class'sAtt_*table, move-to-human when out of reach,Brain_QueueAttack's chains, and the attack action's wait on0x5c0221f. The command is written once, in Start (not every update); the attack delay is halved when the target targets the attacker; the target's+0x1ectakes 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). - The block goal and the counter, driven by the attacker's warning:
+0x200counts events0x10, 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 flag0x800(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. - Reaction goals for the states a fight produces (stunned, knocked down, grabbed), one update after the state.
- Scripted goals for
level99, in this order; each sub-step is testable on a synthetic level:- 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
0x80detour, the route pool with its shortcuts and use counts. - 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 is0xff, and a leg of kind 8 or0x80is a climb through the player'sClimb_TryStart(link kinds):level99's Vermin crosses a fence that way. Steering round humans (Steering) is built; choke points and queues can follow later. 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.- The turn actions and
ActLookAt(Turning): heading to brain+0x110, done within 15° or after 3 s; the fourth argument is a start delay. - Lua callbacks scheduled 33 ms after a goal's End with (handle, completed)
(Scripted goals), then
GoalPlayDynAnimation(slot 668) andGoalAddressPersonwith its scene goal0x21. GoalFight,BrFlush,BrDead: flush ends goals (callbacks fire); dead clears actions only.- Gangs (Gangs): records, enemy and friend masks,
GangBrDead,GangSetThreatResponseon the current members,Gang_OnEventbeforeBrain_OnEvent, andGangSetMsgHandler's event 18 with the count still standing (level99ends its fights on it). - 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. - Tactics: the base (a tactic suppresses the fight goal), then
TacticCrowdandTacticTrigger(Tactics);GoalDealer(GoalDealer) forlevel99_lesson2.
- 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
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 onCombatEnemy,CombatEnemy2andCombatTeacher(and 2 onCombatEnemy2when its fight starts). TheirGoalFighttherefore runs in full:Brain_Fightadds the player as an enemy, takes him as the target andBrain_PushFightGoalpushes 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 thewarriors_*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, thewarriors_passivestate with each Warrior's goal stack, position and+0x140read 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
+0x1acand clip read every update; EngageEnemy step 9 confirmed (code) at0x002afa48). 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 becomes0x180000, inside the busy mask0x7bf9e9f7ff0that 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
0x0028d2e8and0x0028d190: a threat response of 0 stopsGoalFightcompletely (Brain_Fightreturns 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 scriptedGoalFightnever 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 scenel99_c7), inferred from the disassembly oflevel99_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 tofRudyStand, whose message 8 (P1.RudyAtFence) plays his drink and a line; then for eachCombatEnemy2memberHuSetNoTarget(false)andGoalTrackHuman(member, player, 0.5); thenEnableAllButtons(),EnableCommand(player, 1, 0)andEnableCommand(player, 40, 0), the lesson's text, andP1.StartSnaps5 s later. NoGangBrDead(false),GangSetThreatResponseorGoalFighttouches the gang, so its members stay dead to the AI with threat response 0 (as set up, the cast) and never attack;GoalTrackHumanstill runs. - Confirmed (runtime), PCSX2 2.9.94,
level99checkpoint 1 withP1.PowerMovesDonescheduled from lesson 2 (call hook): during the scene the four holdJoinCinematic(0x29); when it ends each has TrackHuman (0x30) on top, brain+0x09still 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):
- Call
0x0041abc0(1)(SetCheckPoint's worker), then0x00160d78(name)(MenuLoadLevel's worker) with"level99"written to free memory: the level reloads at checkpoint 1, and its tutorial starts about 60 s later. - Schedule script functions by name with
0x003863d8(name, delayMs)(ScheduleFunc's worker), the name in free memory:AddFenceWarriors1; thenGangMakeFriends's worker0x0016ad80(0, fenceGang)(the fence gang's id, 5 here, is brain+0x20c's); thenP1.SetupWarriors. - 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 stub0x004edd40) with one goal, JoinCinematic (0x29), whose actions are a move (vtable0x00542f20,MoveAction_Init's) and then a turn (vtable0x005430e0, the oneAction_TurnToTargetmakes). - Ash (type 3) is suspended (
+0x0a= 1) but not dead, holding one goal, HoldPosition (0x36). GenWarriorand 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, init0x00313490); 1GenWarrior, 2 the second wave (both kind 19, enemy 0); 3 the teachers and 4 the threeCombatWarriors(kind 19, no masks yet:GangMakeEnemiescomes 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, …)from0x0041c4e0, whichHuman_MakePlayer(0x00229c40),Human_SetWarChiefandGang_OnEventcall: the player gang's standing tactic, and the source of Ash's HoldPosition (0x00313718pushes 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 feedPlayer::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.ReachCenterpushesGoalAddressPerson), which must stay on the stack. - Fighting:
FightGoalin 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;AttackActionpresses the command once in Start (the delay halved when the target targets the attacker or the brain is type 3, then a wait on0x5c0221f), with a full stick along its angle when it has one, and, when neither human is busy, moves the target's+0x1econ by the kind's swing time over his spacing.MoveToHumanActionsets a heading and speed in the record'smove(no stick). - The attack choice (
repo:src/ai/attack_choice.h,repo:src/ai/attack_views.h):Human_CanUseAttackKind's table andHuman_CanStartAttack's guard as pure tests over two views of the humans, andBrain_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).attackerViewOfandtargetViewOffill 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 ofrepo: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'sCfgGangvalues 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_CheckAttackanswers 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 goal0x35are 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 andHuman_CanSeeHuman(range, the shadow's rule, then the line); the brains get the mesh fromBrains::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_ScoreEnemywith every term and the weights ofCfgSetTargetingPoints/CfgSetTargetingPointsEx(read from the scripts' calls), andBrain_PickBestEnemywith 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, bit0x10); after the level's stepai::updateHidingruns 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), andHuman_CanSeeHumansees 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):scanEnemieslists 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 event0xb(its script's handler, else its type's; a tactic throughBrain::addEnemy).ScanScheduleruns 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:
BlockGoalnever 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, bit0x800) 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, thencounterTest: 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) + 0x56e3taken 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'sCfgChar, power class 40'sCfgPowerClass,CfgAttackDelayandCfgBaseChanceToBlockfrom 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, theconfig_preload2delays,Att_Normal. - Play:
fighterlines 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'sBrainsandScriptedBrainsbefore 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;releasethen makes eachHuCreated human (player 1 bound to the player's brain, the others AI humans of the classaiConfigFromgives their type, drawn with their own model,repo:src/platform/play_level_cast.cpp) and replays the held calls in order. A laterHuCreateof player 1 is an AI human untilHuChangePlayerGanghands player 1 over (Characters), andGangDeletetakes every member but player 1 out of the world at once (Deleting a gang). A teleport moves an AI human;GetPositionand 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 with0x8cand 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 fromBrains::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::updateclears the move first (Brain::stopMove, asBrain_StopMovestartsBrain_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 runsHuman::aiCombatWalk(An AI's locomotion), and so does a player whose pad a script took (per-player+0x1bclear, 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, asMoveMeleedoes. 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 thelevel5bosses 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 mask0xff; a leg of kind 8 or0x80is 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,steerAroundHumansfinds 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 isMove_CornerSpeedLimitas 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), andSteering_TryDetourrefuses a point whose first ground within 2.1 m below is a triangle with flag0x10(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 inrepo: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) andgiveWayTo(an AI's or a "dead" brain, no threat either way, the stander idle under his control); the first free sector gets aGiveWayActionon 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.TakeStepActionstops 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) holding0x80000and turns him to its end facing over half of it; the action ends with the clip. The script'sActGiveWayasks a human with fewer than 8 actions to give way to the other along his facing. Callers so far:ActGiveWayonly. 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 bits0x180f3ff0. 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+0x2e5mode in the threat test; brain flag+0xccbit 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 throughFlagServices),TurnAction(look-at, to a point, to a heading; 15°, 3 s),PlayDynAnimationGoalwithPlayAnimAction(slot 668),AddressPersonGoalwithPlayAnimationGoal(its scene),TrackHumanGoalandDealerGoal. A goal's callback is scheduled 33 ms after its End with (handle, completed) (scheduleGoalCallback).Brain::flushends the goals (their callbacks fire) and clears the actions;Brain::setDeadclears 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,GangCreateinrepo:src/scripting/script_bindings.cpp) hand their calls to the binding context's AI host.ScriptedBrainsis that host and the goals'ScriptServices: it names brains by handle (bind, withHuCreate's gang), finds flags and look-at targets, and runs the callbacks, message handlers and tactic callbacks in aScriptSystem. 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, inBrains): 32 records with kind, name, enemy and friend masks, members (10, 16 for the police kinds), suspension, message handlers and a tactic;Gangs::friendsandenemies; the whole-gang switches (GangBrDead,GangBrFlush,GangSetThreatResponseon the current members,GangSuspend). An event goes to the human's gang before its brain (deliverEvent): the handler for 18, 2 and0x11with (member, other, value), any other id used when the call returns true, then the tactic.Brains::updatesends event 18 once when a member's health runs out, solevel99'sP1.BumDiedgets 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 fromGoalFight.TacticCrowdseats 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, onTacticTriggerand 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 isBrain_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 event0x12reaches 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):TacticBossScenarioAgives class 120 aBigBrawlerGoal, class 119 aBigThrowerGoalin stage 2 and aBigBrawlerGoalin stage 3, the othersStationaryThrowerGoal; 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 pushTiredGoal, 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 oflevel99'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 oflevel80,level87andlevel99, the search finds every one butlevel80'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:
- Scatter and wreck do nothing (the crew freezes): build
WarriorScatterTacticwith theScattergoal andWarriorStealTacticwithWarriorVandalSteal(Warrior commands, steal); add the steal automatic command and its return to follow. - 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
sayCommandservice already takes volume and interrupt. Add the attack and defend gestures (676 / 680, 675 / 679). - Defend: replace
GoalTrackHumanwithTactic_Defendround the chief andFollowAndDefend, so the crew fights for him and (withSaveHuman) frees or revives him. - 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.
- Revive (item 8): built for player 1 (Combat); the crew's flag (
Human_Init's) and the AI rescue remain. - 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
cheer2taunts 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) inAmbient_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 event0xf(the tag-spot message); what reads the tackle meter brain+0x148besides the fight goal, and what brain+0x28fcontrols. - 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; whatGoalBackoff,GoalMoveToUseFlagand the pedestrian goal (0x69, with its two variants andFlagNetTraverse's flags) do each update (Process), and the use-flag goal's two floats; whatGangInvinciblesets on a member (0x0016a000). -
What sets polygon
+0x02in the level files (non-zero for most areas with route nodes, the link kinds), and the goals that search with the mask0x13(0x002aafd0,0x002c1470). Whether the level-table ids0xe,0x33,0x34and0x52arelevel14,level51,level52andlevel82(follow). - Which record
+0x08bits the play-anim action's clip holds (0x0025a3e0), its rate for a dynamic clip and its blend out; Coney holds0x80000. - 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, andGoalFollowPlayer'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_pushclip does to the buyer (Buying). - Who sends a gang's event 18 (a member down or dead), and with which attacker:
Gang_OnEventreads it, but its sender was not found (0x0022dd98and0x0022e020are the arrest's). - What a scene's end passes to the callback
Goal_PlayAnimationhands 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+0x333is the detail level from the camera distance (Characters). - The hold's damage (
0x00510acc) ofGoalGrabTargetand the boxing attack weights (0x00511120) ofGoalBoxer. - 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
+0x11bvalue 13 is (Combat), and what the byte*(0x0051489c) + 0x56e3that lets every AI counter is. - What stopped Vermin's route jump at the
level99window (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 byRunCarrotGoal_Init0x002e1230, AI goals); Coney does not model it.