Gang fights: how a gang fights together¶
Verified against: SLUS_212.15 (NTSC-U, SHA1 e9cb2cc49aa046b9e494313dce2f5038ed17b2f4), read in Ghidra on 2026-10-12;
checked at runtime on Rumble's 9-a-side brawl in PCSX2 2.9.94 on 2026-10-12 and 2026-10-13 (Runtime checks).
Confirmed (code) at the addresses cited unless marked; the per-gang values are confirmed (disc) from
config_preload2.lua through Gangs.
Purpose¶
How the members of a gang share out a big fight: who goes for whom, how many may hit one man at once, what the rest do while they wait, when they switch, how they help each other and what the gang as a whole adds. There is no fight coordinator: no object owns a brawl and hands out targets. The coordination comes from five mechanisms that each brain meets on its own: the target's own limits (attack slots and active places), a shared score for choosing among enemies, help calls (event 20), the gang's tactic (whose Process and events push goals on the members) and, under TacticAttack, short team-up orders from the leader. The single fighter's side (the fight goal, the attack check, the reposition, the run-in) is on AI humans and only linked here.
Original structure¶
No object of its own: the mechanisms below live in the brains, the gang record and its tactic (AI code index); each section cites its functions.
Data¶
The records involved are the brain and the gang; each rule cites the offsets it reads.
Behaviour¶
The rules follow, one section per mechanism.
Who goes for whom¶
- Each member keeps his own enemy list (brain
+0x164, 16). It fills from the periodic scan (The enemy scan), from being hit or warned (the attacker is added both ways byBrain_AddEnemy0x0028d538), from help calls and from the tactic's events. Nothing copies one member's list to another, except the shared target (rule 4). - The choice is a score (Choosing among enemies): Melee re-picks with
Brain_PickBestEnemy(0x0029f230, flags0xfffffffd) every time a fight goal ends. The terms that make a gang spread are the distance (4 points a metre nearer), "he targets me" (+4), "he is armed and targets me" (+3) and above all −999 when his attack slots are full and the scorer is not nearer than a holder (Brain_CanTakeSlotOn0x0028dc80): a fifth man cannot choose a target four already hold. The terms that make it focus are the player (+20), the enemy leader (+6 for a scorer that is not a Warrior, flag0x4), the previous target (+3, the default adjustment0x0029f3a8) and the gang's shared target (+400). Grabbed (+1), grabbed from the rear (+6), down (+1) and stunned (+1) draw mates to a man who is already held or hurt. - The crew under the attack command (Warriors, and Rumble's side 1, below) runs
FollowAndAttack, which scores with flags
0xfffffbf9(no "near the leader", "enemy leads his gang" or "running" terms) and drops a winner more than 60 m from the chief (FollowAndAttackGoal_Process0x002bc198: squared distance chief to enemy over 3600 clears the target). Its fight goals last 4000 ms. - The shared target (gang
+0x10) has one writer besides the reset: the Warrior-command dispatcher (WarriorCommand_Dispatch0x0041c4e0). Every command first clears it; an unforced attack command (the player's menu) then sets it to the chief's lock-on target when he is locked on (Human_IsLocked) and that target is an AI (pad+0x1b0= −1) and not friendly, else to the human held in game state+0x280or, failing that,+0x284, when he resolves and is not friendly (these two are not tested for being an AI): +0x280is the crime reporter, the one human at a time phoning the police or calling a gang over a crime (GameState_SetCrimeReporter0x0041cfe8; Crimes lists it withGameState_CanReportCrime). A civilian sets himself there when he sees a crime and pushes CallPolice or CallGang (CivilianBrain_OnCrimeSeen0x002ff660,0x002ff72c; alsoCivilianBrain_OnEventandPedReactionGoal_Process),CallPoliceGoal_Start(0x002a7ed0) sets him andCallPoliceGoal_End(0x002a7f94) clears it.+0x284is the human calling his gang for backup or running off with the player's money, one at a time (GameState_SetDealerCustomer0x0041d080, set only whileGameState_IsDealerFree0x0041d088finds it empty): a scout who has spotted an enemy and pushes CallGang (ScoutTactic_OnEvent0x0031ab28, thenGoal_CallGang), or a dirty dealer who has just ripped off the buyer and runs off (DealerGoal_OnBuy0x002c7910, Buying rule 3).CallGangGoal_Destroy(0x002d606c) andDealerGoal_End(0x002c70fc, when it is him) clear it.
Both are set to the null handle (0x006ebd30) by the level's reset (GameState_ResetForLevel, 0x00418d0c and
0x00418d24) and first written at construction. So with no lock-on, the attack command sends the crew after
the snitch (the civilian phoning the police), else after the scout calling his gang or the dealer who cheated
the player. Confirmed (code) at the writers cited, each of which passes the brain's own human;
GameState_SetCrimeReporter and GameState_SetDealerCustomer are their only writers apart from
GameState_Construct (0x00418708, every sw to +0x280 and the setter's callers checked).
Gang_SetSharedTarget (0x00165b70) accepts the target only when it passes Brain_ValidateEnemy for the
gang's leader and is not of a friendly gang, and then adds it to every AI member's enemy list. With +400 in
the score (and the scan always re-adding it) the whole crew converges on him. A forced command
(WCIssueCommand(..., true), as Rumble uses) never sets it. No other gang sets one.
How many hit one man at once¶
Two limits on the target, both described in Attack slots, active places and spacing:
- Attack slots: at most
+0x1e4(4 by default) humans may target him. A nearer newcomer, or one forced in (the fight goal's re-target, rule 2 of the switches), takes the slot of the farthest holder and clears that holder's target, whose fight goal then ends at its step 2 and whose Melee picks again. - Active places: of the slot holders, only
smay be in a swing at once,sbeing the spacing byte the attackers' gangs raised:CfgGangvalue 2 for a standing man, value 3 for a downed one (the largest over every gang that has claimed a slot on him since his list was last empty). A Warrior target (brain type 3) keeps 1 standing; the player (type 0) does not. The values per gang type (Gangs, types asGangCreate's first argument):
| Gang type | Strategy | Standing (v2) | Downed (v3) |
|---|---|---|---|
| 0 (Warriors), 2, 4, 5, 6, 9-12, 14-20, 22-24 | various | 2 | 2 |
| 1 (cops) | StratNormal | 3 | 3 |
3 (Gang1, Rumble side 1) |
StratOrphans | 1 | 4 |
| 7 (Turnbulls), 21 (Huns) | StratTurnbulls, StratHuns | 4 | 4 |
| 8 (Furies) | StratFuries | 2 | 1 |
| 13 (Hurricanes) | StratHurricanes | 1 | 1 |
Rumble side 2 (Gang2, type 19) is in the first row. So two soldiers of an
ordinary gang swing at the player together, a Turnbull pack four.
3. The gap between swings at one man (+0x1ec) is one swing's length divided by s, shared by every holder: a
holder whose turn has not come waits even with a place free (Brain_ClaimActiveAttacker 0x00291008 needs the
gap passed, or a place already held by someone).
What the waiting ones do¶
A slot holder without a place, or whose own next-attack time (+0x1e8) has not come, gets a wait reason from
Brain_CheckAttack and repositions:
- They circle in a band of 4 to 4.75 m round the man (move-to-human, 2000 ms), the one he is fighting (his own
target, or the first slot holder when he has none) staying close (inner radius 2 × his capsule radius). Members
under a Defend tactic (or tactic type
0xd) stay close too. - They taunt: once every random 1000-2000 ms, when at least 2.75 m away, fewer than two men hold slots on the
waiter, and the target is empty-handed (11 % for reason 7), a fidget on anim
0x25bwith line0x11. - Some reasons hold a waiter back: 4, an empty-handed waiter does not walk into an armed man near his own leader; 6, a thrower does not throw through a mate in a 40° cone; 8, an empty-handed man lets a slot holder with a heavy object go first; 5, everyone but a cop waits 15-16 m off while the police have the man.
- On the run-in (EngageEnemy) attackers fan out: the aim point is turned 9° per attack-slot index, alternating sides, when the target faces away (EngageEnemy step 12).
- Event
0x15makes every slot holder whose top goal is Fight taunt (kind 4 at his own target, kind 3 otherwise when he is beyond the middle of his melee ranges, his next attack delayed by twice the taunt): The shared event handler. It is sent to a man when he is put on the ground, so his attackers gloat over him. Two senders, each writing id0x15at record+0x20and the downed man at+0x24, and passing the record to his own human's event slot (vtable0x0053f088+0x44,Human_OnEvent0x0021d480). That offers the event first to the man's own script handlers (human+0xe8,0x00384c38), which can consume it, and only then to the gang'sGang_OnEvent0x00164c20andBrain_OnEvent(Gang events, Messages and events); so a script handler on the downed man that consumes event0x15stops the taunts (inferred from that order): HitReact_OnSpecialEnd(0x0026a1f8), the end callback of the knock-down clip thatHuman_PlayReaction(0x0026a6d0, its knock-down branch, afterHuman_KnockDown) andHitReact_InSpecial(0x0026860c) chain on a man knocked down by a hit;Throw_ClearVictimLink(0x0022bdc0, the store at0x0022becc), when a throw, a grab or a mount lets go of its victim and puts him down (Human_GoDown, when he is not knocked out, wounded or dying and the clip's knock-down value says so; its callers areThrow_ClearLink,Human_BreakPair,Pair_OnVictimClipEnd, the grab and mount reversal and throw ends).
Confirmed (code). No other human or AI code loads 0x15 as an event id (the 164 li ..., 0x15 of the executable
and the 86 delay-slot _li ..., 0x15 matches, some of them 0x15x, checked; the rest are anim ids, lines,
sizes, call arguments and the world objects' own message 0x15, which is not a brain event).
Target switches and what interrupts a fight¶
- The fight goal ends (and Melee re-scores, +3 for the previous target) when the target fails
Brain_ValidateEnemy, when he no longer holds a slot on him (displaced, limits rule 1), when the target is beyond 5.5 m (unless he holds a throwable), every 30 updates when no straight walkable line is left, and at its deadline: 4000 ms for every fight goal Melee pushes (The durations), unless at that moment he still holds an active place on an attackable target and has no FollowAndDefend goal. So in a long brawl every member re-chooses at least every 4 s. - The re-target (
FightGoal_Process0x002b3ab0, at most once a second,+0x14): the first word of his sector record, refreshed when older than 1 s, which is the nearest human within 1.5 m in sector 0 (straight ahead, ±22.5°). When that human is someone other than the target, a threat (Brain_IsThreat0x00290138: an enemy gang, or both players) and no GrabTarget goal (0x1f) is on the stack, he becomes the target with the slot forced (Brain_SetTarget(brain, him, 1)). Confirmed (code); this replaces the "inferred" reading under Starting a fight. The new target is not checked against the enemy list orBrain_ValidateEnemythere: an enemy who steps in front of a fighter takes him over, and the next update's step 2 ends the goal if he is not valid. - EngageEnemy re-targets every 2 s to the nearest enemy he can see when the current one is beyond 5 m.
- Interruptions within a fight: a block (
Goal_TryBlockon an attack warning, Blocking) gives up the active place; a reaction goal (hit, grabbed, tackled, down, stunned) runs above the stack and the fight goal resumes when it ends; a knock-out ends every goal and releases his slot and place on his target (Brain_OnKnockedOut0x0028c3b0); a tactic's order pops the member to his goal base and gives new goals (the 3 s idle check, a team-up order below, the attack tactic's events 19 and 22).
Helping a gang-mate¶
- Being hit or warned spreads the fight. A member warned of an attack (event
0x10) or hit (event 1) sends a help call within 20 m (Help calls, The shared event handler); a busy gang member hit by a player calls within 30 m. Every hearer with no target, no held flags and free actions picks the side (Brain_PickSideInFight0x002912b0: his friend's attacker), starts a fight on him and pushes HelpRespond, which walks him over until he lists an enemy. A member who already has a target ignores the call: help comes from the idle. - A grabbed member does not call for help (only a civilian in the Grabbed reaction does, every 30 updates within 10 m, The fight reaction goals). Mates are drawn by the score instead: a rear-grabbed enemy is +6 and +1 grabbed. The other way round, a member holding an enemy from behind presents him to a nearby friendly player (Grabbing), and an AI mate may start a tandem on a held enemy (Combat moves: tandem, issue #84).
- A downed or cuffed mate is rescued only by a member running FollowAndDefend (the Defend tactic, or the
Warriors' defend command), which pushes SaveHuman on a cuffed or knocked-out chief;
the Defend tactic also sends its members to the defended human on events 17 and 18. No rule was found that makes a
plain gang soldier protect a downed mate (inferred from the callers of
Goal_SaveHuman0x002b7d80). A downed man himself, still attacked, plays the get-up attack after about 1.9 s (Grounded). - Under TacticAttack, idle members are sent to the fight every 3 s (
AttackTactic_Process0x003081a8): each member with an empty goal stack is popped to his base and given Melee, and then a HelpRespond goal toward the farthest member that has goals (Gang_SendHelper0x002b75b8). Confirmed (code): the loop keeps a candidate only when his squared distance is greater than the best so far, so this corrects "the nearest" in The scripted tactics. On violence nearby (event 20,AttackTactic_OnViolence0x00307878) the leader picks the side and every member with an empty stack melees that man, takes him as enemy and target and gets HelpRespond toward him.
The gang as a whole¶
- Alert state (gang
+0x32,Gang_SetAlertState0x00164a28): 1 while fighting; it installs the gang's fight anim substitutions and sets each AI member's brain+0x14dto 0 (calm 1), which lifts the turf limit on whom he may take (The gang). Melee sets it when the gang has no tactic; a tactic's Start sets it for types below0x12. - The leader (gang
+0x44,Gang_GetLeader0x00165678: while alive, not a player, not down and not out of the fight, else another member). He counts in the score (+6 against an enemy leader), in reason 4, inAttackTactic_OnViolence(he picks the side) and he gives the team-up orders. - Flight (
GangTactic_CheckFlee0x00307d40, on event 18 under TacticAttack):AttackTactic_Start(0x00307fe0) records the gang's living count at its start in+0xe0(whenGangCanFlee+0xdfis set, 1 after a reset, and+0xe0is still −1). On each member going down, when the standing, uncuffed members number at most round(CfgGangv9 × that count / 100), each standing member whose class byte+0x11bis 11 gets threat response 0, Melee and a PedReaction (type 9) away from his nearest enemy, and the tactic's fled flag (+0x32) is set (no moreTacMemberDied, no more team-ups). v9 is 50 for gang type 3 only and 0 for the rest, so in practice half of Rumble side 1's starting members down makes its class-11 members run. This corrects AI code, which read+0xe0as a percentage. - Team-ups (TacticAttack's coordinated sub-tactics, AI code: attack sub-tactics).
AttackTactic_StartSubTactic(0x00307a10) runs 7 s after the tactic is free (no sub-tactic running, gang not fled): it needsGangEnableAttackStrategies(gang+0xd9, 0 after a reset,Gang_Reset0x001649c0), a leader who is not busy and at least two living members. It countsE, the living members of the (at most two) nearest enemy gangs within the leader's sight range (Gangs_FindWithinRadiuswithGang_CanJoinFightAgainst), andL, his own living members, then draws one of seven orders by the weights of the gang type's strategy table (CfgGang's sixth argument, bytes+0x73-+0x79,GangConfig_GetStrategyEntry0x00164610):
| Draw | Order | Members | What happens |
|---|---|---|---|
| 0 | Charge (10 s) | max(L / 2, L − E) | the leader says attack and gestures; free members drop their fight and run in on their nearest enemy (EngageEnemy) |
| 1 | Guard (15 s) | the rest | the leader says defend; members form round him (FollowAndDefend, 3 m) with the attack-weight table 0x00511438; at the end he says attack |
| 2 | Guard, leader backs off (15 s) | the rest | as 1, but the leader says follow and backs off (FightBackOff) and the members keep their own weights |
| 3 | Throw from a pile (20 s) | max(L / 2, L − E) | the leader says throw; members fetch from dyn_pile objects within 30 m |
| 4 | Get their leader (15 s) | L − 1 | the leader says attack; members with an enemy leader in their list run at him; for its length the gang type's standing cap (v2) is 4 and the enemy-leader score term is 18 for every scorer (the global 0x00510b54, read as an integer at 0x0029d054) |
| 5 | Throw objects (20 s) | every living member | the leader says throw; members fetch throwable objects (kinds 4, 5) within 30 m |
| 6 | Grab (no limit) | the leader | the leader grabs the first living class-221 human (GrabTarget) |
Weights by strategy (draws 0-6): StratNormal 20, 20, 20, 20, 20, 20, 0; StratOrphans 0, 0, 0, 5, 0, 5, 0;
StratLizzies 0, 20, 20, 0, 0, 0, 0; StratPunks 0, 0, 0, 0, 0, 0, 20; StratTurnbulls 20, 0, 0, 20, 20, 20, 0;
StratFuries 12, 0, 10, 0, 20, 0, 0; StratHurricanes 40, 0, 10, 0, 0, 0, 0; StratBoppers 0, 20, 20, 5, 10, 10, 0;
StratHiHats 5, 5, 40, 10, 10, 5, 0; StratHuns 0, 10, 15, 0, 20, 0, 0. When the order ends, its End restores what it
changed and, when it says so (AttackTactic_OnStrategyEnd 0x00308150), the members get Melee again.
The player's presence¶
- He is the favourite target: +20 in every score, and +4 more when he targets the scorer.
- He is hit by more at once than a Warrior is: the player's brain is type 0, so the attackers' gang raises his standing spacing to its v2 (2 for most gangs), while an AI Warrior (type 3) keeps 1.
- A member busy when hit by a player calls within 30 m instead of 20 m, and turns his head to him.
- Warriors (type 3) list only threats (The enemy scan); they hold back from a boss a player holds
(reason 7); the AI learns to block a player's (or Warrior's) repeated pattern (
Brain_TryPatternBlock0x00292158, Blocking). - His crew follows his commands: the attack command shares his target (rule 4 of targets), defend makes them guard and rescue him, and a crew member holding an enemy presents him to the player.
Rumble's CPU-controlled sides¶
From Rumble (brawl, one-player and co-op): side 2 (GangCreate(19, "Gang2")) gets
TacticConfront(gang2, gang1, 10, 2, 0, -1, "AIConfrontCallback"), whose callback switches to TacticAttack(gang2)
on results 1, 5 or 6. Under TacticAttack:
- Start (
0x00307fe0): every standing member without a PedReaction goal gets threat response 2, is popped to his base and gets Melee with no time limit (AttackTactic_GiveMelee0x00307788); the gang saysspot; the flight count is noted (gang type 19 has v9 0, so side 2 never flees).AttackTactic_ComputeSpacing(0x00307668) stores an interval (halfwords+0x30= 1000 ms, or 50 ms when the first member's nearest enemy runs, and+0x2c= that divided by the living members;+0x2c= 300 with no enemy) that nothing else reads: apart from ComputeSpacing reading its own+0x30back to divide it (lhuat0x00307710), no halfword load of+0x2cor+0x30anywhere in the executable is on a tactic (everylh,lhuand delay-slot_lh,_lhuat those offsets checked;0x0030c5a0is a Luther goal's byte at+0x30; the word loads at+0x2cin the tactic code,0x00306600-0x00308400, are of vtables, brains and gangs). Confirmed (code) by that search; so it is write-only and Coney needs no behaviour from it. - Each second with no member listing an enemy, code 9 (
TacNoEnemies). - Every 3 s, idle members melee and go toward the farthest busy mate (helping rule 4).
- Events: 1 and 11 (a member hit or spotting someone) give Melee to a member with no fight goal while no team-up
runs; 2 (a member died) fires
TacMemberDiedunless fled; 18 runs the flight check; 19 and 22 give everyone Melee again; 20 runsAttackTactic_OnViolence. - Team-ups only if the gang's scripts enable them (gang
+0xd9). Rumble's two sides never get them. The flag has two writers,Gang_Reset(0x00164808, 0, the store at0x001649c0) andGang_EnableAttackStrategiesById(0x0016a2a8, store at0x0016a2d0), whose only caller is the script bindingGangEnableAttackStrategies(0x00373308) (confirmed (code), everysbto+0xd9). In the scripts it is set in two ways: GangSetup, the strategy library's gang constructor inglobal.lua, callsGangEnableAttackStrategies(gang, true)for every gang it makes unless the gang's table setsAttackStratsOff(The strategy library, read from the compiled scripts; the call, withtrue, is inGangSetup's code). So in the story every gang made throughGangSetupgets the team-ups unless it opts out; 11 chunks besidesglobal.luanameAttackStratsOff.- Directly, in three story chunks only:
level2_clinic.luaandlevel55_punkfight.luaturn them on for some gangs,level93_chapter3.luaoff for four of its fights.
That covers every compiled chunk on the disc: of all 467 (the 356 with a recovered name and the 111 without,
so also any flag chunk level<n>_<type>_init.lua of the four arenas whose mode script names are not recovered),
only global.lua and those three name GangEnableAttackStrategies (a string search of each chunk's bytes,
2026-10-13). No Rumble chunk names it: not the rules chunks (brawl.lua, kinghill.lua, ...), not the arena
scripts level101.lua-level137.lua, not the 56 named flag chunks. Rumble makes its two sides with
GangCreate directly (AddRumbleGang1 and 2 in the arena scripts; Rumble: the sides),
not through GangSetup (inferred from the chunks' calls).
The one Rumble gang made through GangSetup is level 105's Red Devil band (brawl.lua's AddRedDevilBand, no
AttackStratsOff), so its flag is set; but it is set dressing that never fights: halted, threat, damage and
investigate responses 0, no reaction to violence, and its hit handler only changes its music and animations
(inferred from the chunk's calls).
At runtime +0xd9 stayed 0 for both sides in every recorded match and no order ran (Runtime
checks), confirmed (runtime).
Side 1's AI members (with the player as chief) get the forced attack command, WCIssueCommand(player, 1, true):
FollowAndAttack on the player, best enemy within 60 m of him, and no shared target. Their gang type 3 lets one of
them swing at a standing man at a time (v2 1) and four at a downed one (v3 4).
Runtime checks¶
Confirmed (runtime), PCSX2 2.9.94: eight recordings of Rumble's gang battle, every
human on every update, five with the Furies as side 1 (2026-10-12 and 2026-10-13) and three with the sides swapped
(The sides swapped); the five of 2026-10-13 also ran the hooks of
repo:research/traces/patches/gang-battle.toml. Counts are over the fight (the brains going on to RumbleOver), per
minute that a fighter stands. The numbers per match are on Rumble.
- Each side's tactic. Side 1 runs the attack command's tactic (
WarriorAttackTactic, vtable0x00544160) for the whole match, with FollowAndAttack (52) at the bottom of every member's stack; so side 1 never meets TacticAttack's idle check, events, flight or team-ups. Side 2 runs TacticConfront (vtable0x00543740) for its first 23 to 47 updates, then TacticAttack (0x00543320), with FindEnemy (65) at the bottom, Melee (8) above it and Fight (15) on top. The alert state (+0x32) is 1 from the brains going on (0 under TacticConfront). - No shared target and no team-ups. The shared target (
+0x10) stayed empty andGangEnableAttackStrategies(+0xd9) 0 on both sides in every match, and no team-up order ran. The leader (+0x44) stays one fighter until he goes down, then passes to another (side 1 starts withP11). - The limits. At most 4 of a side target one man, as the 4 attack slots allow. The spacing bytes on a man are
those of the gang type attacking him: on side 2's fighters (attacked by type 3) 1 standing and 4 downed, on side 1's
(attacked by type 19) 2 standing and 1 or 2 downed (the downed byte is raised only by a slot taken while he is
down). The active places held on a man stay within his byte, except in 0 to 2 % of the samples where places are
held, nearly all within 10 s after he got up (inferred: places taken while he was down, under the downed byte,
stay until their holders let go;
Brain_ClaimActiveAttacker0x00291008only looks at the firstsplaces). - Fight goals last 4 s. Every fight goal starts with 4000 ms left (a few with 500 to 2500 ms), and 24 to 74 % of them run to 4 s or longer (kept while an active place is held; the longest 41 s), so a member re-chooses every few seconds (Target switches rule 1).
- Where the waiting ones stand. A fighter with Fight on top and no active place stands a median 0.9 to 1.1 m from
a target who is fighting him (1 to 5 % of that time in the 4 to 4.75 m band), and 1.5 to 3.5 m from a target
fighting someone else (6 to 25 % in the band, 29 to 67 % beyond 2.5 m). The band is where a reposition sends a
waiter, not where waiters spend their time (What the waiting ones do). Taunts (clip
0x25b) come 0 to 1.9 times a standing minute. - What changes a target (the
set-targethook onBrain_SetTarget0x0028cfe0, by its caller; five matches, per standing minute): the enemy pick (Brain_PickBestEnemy, Melee's on side 2 and FollowAndAttack's on side 1, inferred from the goals each side runs: the hook cannot tell the two callers apart) sets one 13 to 31 times and clears one 7 to 28 times (it clears, then sets: a change of enemy shows as a clear and a set in one update); the fight goal's forced re-target (FightGoal_Process, rule 2 of the switches) switches 2 to 17 times; hitting back at the man who hit him (GangBrain_OnEvent, event 1, forced) 1.0 to 4.7, which side 1 does throughBrain_Fightand side 2 directly; clears byHuman_DropTarget, the Stunned goal and a slot displacement (Brain_ClaimAttackSlot) 0 to 7.5. Calls that set the same target again (most of them, throughHuman_SetTarget0x00226c30) change nothing. Every return address the recordings logged, the other call sites and each cause: What each caller means. An earlier reading put side 1'sBrain_Fightswitches on its FollowAndAttack goal; the hook's second address shows they are all the hit-back. - Help. Help calls (the
help-callhook onGang_BroadcastHelpCall0x00293640) come from three sites ofBrain_DefaultOnEvent0x00292d80, which the hook tells apart by return address: on a hit (event 1, call at0x002930d0, returning to0x002930d8, with an amount ina3), on an attack warning (event0x10, call0x00292f00, return0x00292f08) and on a new enemy (event0xb, call0x002934f4, return0x002934fc: unless that enemy is throwing at him,Human_IsThrowingAt0x002281d8, a 20 m call with the new-enemy flagt0= 1 anda2,a3zero; confirmed (code), the shared handler item 3). Event0xbcomes from the enemy scan whenBrain_AddEnemylists an enemy for the first time, and is rare once both sides are engaged (inferred): 0.1 to 1.3 a standing minute a side (confirmed (runtime)). Per standing minute, five matches (three swapped, two not): hit and warning, the Orphans 41 to 70 and the Furies 12 to 37 (the Orphans are hit more); all three sites, the Orphans 41 to 71 and the Furies 13 to 38. Rumble's comparison rule 11 counts all three. Every helper sent (thesend-helperhook onGang_SendHelper0x002b75b8, 0.1 to 1.6 a minute) was sent by a hearer answering a call (Brain_OnViolenceSeen0x00291960); TacticAttack's 3 s idle check never sent one, because side 2's fighters never had an empty goal stack (0 to 0.5 % of the time; side 1's 0.4 to 12.6 %). - Side and gang (the swap): the Furies won all eight matches, as side 1 and as side 2, and the Blocking goal and few reactions go with the Furies, while side 2 attacks more often than side 1 whichever gang it is (Rumble: the sides swapped).
What each set-target caller means¶
Brain_SetTarget has 102 call sites (confirmed (code), the cross-references to 0x0028cfe0). The five hooked
recordings logged 21 distinct return addresses, one per call site; the first table below gives those 21 (and the
second GetItemGoal_Process site, which a match could reach), the second table every other site.
The set-target hook logs ra, the return address into the function that called Brain_SetTarget, and ra2, the
word at 0(sp) on entry: the caller's own stack slot 0, since the hook runs before Brain_SetTarget moves sp.
ra2 is the caller's own return address wherever that caller saves ra at 0(sp) (confirmed (code) at each
prologue's sd ra,0x0(sp)):
- the wrappers
Human_SetTarget(0x00226c40),Brain_Fight(0x0028d308) andHuman_DropTarget(0x00226f7c), for whichra2names the cause; GangBrain_OnEvent(0x00304fcc),Brain_OnViolenceSeen(0x0029197c),StunnedGoal_Process(0x002b4bc0),SpectateGoal_End(0x002b4300) andAttackAction_Start(0x002fa9cc), for whichra2names only the dispatcher that ran them, one value each in the recordings:Brain_OnEvent(0x0028f928, returning to0x0028f974),GangBrain_OnEvent's call ofBrain_OnViolenceSeen(returning to0x003052a4),Goal_Process(0x0029eed8,0x0029ef64),Goal_End(0x0029edd8,0x0029ee08) andAction_Update(0x002f9e48,0x002f9ed4). These add nothing to the cause;raalone gives it.
The other logged callers save ra elsewhere (Brain_PickBestEnemy at 0x10(sp), 0x0029f270; FightGoal_Process
at 0x30, EngageEnemyGoal_Process at 0xa0, GetItemGoal_Process at 0x60, Brain_ClaimAttackSlot at 0x80,
WoundedGoal_Process at 0x50, Gang_AddMember at 0x40, Brain_DefaultOnEvent and SpectateGoal_Process at
0x20, GrabbingGoal_Process and GrabbedGoal_Process at 0x10), so for them ra2 is a stale stack word (for
Brain_PickBestEnemy it varies from call to call) and means nothing. For the next recording: logging the word
at sp + 0x10 as well would recover Brain_PickBestEnemy's caller, since that is where it saves ra: 0x002aecb8
for MeleeGoal_Process, 0x002bc224 for FollowAndAttackGoal_Process (it has 29 call sites in all), which would
replace rule 6's inferred side split with a measured one.
The functions and conditions are confirmed (code) at the call sites (each call is the instruction 8 bytes before
ra); "changes" counts calls that changed the brain's target, over the whole of the five hooked recordings (offsets
100 and 500 and the three swapped; ra2 was logged in three of them, offsets 100, 500 and swapped 682), confirmed
(runtime).
ra |
ra2 |
Caller | Cause | Force | Changes |
|---|---|---|---|---|---|
0x0029f298 |
Brain_PickBestEnemy (0x0029f230) |
AI pick, first step: clears the target before scoring the enemy list | 0 | 1772 clears | |
0x0029f368 |
Brain_PickBestEnemy |
AI pick: the best-scoring valid enemy (Who goes for whom); called by Melee (side 2) and FollowAndAttack (side 1; inferred from the goals each side runs, ra2 cannot tell) |
0 | 2269 sets | |
0x002b3ca8 |
FightGoal_Process (0x002b3ab0) |
re-target to the man in front: the nearest human within 1.5 m straight ahead, a threat, no GrabTarget goal (switches rule 2) | 1 | 835 switches | |
0x00305220 |
0x0028f974 |
GangBrain_OnEvent (0x00304fa8) |
hit back: event 1 from a non-friendly attacker, not busy, a fight goal already on the stack: takes the attacker as enemy and target | 1 | 116 switches, 3 sets |
0x0028d330 |
0x00305200 |
Brain_Fight from GangBrain_OnEvent |
hit back, as above with no fight goal on the stack: Brain_Fight(attacker, -1, 1) |
1 | 104 switches (plus 51 switches and 9 sets with force 1 where ra2 was not logged) |
0x0028d330 |
0x00291c6c |
Brain_Fight from Brain_OnViolenceSeen (0x00291960) |
help response: event 20 (a help call, Helping rule 1) heard with no fight goal: fights the side picked by Brain_PickSideInFight |
0 | 10 sets (plus 4 unlogged with force 0) |
0x00291c80 |
0x003052a4 |
Brain_OnViolenceSeen |
help response with a fight goal already on the stack | 0 | 37 sets |
0x002afcd0 |
EngageEnemyGoal_Process (0x002afa48) |
engage re-target: the nearest enemy he can see, every 2 s, when his own is beyond 5 m (switches rule 3) | 1 | 7 switches, 2 sets | |
0x002fad04 |
0x002f9ed4 |
AttackAction_Start (0x002fa9a8) |
attack action: a queued attack aimed at a human other than the target makes him the target | 0 | 14 switches, 1 set |
0x002dce08 |
GetItemGoal_Process (0x002dc9f8) |
item fight: an attack (Brain_QueueAttack, kind 0x11) on a human within 1 m of the item he goes for, when Human_CanStartAttack allows it; the other site, ra 0x002dcb08, targets a non-friendly human who holds the item |
0 | 2 sets | |
0x0028e0fc |
Brain_ClaimAttackSlot (0x0028df30) |
displaced: a closer attacker took the last attack slot on the man; the farther holder's target is cleared (limits rule 1). The logged brain is the displaced one | 0 | 61 clears | |
0x00226fd8 |
0x0028fb30 |
Human_DropTarget from Brain_MaybeSpotPlayers (0x0028fae8) |
own state: every think while his state holds any of 0x1f80874000 (dying, wounded, cuffed, knocked out, dead and others) |
0 | 154 clears |
0x00226fd8 |
0x0028c414 |
Human_DropTarget from Brain_OnKnockedOut (0x0028c3b0) |
knocked out (AI) | 0 | 8 clears |
0x00226fd8 |
0x0028c564 |
Human_DropTarget from Brain_TearDown (0x0028c4f8) |
the brain torn down (the human deleted) | 0 | none |
0x00226fd8 |
0x002ade80 |
Human_DropTarget from MeleeGoal_End (0x002ade60) |
Melee ends: its End runs when the goal is popped | 0 | 53 clears |
0x00226fd8 |
0x002dc884 |
Human_DropTarget from GetItemGoal_Start (0x002dc7f8) |
goes for an item (side 1 only in these matches) | 0 | 38 clears |
0x00226fd8 |
0x0030678c |
Human_DropTarget from Tactic_Start (0x00306690) |
new tactic: each non-player member is popped to his goal base and drops his target (side 2's TacticConfront, then TacticAttack; side 1's at the start) | 0 | 51 clears |
0x00226fd8 |
not logged | Human_DropTarget |
any of the five above | 0 | 90 clears |
0x002b4bdc |
0x0029ef64 |
StunnedGoal_Process (0x002b4bb8) |
stunned: no target while the Stunned goal runs | 0 | 186 clears |
0x002b52fc |
WoundedGoal_Process (0x002b52c0) |
wounded: no target (already cleared by the own-state drop) | 0 | none | |
0x002b437c, 0x002b4310 |
0x002b4310: 0x0029ee08 |
SpectateGoal_Process / _End (0x002b4330, 0x002b42f0) |
spectating: no target | 0 | 9 clears |
0x00166370 |
Gang_AddMember (0x00166308) |
joining a gang (the match's set-up, before the fight) | 0 | 5 clears | |
0x00226cbc |
0x00226f5c |
Human_SetTarget from Human_SyncTargetFromBrain (0x00226ee8) |
the human copying the brain's own target back: never a change | 0 | none (about 330,000 calls) |
0x00292dfc |
Brain_DefaultOnEvent (0x00292d80) |
event 2, the brain's reset (shared events) | 0 | none | |
0x002b6160, 0x002b6974, 0x002b69e8 |
GrabbingGoal_Process, GrabbedGoal_Process |
a grab's reaction goals: the grabber takes the man he holds (his pair link +0xc4) as target, the held man his grabber, as the mount goals do |
0 | none |
The other 80 sites, none of which any of the five recordings logged (confirmed (runtime)). Set or clear and the
force are read at each call (confirmed (code)); the conditions of the first group are confirmed (code) in the
functions named; for the rest the cause is the function's role as AI goals and AI code
give it. ra is the call + 8.
Goals and handlers a brawl's fighters run or could meet:
ra |
Caller | Cause | Force |
|---|---|---|---|
0x002aec88 |
MeleeGoal_Process (0x002aebf8) |
no slot: with threat response 0 (brain +0x21c) Melee keeps its set enemy instead of picking; when that enemy is valid but his brain gives this man no attack slot (Brain_HasAttackSlot), the target is cleared and Melee ends. With a threat response (2 from the brain's creation) Melee picks through Brain_PickBestEnemy instead |
0, clear |
0x002bc278 |
FollowAndAttackGoal_Process (0x002bc198) |
too far from the leader: the pick is more than 60 m (squared 3600) from the goal's leader | 0, clear |
0x002b4af4 |
GroundedGoal_Process (0x002b4aa8) |
down: each update while he is knocked down, the target is cleared when the target is hidden in shadow (the target's brain +0x2d4) |
0, clear |
0x0030797c |
AttackTactic_OnViolence (0x00307878) |
TacticAttack, event 20: each member with an empty goal stack takes the side the leader picked as enemy and target (AI reimplementation); side 2's stacks were empty 0 to 0.5 % of the time | 0, set |
0x002af3ac |
AttackTargetGoal_Process (0x002af278) |
each update, the goal's own target once validated (a goal a script order gives) | 0, set |
0x002b1e88 |
ChaseGoal_Process (0x002b1860) |
the chased man, whenever he is in line of sight (when he is hidden in shadow, only within 2 m and when this brain sees hidden humans, +0x2d5); also added as an enemy |
0, set |
0x002b66cc |
MountingGoal_Process (0x002b6638) |
the man under him (his pair link +0xc4) |
0, set |
0x002b6cb8 |
MountedGoal_Process (0x002b6bb8) |
the man on top of him, after the help call, when his brain is not type 5 and has a threat response | 0, set |
0x002bb908 |
GrabTargetGoal_Process (0x002bb858) |
the human to grab | 1, set |
0x002b92f0 |
SaveHumanGoal_Process (0x002b8ad8) |
a human whom Brain_IsThreat (0x00290138) calls a threat (the goal fights off attackers on its way) |
1, set |
0x002a3590 |
ManWeaponPileGoal_Process |
the human he throws at | 0, set |
0x0026a280 |
HitReact_OnSpecialEnd (0x0026a1f8) |
end of a special hit reaction: when the human's foe is the player (brain type 0) and the player's target is this human, the player's target is cleared; never an AI's | 0, clear |
0x0022c984, 0x0022c230 |
Grab_Link (0x0022c730), Pair_LinkMount (0x0022bfd0) |
a grab or mount links: when the victim is the player (brain type 0), the player's target becomes the grabber | 0, set |
0x00226d88, 0x00226dc8, 0x00226b84 |
Human_SetTargetObject (0x00226cd0), Human_SetTargetFromHandle (0x00226af8) |
a human's own target written to his brain: callers are the player's controls (Player_Square, Player_Cross, Player_LockOnTarget and others) and Human_SyncTargetFromBrain |
0, set |
0x00190518 |
Human_TraceThrowAim (0x0018fee0) |
after the aim trace of a throw (Objects: throws); the condition was not traced | 1, clear |
0x00169b80 |
Gang_CalmMembers (0x00169b00) |
the gang calmed: each AI member's target and enemies cleared | 0, clear |
0x00260128 |
Cuff_OnCufferClipEnd (0x002600f0) |
the cuffer's clip ends (cops) | 0, clear |
Other goals, brains, tactics and bosses (missions, the street, cops, the player; not on a brawl fighter's stack):
ra |
Caller | Set or clear, force |
|---|---|---|
0x002baa1c |
StandGroundGoal_Process |
clear, 0 |
0x002baee8, 0x002bb104, 0x002bb334 |
ObjectPileGoal_End, ObjectPileGoal_Process (twice) |
clear 0, clear 0, set 1 |
0x002bd6d4, 0x002be2b8 |
FollowAndDefendGoal_DefendCrime, FollowAndDefendGoal_Process |
set 1, clear 0 |
0x002be930, 0x002be9e8, 0x002bea74 |
HoldPositionGoal_Process |
clear, 0 |
0x002bf9d0, 0x002bfa9c |
WarriorVandalStealGoal_Process |
set 0, clear 0 |
0x002c4674 |
Goal_ReportCrimeB |
set, 1 |
0x002c9230, 0x002c92c0, 0x002c9aa8, 0x002c9c0c, 0x002ca040, 0x002ca104, 0x002ca7cc |
HTLDefenseGoal_Process (twice), HTLDefenseFightGoal_Process (twice), HTLOffenseGoal_Process (twice), HTLOffenseFightGoal_Process |
clear 0, not traced 0, set 1, clear 0, clear 0, clear 0, set 1 |
0x002ce284 |
StealGoal_Start |
clear, 0 |
0x002d36f8 |
ScatterGoal_Process |
set, 0 |
0x002d9984 |
BoxerGoal_Process |
set, 0 |
0x002daaf0 |
MoveToExitFlagGoal_Resume |
clear, 0 |
0x002de630, 0x002deac0, 0x002deb0c, 0x002dec18, 0x002dec2c |
FollowPlayerGoal_Suspend, FollowPlayerGoal_Process (four) |
clear, set, set, clear, clear; 0 |
0x002e20c0 |
DevilRunGoal_Pace |
set, 0 |
0x002e3308, 0x002e3a10, 0x002e3c20, 0x002e3e4c |
AvoidEnemiesGoal_Resume, AvoidEnemiesGoal_Process (three) |
clear, clear, set, set; 0 |
0x002e6624 |
ShopkeeperGoal_Reset |
clear, 0 |
0x002e89d4, 0x002ea1f0, 0x002ebf70 |
BigBrawlerGoal_OnAttackWarning, BigFighterGoal_OnAttackWarning, BigFighterAGoal_OnAttackWarning |
set, 1 |
0x002ed920, 0x002ed958, 0x002eddc8, 0x002ede64 |
BigThrowerGoal_Process |
set, set, clear, clear; 0 |
0x002ef3a8, 0x002ef614 |
StationaryThrowerGoal_Process |
set, clear; 0 |
0x002f80fc, 0x002f8370 |
HideAndSeekGoal_Found, HideAndSeekGoal_OnAttackWarning |
set, 1 |
0x002a237c, 0x002a2398, 0x002a24b4, 0x002a26ac, 0x002a297c, 0x002a2b50, 0x002a2e74 |
BossDiegoGoal_Process |
set 1 (four), clear 0 (two), set 0 |
0x0030c284 |
BossRoofTactic_OnEvent |
set, 1 |
0x0031aa00, 0x0031ae5c |
ScoutTactic_OnEvent, ScoutTactic_OnViolence |
set, 0 |
0x0031e6b4 |
VandalizeTactic_OnDamage |
set, 1 |
0x002fefc4 |
CivilianBrain_Think |
clear, 0 |
0x00301fc0, 0x00302668 |
CopBrain_ReportCrime, CopBrain_OnEvent |
set 1, set 0 |
0x00303644 |
PlayerBrain_Update |
clear, 0 |
So the causes rule 7 of Rumble's comparison needs map as follows: AI pick
0x0029f298 and 0x0029f368 (a clear and a set in one update, a change of enemy); re-target in front
0x002b3ca8; hit back 0x00305220 and 0x0028d330 with ra2 0x00305200 (or, unlogged, force 1);
help response 0x00291c80 and 0x0028d330 with ra2 0x00291c6c (or, unlogged, force 0); engage
0x002afcd0; displaced 0x0028e0fc; reactions and own state (stunned, wounded, knocked out, down)
0x002b4bdc, 0x002b52fc, 0x002b4af4 (Grounded, never logged: it runs only while he is down, but the clear's cause
is the target hiding in shadow, not his being down) and Human_DropTarget from 0x0028fb30 and
0x0028c414; orders (tactic start, Melee's end, an item) Human_DropTarget from 0x0030678c, 0x002ade80 and
0x002dc884. The rows this list left out, with their bucket:
- Reactions: the grab's goals
0x002b6160,0x002b6974and0x002b69e8. They make the pair partner the target, as the mount goals (0x002b66cc,0x002b6cb8) do, and run only while he grabs or is grabbed (confirmed (code), the calls at0x002b6158,0x002b696cand0x002b69e0; the held man only when the grabber is not friendly and he has a threat response, the second call a 60 % repeat of the first). - Hit back:
AttackAction_Start0x002fad04. It makes the victim of every attack action that starts the target (if victim != self, confirmed (code) at0x002fa9a8), so it changes the target only for an attack queued at someone else. In a brawl those are the block's punishing attack at the man whose blow he blocked (Block_QueuePunish0x002b5718, Blocking) and the grab and mount goals' attacks on their partner (Brain_QueueAttackfrom0x002b643c,0x002b67ec,0x002b6b4c,0x002b6d90); the fight goal, Melee and the tackle attack their own target, and the Grounded get-up attack is aimed at himself.Brain_QueueAttack(0x0028e248) has 38 calling functions (46 call sites); the rest are left out because they belong to goals a brawl's TacticAttack does not push: the bosses' and set pieces' goals (BigBrawler, BigFighter, Boxer, the HTL goals, ObjectThrower, ...), civilians', cops' and item and object goals, and the crew's FollowAndDefend and SaveHuman, which only the Defend tactic or the defend command push (helping rule 3; their attacks would also be at a man attacking the defended mate).PedReactionGoal_Processcan run in a brawl (the flight check pushes PedReaction type 9 under TacticAttack, the gang rule 3), but its one attack (0x002aa9d0, the kind 15 shove at the enemy) is only for a civilian, brain type 4 (PedReaction step 5), not a gang member. Inferred from the goal lists, not traced per call site. So a switch here is mostly an answer to the man who attacked him (inferred: the hook cannot tell which goal queued the attack); a trace that logged the queuing goal could split it from the reactions. - Orders: the item sites
0x002dce08and0x002dcb08.GetItemGoal_Processfights for the item it was sent for, as its Start's drop (0x002dc884) already counts there. - Pick: Spectate
0x002b437cand0x002b4310. In a brawl the Spectate goal is pushed by Melee when its pick found no target (The Melee goal step 3), so its clears belong to the same outcome as the pick (inferred: ofSpectateGoal_Init's five callers,0x002b4098, the others areAttackTargetGoal_Process, the riot cop, the dealer's wary scan and the crowd tactic, none of which a brawl fighter runs). - Orders: the reset
0x00292dfc(event 2, which empties the brain from outside: the brain's goals, actions, enemies and target), inferred from what it does; no recording logged it.
The sites a brawl could reach but the recordings never logged (Melee's no-slot clear 0x002aec88,
FollowAndAttack's 60 m clear 0x002bc278, TacticAttack's violence handler 0x0030797c, the grab and mount
reactions) are in the second table; a comparison that meets one should count it under its cause there. Two causes
the brief of issue #273 names have no site of their own:
- A helper sent (
Gang_SendHelper0x002b75b8) does not set a target itself: neither it nor the HelpRespond goal is among the 102 callers (confirmed (code)). Its caller does, on the same man, just before sending:Brain_OnViolenceSeensets the side it picked as the target (0x00291c80, orBrain_Fightwithra20x00291c6c) and then sends him toward that man (confirmed (code), the calls at0x00291c78and0x00291cdc), andAttackTactic_OnViolencedoes the same for an idle member (0x0030797c). Every send in the recordings came fromBrain_OnViolenceSeen(Runtime checks rule 7), so a helper sent shows in the trace as a help response. This corrects the earlier reading that the target came from Melee's later pick. - Re-target on the target's death: no caller reacts to the target going down; the fight goal's validation fails,
the goal ends and the next pick clears and sets (
0x0029f298,0x0029f368), or a slot is lost (0x0028e0fc) (switches rule 1; inferred: none of the 102 call sites is such a handler).
Coney's implementation: what Coney needs¶
Coney's state from reading repo:src/ai/ (2026-10-12); each item names the rule above it implements.
- The periodic enemy scan for every brain (The enemy scan):
scanEnemiesand its schedule exist (repo:src/ai/enemy_scan.h) but only the Scout tactic calls them, so gang soldiers, Warriors and Rumble fighters learn enemies only from hits, help calls and tactics. RunBrain_MaybeScanEnemiesfrom each brain's update, 5 scan tokens an update. - TacticAttack's members choose by the score: built (Unit 2); they run the real Melee
goal, which picks with
Brain_PickBestEnemy. - The 3 s idle check sends the idle to the farthest busy mate (HelpRespond after Melee): built (Unit 2).
- The team-ups (The gang as a whole rule 4): not built (
repo:src/ai/tactic_attack.h); withGangEnableAttackStrategies, the seven orders, their weights, counts, lines, gestures and the leader order's two temporary changes. - The shared target: the field exists (
Gang::chosenTarget) but nothing sets it; the unforced attack command sets it from the chief's lock-on target, else from the crime reporter or the gang caller (targets rule 4), and adds it to every AI member's list. - The re-target forces the slot and does not require the enemy list or validity (Coney's
retargetFromSectorschecks both, and its threat test is a stand-in). - Waiting attackers circle and taunt: the taunt every 1-2 s is built (2026-10-20:
FightGoal's timer, the fidget action on anim0x25bwith the gang's combat fidgets,cheer2, and the voices' gesture slots;repo:src/ai/fidget_action.h); a Warrior'sturf_defendline is not. Coney's ring only steps out to the outer radius and turns to face; the original's move-to-human holds them inside 4-4.75 m (they keep moving). Wait reasons 4, 6 and 8 are never raised (their inputs are not computed). - Event
0x15's slot-holder taunts are not built: sent to a man as he is knocked down by a hit or put down by a throw or grab (waiting rule 5). - The gang's alert state (
+0x32): built (Unit 2) as state, clip-group counts and the members'+0x14d; nothing plays the clip groups or reads+0x14dyet (Coney has no turf-only target test). - Flight: TacticAttack's flee check (v9 × the starting count, class 11 only) under
GangCanFlee: built (Unit 2). - The tandem on a held enemy (Combat moves, issue #84): not built.
- FightBackOff for the guard-and-back-off order and the pusher: not built.
Already faithful (keep): attack slots and their displacement, active places and spacing bytes from CfgGang v2 and
v3, the shared swing gap, Brain_CheckAttack's order, the score terms and weights, help calls and side-picking,
HelpRespond, the leader rule, TacticConfront's switch to TacticAttack.
What a trace records to check these rules¶
Per human and update (the field set the gang-battle trace uses; brain = 0x006d53f0 + index × 0x2f0, gang record
0x005e6e30 + id × 0xb10):
| Rule | Quantities | Where |
|---|---|---|
| Who goes for whom | target, enemy-list count, gang id, brain type, distance to target, same-gang count on the same target | brain +0x124, +0x164 (16 handles), +0x20c → gang +0x30, +0x04 |
| Shared target | gang's shared target | gang +0x10 |
| How many at once | slot holders on each man, active places held, spacing bytes, swing gap, own cooldown | brain +0x1a4 (16), +0x1e4, +0x1f0 (4), +0x14a, +0x14b, +0x1ec, +0x1e8 |
| Waiting | wait reason (hook Brain_CheckAttack 0x002906b8 result), ring distance, taunt clip 0x25b |
the hook; human record +0x20 (clip) |
| Switches | top goal (vtable of +0x40[top]) and depth +0x2c, reaction goal +0x3c, fight goal +0x10 (kind), +0x14, +0x24, +0x28; target changes with their caller (hook Brain_SetTarget 0x0028cfe0, ra) |
brain; the hook |
| Helping | help calls (hook 0x00293640), helpers sent (hook 0x002b75b8), side picked (hook 0x002912b0), state flags of mates (grabbed 0x30, down 0x80000) |
hooks; human record +0x00 |
| The gang | alert +0x32, tactic vtable (+0x40 → +0x1c), leader +0x44, team-ups allowed +0xd9, can flee +0xdf, start count +0xe0, running order (tactic +0x10 → its vtable +0x0c) |
gang record; the tactic |
The humans field set records the brain and gang quantities above (the gang's as g_alert, g_tactic, g_leader,
g_chosen, g_teamups and g_order, the team-up order running), and the hooks set-target, help-call and
send-helper of repo:research/traces/patches/gang-battle.toml log the target changes with their caller, the help
calls and the helpers sent. The wait reason (Brain_CheckAttack's result) is not recorded yet: it needs a hook where
the result is returned, not at the entry.
Metrics the comparison can draw from them, per gang: the share of updates with 0, 1, 2, ... members on one enemy and with 0, 1, 2 active places held; the mean time a member holds one target; switches per minute split by cause (re-target, fight-goal end, engage re-target); the share of slot holders inside 4-4.75 m while waiting; taunts per minute; help calls and helpers sent per member down; the share of members idle (empty stack); team-ups started per minute by kind.
Open questions¶
None left from issue #298: what fills game state +0x280 / +0x284 (targets rule 4), who reads
TacticAttack's interval (nothing, Rumble rule 1), what sends event 0x15 (waiting rule 5)
and whether Rumble enables the team-ups (its sides never get them, Rumble rule 5) are answered on the
page. Where the team-ups are on in the story is known from the scripts (GangSetup unless AttackStratsOff, and
three chunks directly, Rumble rule 5).
- Unseen in the recordings made so far: a story gang with
+0xd9set giving team-up orders in a fight. Theg_teamupscolumn of a trace in a story fight against aGangSetupgang would confirm rule 5's script reading and show how often the orders run.