Characters (humans)¶
Verified against: SLUS_212.15 (NTSC-U, SHA1 e9cb2cc49aa046b9e494313dce2f5038ed17b2f4). Runtime claims were made in
PCSX2 2.9.94 (2026-10-04) by reading memory over PINE in level99, checkpoint 1, and say so; those about sprint,
jumps and climbs (2026-10-05) in a street ("the street"), from save states made at test spots, with pad
input patched in through a copy of the state; the street is in level99's world (its spots, ground and objects are
where level99's collision mesh has them, Feel comparison). The disc survey read the
NTSC-U disc's WAD with throwaway scripts outside the repository and reports counts and hashes only.
Purpose¶
Every person in the game, the player included, is a human: one object of a single class with a skinned model, an animation player and a brain. The player is a human whose controller is a pad instead of the AI. This page covers what the first playable milestone needs: how a level script creates the player, which files make a character, the object's layout as far as it is known, how pad input becomes movement, how a speed picks idle, walk or run, and the player's sprint, stamina, jump and climbs. The animation format is on Animation; the camera that follows the player on Camera.
In one paragraph: a level script calls HuCreate(name, type, position, heading, ..., player, gang). The game takes a
free slot among 60 static humans, remaps the type to a behaviour class, finds the character's model by name in
the Character List of warriors.glr, snaps the position to the ground with a short ray cast and, for player 1,
binds pad 0 and the camera. The character's files are three resources named by CRC: the model (an RW clump with a
32-bone HAnim skeleton and a PS2 skin), its texture dictionary, and its character data (its animations and an
anim id → animation table). Each update (30 per second) the pad's stick, already turned into camera space, gives a
direction and a magnitude; the magnitude chooses walk (from 0.12) or run (above 0.95), the speed ramps towards it at
24 m/s², and the heading turns towards the stick at a limited rate. Holding L2 at a run sprints while stamina lasts;
Triangle climbs a fence or wall in front of the player or, at a run, jumps.
Original structure¶
Human/ (0x00217a70-0x00323298, inferred from the path strings and calls on Source map; the four
functions before Human_GetClump at 0x00217a70 belong to GameStatsSubItem.cpp, inferred from the vtable
0x0053ef48 they share with that file's anchor) holds the human; the model side is in Graphics/Character.cpp,
CharacterModel.cpp and Animations.cpp. Names are ours unless a path or class string gives them.
| Address | Name | Role | Evidence |
|---|---|---|---|
0x00358428 |
HuCreate binding |
reads the Lua arguments, calls Human_Create |
confirmed (code) |
0x00233d60 |
Human_Create |
heading quaternion, slot, init, name, player; returns the handle | confirmed (code) |
0x00217f08 / 0x00217ec8 |
Human_AllocSlot / Human_FreeSlot |
a free index below 60 in the object table | confirmed (code) |
0x00218008 |
Human_Init |
type remap, model, ground snap, defaults | confirmed (code) |
0x0021cda8 |
Human_SetName |
15 characters at +0x80 |
confirmed (code) |
0x00229c40 |
Human_MakePlayer |
joins the player list, takes a pad and a HUD panel, brain type 0 (Players) | confirmed (code) |
0x0022a2a8 |
Human_ReleasePlayer |
the reverse: frees the pad and panel, leaves the player list, AI again | confirmed (code) |
0x00419b10 / 0x00419c68 |
PlayerList_Add / PlayerList_Remove |
the game state's list of at most two players | confirmed (code) |
0x0022a770 |
Gang_PickNextPlayer |
the gang member a player hand-over chooses | confirmed (code) |
0x0021c7c8 |
Human_Destroy |
takes a human out of the world at once (Destroying a human) | confirmed (code) |
0x00228730 / 0x0021cb78 |
static initialiser / constructor | builds the 60 humans at start-up | confirmed (code) |
0x00228800 |
CharClass_Get(id) |
0x00684620 + id × 0x1ac |
confirmed (code) |
0x00383f38 → 0x00228af8 |
CfgChar |
fills a character class | confirmed (code) |
0x003842c0 → 0x00228888 |
CfgSpeedClass |
fills a speed class | confirmed (code) |
0x00221710, 0x002215d0, 0x00221620, 0x00221670, 0x002216c0 |
speed getters | base, walk, jog, run, sprint | confirmed (code) |
0x00221760 |
Human_GaitForSpeed |
a speed to gait 0, 2, 3, 4 or 5 | confirmed (code) |
0x002213d8 |
Human_MaxTurn |
the turn limit per update | confirmed (code) |
0x00146078 |
PlayerRecord_Update |
pad → stick angle and magnitude | confirmed (code) |
0x00240e38 |
Human_PlayerLocomotion |
stick → velocity and heading | confirmed (code) |
0x00248df0 |
Human_Lean |
a lean from the turn rate | confirmed (code) |
0x00259578 |
Human_ChooseAnimState |
picks the anim state, calls its builder from 0x005106e0 |
confirmed (code) |
0x0025b200, 0x0025b9f8, 0x0025f770 |
move, run-start and idle task builders | see Clip selection | confirmed (code) |
0x0025ec28 |
Gait_BlendForSpeed |
speed → gait blend target 0-3 | confirmed (code) |
0x0023fea8 |
Human_StateUpdate (vtable +0x13c) |
per update: state function, gravity, out-of-world, move | confirmed (code) |
0x0023d8c8 |
Human_Move |
slope factor, physics sweep, ground snap | confirmed (code) |
0x0023eab8 |
Human_SnapToGround |
1.5 m ray down from 1 m above the feet | confirmed (code) |
0x0023dc58 |
Human_StartFall (vtable +0x154) |
airborne flag, fall target, camera ledge hint | confirmed (code) |
0x0023e408 |
Human_LandingTest |
the airborne sweep's floor test (n.z > 0.65) |
confirmed (code) |
0x0023e090 |
Human_Land |
clears airborne, fall damage, vz = 0 |
confirmed (code) |
0x003a2158 / 0x003a21c0 |
Object_SetAirborne / Object_SetGrounded |
flag 0x4000000 / 0x2000000 in +0x54 |
confirmed (code) |
0x0021b0b8 |
Object_SetPosition |
writes the transform table 0x00714b00 |
confirmed (code) |
0x0033e278 |
PhysicsBody_Sweep |
moves a body by v × dt, up to three sliding passes |
confirmed (code) for the steps listed |
0x00347c08 |
PhysicsMesh_SweepCapsule |
the walking body's sphere swept against the mesh's walls, with the 0.25 m step rule | confirmed (code) |
0x003477c0 |
PhysicsBody_PushOutOfWalls |
sphere against the collision mesh's walls (n.z within ±0.65) |
confirmed (code) |
0x0021a490 |
Human_PushOutInAir |
the push-out sphere (or a bone segment) while airborne and in some states | confirmed (code) |
0x00219d50 |
Human_OnContact |
the body's contact handler: landings, fences while climbing, objects | confirmed (code) for the parts cited |
0x0027c120 |
Player_UpdateActions |
the player's commands each update: climb, jump, block, sprint | confirmed (code) for the parts cited |
0x0027ce90 |
Player_UpdateSprint |
clears the sprint flag and sets it again while L2 is held | confirmed (code), runtime |
0x00223188 |
Human_StaminaMax |
stamina maximum from the power class | confirmed (code) |
0x002562d0 |
Human_DrainMeters |
stamina drain while sprinting (and a second meter) | confirmed (code) |
0x00256a60 |
Human_RefillMeters |
stamina refill (and the second meter) | confirmed (code) |
0x002829e8 / 0x0023db48 |
Player_TryJump / Human_BeginJump |
the jump's checks; take-off gait and height | confirmed (code) |
0x002217f0 |
Human_LaunchJump |
the jump's velocity, then airborne | confirmed (code) |
0x00240898 |
Human_AirControl |
the jump's state function: steering in the air | confirmed (code) |
0x002826f0 |
Climb_TryStart |
the two forward probes; fence, wall and their short forms | confirmed (code) |
0x00282370 |
Climb_ProbeTop |
finds the obstacle's top and picks fence or wall | confirmed (code) |
0x00281c20 |
Climb_Start |
reach window, start point, the three-clip chain | confirmed (code) |
0x00281450 / 0x00281838 |
climb callbacks | end of the first clip, from a run / from standing | confirmed (code) |
0x00249108 |
Humans_Update |
the characters' update, every second task-manager tick | confirmed (code) |
0x00249b98 |
Humans_MarkSkeletons |
marks every skeleton for an update, from mode 1 | confirmed (code) |
0x001783d0, 0x0018e9e0, 0x0016e8f0 |
resource loaders | model (type 3), textures (type 4), character data (type 5) | confirmed (code) |
0x0016e258 |
chunk 0x08 handler |
the character data's anim table | confirmed (code) |
0x00177b80 / 0x00174d00 |
CharacterInstance create / constructor |
the model instance (0x330 bytes, vtable 0x00538a78) |
confirmed (code) |
0x00217a98 |
Human_AttachInstance |
gives the human its instance (+0xd8) |
confirmed (code) |
0x00175080 |
CharacterInstance_GetAnim(id) |
an anim id to an animation | confirmed (code) |
0x0023ac50 |
AnimCallback_Dispatch(human, id) |
runs the script's anim callback (Animation callbacks) | confirmed (code) |
0x001897a8 |
resource manager update | loads missing characters and dynamic animations later | confirmed (code) |
Data¶
The human object (0x6d0 bytes)¶
Sixty humans live in one static array at 0x00640c80 (human i at 0x00640c80 + i × 0x6d0), built at start-up by
0x00228730 with the constructor 0x0021cb78. A human in use has an entry in the handle table
(0x006ebd38, 0xb00 entries of {pointer, u16 serial}; a handle is serial | index << 16, resolved by
0x00390180 / 0x00390268). Vtables: 0x0053f088 at +0x00, 0x0053f020 at +0x70, 0x00544b98 at +0xe8.
Confirmed (code); offsets with "runtime" were checked on Rembrandt.
| Offset | Type | Meaning | Evidence |
|---|---|---|---|
+0x10 |
vec4 | position, game axes (z up), metres |
confirmed (code), runtime |
+0x20 |
quat | rotation (x, y, z, w); a heading is a rotation about z |
confirmed (code), runtime |
+0x80 |
char[16] | name (Rembrandt) |
confirmed (code), runtime |
+0x90 / +0x92 |
u16 | own handle's serial / index | confirmed (code) |
+0xcc |
int | behaviour class (0x1e for Rembrandt): selects the class record for speeds |
confirmed (code), runtime |
+0xd0 |
int | the type passed to HuCreate (0x20) |
confirmed (code), runtime |
+0x54 |
u32 | object flags: 0x4000000 airborne, 0x2000000 on the ground (Ground) |
confirmed (code) |
+0xd4 |
pointer | the human's 0x180-byte record (0x0065a540 + i × 0x180, below) |
confirmed (code), runtime |
+0xe0 |
u64 | human flags. Human_Init starts them by brain type (Combat: human flags: a Warrior gets 0x2 and the revivable 0x4). Human_MakePlayer sets 0x2 (may start a climb from a run), 0x4 and 0x2000000 and clears 0x8; 0x4000000 keeps stamina full; 0x10000000 forbids a jump; 0x20000000 forces the anim state and forbids a climb. Rembrandt: 0x24004440407 |
confirmed (code), runtime |
+0xd8 |
pointer | the CharacterInstance (model, skeleton, animation) |
confirmed (code) |
+0x1a0 |
pointer | the physics body (0 for none) | confirmed (code) |
+0x1a8 |
int | the gait for the current speed (0, 1-5; 0x0022aeb0), used by the lean |
confirmed (code) |
+0x1ac |
float | current speed (length of the velocity, written with it) | confirmed (code) |
+0x1b0 |
s8 | player slot: the human's place in the game state's player list (0 or 1), -1 for a human that is not a player; set only by the list (Players) | confirmed (code) |
+0x1b1 |
s8 | hand-over priority: HuCreate's player argument, stored as given (1 Rembrandt, 2 Ash); the lowest non-zero value is the member a hand-over picks (Players) |
confirmed (code) |
+0x1b8 / +0x1b9 |
u8 | power class of a non-player / of a player (64 for Rembrandt) | confirmed (code), runtime |
+0x1d8 |
int | material of the ground under the feet (5 when none) | confirmed (code) |
+0x230 |
vec4 | ground normal from the last snap (+0x238 its z) |
confirmed (code) |
+0x280 |
int | an attachment: −1 normally; otherwise the human moves without the physics sweep | confirmed (code) for the tests |
+0x298 / +0x29c |
float | turn this update / smoothed lean (radians) | confirmed (code) |
+0x2c0 |
vec4 | where a fall will end (Human_StartFall); a climb's start point (Climb_Start) |
confirmed (code) |
+0x2f0 |
vec4 | external push velocity, added to the velocity when moving | confirmed (code) |
+0x338 |
handle | the object in the hand (Nil when none); pose bone 25 is the right hand (runtime), 19 the left (inferred from the left-hand pick-up clips) (World objects: objects in a human's hand) | confirmed (code), runtime |
+0x33c |
handle | the object being picked up, from the pick-up's message 0x14 until its clip event attaches it |
confirmed (code), runtime |
+0x348 / +0x34c |
handle | the carried prop a clip makes (event 0x30) and removes (event 0x31), attached at point 0x13 and moved by event 0x37: both hold the same handle once attached; Rembrandt's power move 59 uses it (Combat: props of a power move) |
confirmed (code), runtime |
+0x37c (+0xdf as a word index) |
int | model index in the Character List | confirmed (code) |
+0x380 |
int | HUD player panel (0 or 1), -1 for none (Players) | confirmed (code) |
+0x384 |
int | airborne updates so far | confirmed (code) |
+0x390 |
vec4 | last ground position | confirmed (code) |
+0x3a0 |
float | vertical velocity, kept by the locomotion, integrated by gravity while airborne, 0 on landing | confirmed (code) |
+0x3a4 |
float | speed multiplier, set to 1.0 by Human_Init (0x002180c8); Rembrandt's speeds match his clips exactly, so 1.0 at runtime |
confirmed (code); runtime inferred |
+0x3c0 |
int | the gait at a jump's take-off (picks the launch speed) | confirmed (code) |
+0x3c8 |
7 × 0x28 | dynamic animation slots | confirmed (code) |
+0x560 |
float | the height (z) at a jump's take-off |
confirmed (code) |
+0x5b9 |
u8 | 1 while a climb clip (437-460) plays, set by Human_StateUpdate |
confirmed (code) |
+0x5d8 / +0x5dc |
float | last stick angle / magnitude | confirmed (code) |
+0x5e0 / +0x5e4 |
float | last turn step / last heading error (turn smoothing) | confirmed (code) |
+0x65c |
float | body scale: 1.0, then a random size and a per-type override (Body scale); read by Human_GetScale (0x0021d020). 0.97 for Rembrandt |
confirmed (code), runtime |
Per-player record (0x00660f50 + i × 0x2c, 60 of them, one per human index), confirmed (code) at 0x00146078:
| Offset | Meaning |
|---|---|
+0x00 |
8 bytes copied from the pad record's +0x08 (Front end): the left stick's x and y as two floats (y up), not turned by the camera (the tagging stick game reads them so, 0x002748a8) |
+0x1c, +0x1d |
the rumble: a slip in the tagging game writes 255 and 1, an on-track update 0 and 0 (inferred from Tagging) |
+0x08, +0x0c |
stick angle, two buffers (radians, atan2 of the camera-turned stick) |
+0x10, +0x14 |
stick magnitude, two buffers, clamped to 1 |
+0x18 |
which buffer is current (toggles 0/1 each update; the other is the previous update's) |
+0x19 |
pad index, -1 for none (0 for the player at runtime) |
+0x1b |
1 while the human is pad-controlled; 0 hands it to the AI |
+0x1e |
when not 0, Player_UpdateActions is skipped (0x00254e78 via 0x001480e0); 0 for every human in the street, so AI humans act on +0x20 too (confirmed (runtime)) |
+0x1f |
input locked: angle π/2, magnitude 0 |
+0x20 |
this update's command id (Buttons), read by 0x00147ef8; cleared each update for a human without a pad (0x00146000) |
+0x24 |
a pending command (from table entries whose mask is 0xfe) |
A second per-human record of 0x2f0 bytes at 0x006d53f0 + i × 0x2f0 is the human's brain (AI);
confirmed (code) at Human_GetBrain (0x0021d408).
The 0x180 record¶
Each human's record (+0xd4), as far as this page uses it. Confirmed (code) at the accessors named; runtime values
are Rembrandt's.
| Offset | Type | Meaning |
|---|---|---|
+0x00 |
u64 | state flags: test 0x002265f0, set 0x002265d0, clear 0x00226620; bits below |
+0x08 |
u32 | more flags: test 0x00226660, set 0x00226640, clear 0x00226688. 0x10 forbids a sprint, 0x40 is set while climbing over, 0x400000 slows a body in the air (velocity × 0.95 per update); any of 0x84240 refuses a climb and any of 0x41a420 stops the stamina refill. Seen at runtime: 0x10000000 while a start clip plays, 0x80000 during a run stop and the first and last clips of a climb, 0x1000000 while landing from a jump |
+0x14 |
int | state code (setter 0x002266a8) |
+0x18 |
int | anim state (Clip selection) |
+0x20 |
int | anim id playing (getter 0x002266b8) |
+0x28-+0xb3 |
int[35] | anim slots |
+0x144 / +0x146 |
s16 | health / its maximum (Combat) |
+0x148 |
s16 | the power meter that grabs spend, refilled at the power class's +0x2a per second to the maximum 0x00223068 returns (+0x28, Combat) |
+0x14a |
s16 | stamina (Sprint) |
+0x14c / +0x154 |
u32 | game time (ms, *(0x0050b734) + 0x48) of the last update of the second meter / of stamina |
+0x150 / +0x158 |
float | the fractions carried between updates of the two meters |
+0x164-+0x17c |
float | the speeds (Speed classes) |
State flag bits used on this page:
| Bit | Meaning |
|---|---|
0x1000000 |
sprint asked for (L2 held, Sprint) |
0x1-0x8000, 0x4000000 |
fight stance, grabs, mugging, tackles, throwing, blocking, theft (Combat) |
0x200000000 |
dead |
0x400000000 |
jumping (set at launch) |
0x800000000 |
falling (a drop) |
0x1000000000 |
a long fall |
0x2000000000 |
landing |
The three airborne bits (0x1c00000000) are cleared on landing.
Power classes¶
CfgPowerClass fills records of 0x44 bytes at 0x006619a0 + class × 0x44. A human's class is byte +0x1b9 for a
player and +0x1b8 otherwise (0x00222b78). Confirmed (code) for the reads at the cited addresses; Rembrandt's
values confirmed (runtime). A street civilian (class 2) had a 0.35 hurt threshold, stun 750 ms, ground time 2000 ms,
power 200, byte +0x36 4 (confirmed (runtime)). The AI's fields +0x08 (block chance), +0x0c (block chance while
hurt), +0x24 (counter chance) and +0x37 (the pattern-reading threshold) are on AI; +0x32 and
the bytes after +0x37 are below the table. The sparring Warriors' class 40 is on
AI:
| Field | Rembrandt (class 64) | Use |
|---|---|---|
+0x00 |
1.3 | as the attacker, scales the victim's stun for weapon hits of 20-49 damage (0x0022f658) |
+0x04 |
0.3 | the hurt threshold: below this fraction of maximum health the human is hurt (0x00222ff8) |
+0x10 / +0x14 |
1.0 / 1.0 | the stun time's / the ground time's factor while hurt (Combat) |
+0x1c / +0x20 |
3.0 / 3.0 | AI only: the factor on CfgAttackDelay, the second when the target is down (0x00223800) |
+0x28 |
400 | the power meter's maximum (Combat) |
+0x2a |
60 | the power meter's refill per second |
+0x2c |
135 | stamina maximum |
+0x2e |
40 | stamina refill per second |
+0x30 |
200 | a stun's length, ms (0x0022f658) |
+0x34 |
2750 | the time a knocked-down human stays down, ms (0x0022f100) |
+0x36 |
3 | in a grab struggle, the grabbed human's strike costs the grabber 1 / this of its power (0x0027fd68) |
The remaining power class fields¶
Each confirmed (code) at the reader named (the class is the one
Human_GetClassRecord picks, 0x00222b78):
| Field | Reader | What it does |
|---|---|---|
s16 +0x32 |
Human_ApplyPendingDamage (0x00265f70) |
after a hit lands on an AI human (brain kind not 0), its record +0x108 = game time + +0x32 ms; until then Human_IsBusy (0x00228258) says busy, which about 40 AI routines test (spotting, reporting a crime, joining a fight, grabbing). A player is never marked |
s8 +0x38 |
Human_GetClassReactFlag (0x002233c8) in Human_PlayReaction (0x0026a6d0) |
the most ground strikes a downed human reacts to: while a reaction clip plays (Human_IsInReactionClip 0x00228488: held 0x8000, a clip with flag 0x80, or 199) and his record +0xbe is at least this, a new reaction is skipped. +0xbe counts the ground-strike reactions 195 played (Human_PlayReaction, Hit_PlayBegReaction 0x00269b80) and is cleared when he gets up (Human_EndDownState 0x0025e250) |
bytes +0x3a-+0x3f |
Human_ApplyClassFlags (0x00222c98), from Human_Init, Human_MakePlayer and Human_ReleasePlayer |
switches copied into the human flags +0xe0 (Combat: human flags): +0x3a = 1 sets 0x80 (reaction strength at most 1), else clears it; +0x3b 0x1000000000 (hard to stun, below); +0x3c 0x2000000000 (never escapes when the grabber's power runs out); +0x3d 0x800000000 (reader not traced; HuSetBlockFromReact sets the same bit); +0x3e = 1 sets 0x200000 and clears 0x400000, −1 sets 0x400000 and clears 0x200000, 0 clears both; +0x3f = 1 sets 0x400 and clears 0x200, −1 sets 0x200 and clears 0x400, 0 clears both |
byte +0x41 |
Human_ClassCanFlee (0x00224ea0) in Brain_Think (0x0028f6c0) |
an AI with a fight goal and nothing in hand draws a weapon: when free, it clears its actions and plays 477 ANIM_ONE_HANDED_OBJECT_DRAW (GenToIdleAnimAction). A cop (brain kind 1) does so in or out of a fight, unless his type's weapon (CfgChar +0x18c) is none. The clip's object event named random (Anim_FireFrameEvents 0x00101dd8) calls Brain_GiveHandObject (0x0028c780), which puts the type's weapon in his hand, or dyn_swhbld (a switchblade) when it is none. The name "can flee" is an old guess: the byte is "draws a weapon" |
byte +0x42 |
Human_ShouldFleeHurt (0x00224ec8) in Brain_Think |
an AI with a fight goal whose health is under 30 % plays 665 ANIM_SPECIAL_FLASH (blend 0.1 s in and out) when free, after clearing its actions: an AI's flash. The name is likewise a guess the code does not bear out |
Human flag 0x1000000000 (power class +0x3b, or HuSetHardToStun) changes the stun rule of Human_PlayReaction:
without it a hit stuns when its Anim Range List flag 0x400 is set or the caller asks; with it neither counts, and
the hit stuns only a human already stunned on his feet (Human_IsStunnedStanding). Flag 0x100 (never stunned) is
tested first. Confirmed (code) at 0x0026a6d0. Who reads 0x400000 and 0x800000000 in the human flags was not
found (the state and held flags of the same values are other words).
Human_StaminaMax (0x00223188) returns +0x2c; for a player, when unlockable (6, 12) is unlocked, it returns
+0x2c × (1 + b × 0.01) rounded, where b is byte 3 of CfgWarriorUpgrade's record (0x006b6650). The power
meter's maximum (0x00223068) is +0x28, likewise raised by byte 2 with (6, 11), then times +0x18 while the
human is hurt. Confirmed (code); the unlocks: Unlockables.
Which class a human has (Human_Init, 0x00218008; confirmed (code)): +0x1b8 and +0x1b9 both take the
type's CfgChar byte +0x11d; for a type of category 14 (the Warriors) +0x1b9, the class it plays with as a
player, is then replaced by 0x00222ba8's by type: 58 Ajax (types 11-14), 59 Cleon (1-4, 189), 60 Cochise (15-17),
61 Cowboy (18-20, 188), 62 Fox (21-25), 63 Vermin (26-29, 191), 64 Rembrandt and Ash (30-32, 38-40), 65 Snow (33-37,
190), 66 Swan (5-10), 7 for any other. The Warrior class (+0x1ba, 0x00222c20) follows the same groups: 0
Ajax, 1 Cleon, 2 Cochise, 3 Cowboy, 4 Fox, 5 Vermin, 6 Rembrandt and Ash, 7 Snow, 8 Swan, 9 anyone else. Each
difficulty script sets every class again and re-runs CfgChar for 63 types. Every value:
Power classes, Warrior classes.
Character classes¶
CfgChar (in config_preload2.lua) fills a 0x1ac-byte record per type at 0x00684620 + id × 0x1ac: 45 floats from
+0x00, 16-bit values from +0xb8, the model index by name at +0x112 and by "<name>_a" at +0x114 (used in
levels 60-64), the speed class byte at +0x11c, and four strings (32 bytes each) from +0x14c. Rembrandt's call is
CfgChar(32, ..., 1800, DamageFox, Att_Warrior, 1, "warr_re_cv", "none", 7, 0, RangeNormal, "none", 0, "none"); type
40 (Ash) uses warr_ty_cv. Confirmed (code) for the layout; the arguments inferred from the disassembly.
Human_Init remaps the type: 32 becomes behaviour class 30 (0x1e) with a variant flag (confirmed (code) at
0x00218008, runtime +0xcc = 0x1e, +0xd0 = 0x20).
The fields with a known reader besides the tables above, confirmed (code) at the addresses given:
- The range table (45 floats at
+0x00, the script's numbers ÷ 100, so metres): read once per human byHuman_AttachInstance(0x00217a98) when its model is attached, beside the damage table. For each attack kind k (the index of the damage and attack tables) whose value is above 0,Attack_CallKindHandler(0x002545e0, jump table0x0055d640, kinds 0-41) writes it as the far range (value × 1000, mm; Anim Range List+0x08) of that kind's anim ids withAnimRange_SetFarRange(0x002545a8). A 0 leaves the anims' own far range. The ids per kind: 0 → 11; 1 → 12; 2 → 13; 3 → 15; 4 → 14; 5 → 16; 6 → 17; 7 → 19; 8 → 18; 9 → 20; 10 → 25-30; 11 → 21; 12 → 193; 13 → 194; 14 → 237-240; 16 → 653, 655; 17 → 657, 659; 19 and 20 → 0; 21 → 3, 4; 22 → 70, 71; 15, 18 and 23-44 none. The far range is whatHuman_CanStartAttackand the steer use (Combat); kind 22's ids 70, 71 are the grab's search and kind 21's 3, 4 the tackle's. The list written is the character data's (record+0x160points at the model's own list,HumanRecord_SetAttackTable0x00254568), so every human with that model shares the values the last one attached wrote (inferred from the pointer; the damage table has the same reach). - The voice (s16
+0x118): human+0x3b0, the foldervags/character/voices/<n>/(format string at0x0055a548,l<level>_c<checkpoint>_idle_<nn>); type 259 instead takes a random 120-125.Human_Initcounts the type's idle lines 1-3 there (+0x197,+0x196set when any exists).+0x3b4is the level's variant (Anim_LevelVariant) or the voice. So the binding page's "Warrior index" was a misreading: Rembrandt's 7 is his voice folder. - The weapon (string
+0x18c): when it is notnone,Human_Initputs that object type in the new human's hand (Human_PlaceItemInHand(h, name, 1, 1)0x0024c280, confirmed (code): made by name, recorded as a pinned object, refused when the hand is already busy with held flag0x4000). The police types carrydyn_baton. A cop who later has nothing in hand draws it again, and an AI of a power class with byte+0x41draws it, or a switchblade when it isnone(Power class fields).
Body scale¶
Human_Init ends by sizing the human (confirmed (code), 0x00219040-0x002191fc), stored by Human_StoreScale
(0x00219608, which also rescales the physics body):
- A random size, only for a human whose brain type is not 3 (a Warrior), whose class category (
+0x11b) is not 12 or 13, and whose type (+0xcc) is none of 122, 221, 222, 303-305, 307, 351, 414-416, 425, 458, 459. Withr=Random_Int(99)(0-99, stream0x006eb880) and "hat" = the type's hat (+0x14c) is notnone: - a woman (
flag_14b, human+0x3b8, set): 1 − 0.01 × (r/ 25), so 1.0, 0.99, 0.98 or 0.97, a quarter each; with a hat at least 0.99; - anyone else:
r0-30 → 1.0; 31-54 without a hat → 1.02 for a cop (brain type 1) or a gang member (brain type 2) whose brain+0x20crecord's+0x2cis below 23, else 0.98; otherwise (31-54 with a hat, or 55-89) → 1.01 whenr< 90, else 1.0. - The type's fixed size (
Human_ApplyTypeScale,0x00219638, also run byHuman_AttachInstance; confirmed (code)) replaces it for these types (+0xcc): 5 → 1.02; 18, 30 → 0.97 (so Rembrandt's 0.97, as seen at runtime); 21, 23, 24 → 0.98; 33 → 1.03; 89 → 1.16; 119, 120, 135 → 1.2; 128 → 1.175; 161 → 1.11. With game state+0x158set (co-op), 89, 119, 120, 128 and 161 take 1.1 and 135 takes 1.03.
Human_GetScale (0x0021d020) returns it, except 1.0 in a scene for a scale under 1.1. Confirmed (code).
Stats¶
Everything that makes one character stronger, tougher or faster than another comes from four configuration tables, all set by the configuration scripts and again by each difficulty's (the boot set equals normal's). The sections above and the pages they link hold the detail; this is the one list. "Human" is the field the value lands in when a human is made or becomes a player; the evidence is as the cited page gives it. Every type's values on normal: Characters (its Stats (normal) column joins the four tables).
1. The character type (CfgChar, record 0x00684620 + type × 0x1ac, Character classes; confirmed
(code) at CharClass_Get, 0x00228800); Human_Init (0x00218008) reads it at creation, +0x11b and +0x11d from
the type's own record and the rest after the remap from the class's:
| Stat | Field | Human | What it does | Evidence |
|---|---|---|---|---|
| Behaviour | byte +0x11a |
brain type | the AI's brain kind (AI: types) | confirmed (code) |
| Category | byte +0x11b |
(read from the record) | 14 the Warriors: a player takes his Warrior's power class; 13 the bosses and big fighters; the money class and the civilian reactions (Starting money) | confirmed (code) |
| Speed class | byte +0x11c |
record +0x164-+0x17c |
the speed class, a fallback the clips' own speeds replace in play | confirmed (code) |
| Power class | byte +0x11d |
+0x1b8, +0x1b9 |
below; a Warriors player's +0x1b9 is his Warrior's |
confirmed (code) |
| Health | s16 +0x116 |
record +0x144 / +0x146 |
health and its maximum (1800 for every Warrior, 600 for the street civilian) | confirmed (runtime) (Combat: damage) |
| Damage table × damage scale | s16 × 45 at +0xb8 |
the human's Anim Range List | written over the list's damages when the model is attached (0x00217a98); a player's first times his Warrior class's damage % (Damage) |
confirmed (code), confirmed (runtime) |
| Attack table | byte × 45 at +0x11e |
brain +0x298 |
an AI's weight for each attack kind (AI) | confirmed (code) |
| Range table | float × 45 at +0x00 (÷ 100) |
not traced | no reader found | not traced |
| Hat | string +0x14c |
the hat object | worn from creation (Hats) | confirmed (code) |
| Voice | s16 +0x118 |
voice set | the folder of the type's speech (Characters reference; the binding page calls it a Warrior index) | inferred |
flag_14b |
byte +0x14b |
+0x3b8 |
a civilian woman's tag colour (Tagging), the _female animation sounds (Sound events), the money record 16 for category 4 (Starting money); the body scale +0x65c = 1 − 0.01 × n is read beside it, n not traced |
confirmed (code); the scale not traced |
| Drop group, drop chance | string +0x16c, int +0xb4 |
+0x257, +0x278, money +0x370 |
the carried object and its drop chance, and the starting money (Starting money) | confirmed (code) |
| Weapon | string +0x18c |
not traced | read by Human_Init; what it gives is not traced |
not traced |
2. The power class (CfgPowerClass, record 0x006619a0 + class × 0x44, Power classes), by
+0x1b9 for a player and +0x1b8 otherwise (0x00222b78). A player of a Warriors type plays with 58-66 by Warrior
(7 without one); everyone else with the type's own class. Confirmed (code) at each reader:
| Stat | Field | What it does |
|---|---|---|
| Power meter | s16 +0x28, +0x2a |
maximum and refill per second (record +0x148); × +0x18 while hurt (0x00223068); with the gym's power upgrade a player's maximum × (1 + byte 2 / 100) |
| Stamina | s16 +0x2c, +0x2e |
maximum and refill per second (record +0x14a, Human_StaminaMax 0x00223188); with the gym's stamina upgrade a player's maximum × (1 + byte 3 / 100) |
| Hurt threshold | float +0x04 |
below this share of maximum health the human is hurt (0x00222ff8) |
| Stun, down | s16 +0x30, +0x34; floats +0x10, +0x14 |
a stun's and a knockdown's length, ms, and their factors while hurt (Reactions) |
| Weapon stun | float +0x00 |
as the attacker, scales the victim's stun for weapon hits of 20-49 damage (0x0022f658) |
| Grab struggle | byte +0x36 |
the grabbed human's strike costs the grabber 1 / n of its power (0x0027fd68) |
| AI: block, counter, attack delay | floats +0x08, +0x0c, +0x24, +0x1c, +0x20; bytes +0x37, +0x39 |
the AI's block chances, counter chance, attack delay factors, pattern-reading threshold and throws at walls (AI) |
| Health bar | byte +0x40 |
the pause Stats screen's Health bar (+0x40 / 100, Stats screen); the health rings' span (HUD) |
| Not traced | +0x32, +0x38, +0x3a-+0x3f, +0x41, +0x42 |
stored; no reader found |
3. The Warrior class (CfgWarriorClass, record 0x006b65c0 + class × 14, human +0x1ba by type: 0-8 the nine
Warriors, 9 everyone else), read through 0x00222b58. Confirmed (code) at each reader
(Warrior classes):
| Stat | Field | What it does |
|---|---|---|
| Rage | s16 +0x00; bytes +0x02-+0x04 |
the rage maximum, the gain percentage, and the idle decay and the drain while raging, percent of the maximum per 20 s (Rage) |
| Health % | byte +0x05 |
when the human becomes a player (Human_MakePlayer 0x00229c40) his health and maximum are times n %, rounded; divided back when he stops being one (0x0022a2a8). Rembrandt: 1800 × 50 % = 900 |
| Damage % (Strength) | byte +0x06 |
a player's attack damages times n % (Damage); the Stats screen's Strength bar |
| Context games | bytes +0x07-+0x0c |
the cuffing hold, the uncuff mash, tagging, lock picking, the stereo theft and mugging |
4. The gym upgrades (CfgWarriorUpgrade, bytes 0x006b6650-0x006b6653; 10, 20, 10, 20), each only while its
unlockable is unlocked (Unlockables): punch damage + 10 % (6, 5), kick damage + 20 % (6,
6), the power maximum + 10 % (6, 11), the stamina maximum + 20 % (6, 12). Confirmed (code)
(CfgWarriorUpgrade).
The pause Stats screen's bars read these: Strength from the damage %, Stamina from the power and stamina maxima
(upgrades included), Health from the power class's +0x40, Rage from the rage maximum / 150. The formulas are on
Pause: Stats screen. "Strength" is not a field of its own: it is the Warrior class's damage %.
Coney's implementation¶
Coney reads the four tables from the configuration calls the scripts make (characters::CharacterTypes,
ai::aiConfigFrom()), on the one difficulty it runs: the boot set, which is normal's. A human gets its type's stats
when it is made (ai::AiHumans::spawn(), from the cast of a level's scripts) and a player when he is made or changes
type (human::playerClassOf(), human::playerProfileOf()):
| Stat | Applied? | Where | Test |
|---|---|---|---|
| Behaviour | yes, AI | ai::aiConfigFrom() → brain type |
tests/ai/disc_ai_config_test.cpp |
| Category | yes: boss class, a Warriors player's power class, the money class | ai::FighterClass::category, characters::powerClassOf(), characters::moneyCategory() |
tests/characters/character_types_test.cpp, tests/characters/starting_money_test.cpp |
| Speed class | no (a fallback; Coney uses the clips' speeds as the original does in play) | ||
| Power class (AI and player) | yes | play_level_cast.cpp (the type's +0x11d), characters::CharacterTypes::playerTraitsOf() |
tests/characters/character_types_test.cpp, tests/platform/disc_change_character_test.cpp |
| Health (AI) | yes | ai::FighterClass::health |
tests/ai/brain_test.cpp |
| Health (player) × Health % | yes | characters::PlayerTraits::health, human::playerProfileOf() |
tests/characters/character_types_test.cpp, tests/platform/disc_change_character_test.cpp |
| Damage table × scale; player × Damage % | yes | characters::parseCfgChar(), human::Human (its own Anim Range List) |
tests/characters/character_types_test.cpp, tests/combat/anim_ranges_test.cpp |
| Attack table | yes, AI | ai::FightSettings::attackWeights |
tests/ai/fight_goal_test.cpp |
| Range table | no: not traced | ||
| Hat | yes | platform/play_level_hats.cpp |
tests/world_objects/hats_test.cpp (the fittings; the creation path has none) |
| Voice | yes | script::voiceSetOfType() |
tests/audio/game_sound_test.cpp |
flag_14b |
the tag colour and the female sounds; not the body scale (every human has the player's 0.97) | play_level_cast.cpp, the sound events |
tests/audio/human_sound_events_test.cpp |
| Drop group, drop chance, money | yes | characters::rollStartingCarry() at HuCreate |
tests/characters/starting_money_test.cpp |
| Weapon | no: not traced | ||
Power meter (+0x28, +0x2a) |
yes, every human (was the player's 400 and 60 for all before) | combat::PlayerCombat from human::FighterProfile::powerClass |
tests/human/human_test.cpp, tests/platform/disc_change_character_test.cpp |
Hurt power factor (+0x18) |
yes | combat::PowerMeter::setHurt() |
tests/combat/meters_test.cpp |
Stamina (+0x2c, +0x2e) |
yes, every human (was the player's 135 and 40 for all) | human::Stamina, human::Human::spawn() |
tests/human/stamina_test.cpp, tests/human/human_test.cpp, tests/platform/disc_change_character_test.cpp |
| Hurt threshold, stun, down times | yes | human::Victim, human::Fighter::hurt() |
tests/human/being_hit_test.cpp |
Stun and down factors while hurt (+0x10, +0x14), weapon stun (+0x00) |
no | ||
Grab struggle (+0x36) |
yes | human::Fighter |
tests/combat/grabbed_test.cpp |
AI block, counter, delays, walls (+0x08, +0x0c, +0x1c, +0x20, +0x24, +0x39) |
yes | combat::PowerClass, the brains |
tests/ai/fight_reactions_test.cpp, tests/ai/brain_test.cpp |
AI pattern-reading threshold (+0x37) |
no | ||
+0x40 (health rings) |
yes: read from the configuration now (was Rembrandt's 35 for every class) | characters::parseCfgPowerClass(), play_level_world.cpp |
tests/characters/character_types_test.cpp, tests/hud/health_rings_test.cpp |
| Rage (max, gain, decay, drain) | yes, the player's Warrior class (was class 6's for every player) | combat::RageClass, combat::RageMeter |
tests/combat/meters_test.cpp, tests/human/human_test.cpp, tests/platform/disc_change_character_test.cpp |
| Health % given back on release | no: Coney's hand-over is a stand-in that keeps one player human (hand-over) | ||
| Lock pick, tagging, mash bytes | yes | scripting/object_bindings.cpp |
tests/scripting/object_bindings_test.cpp |
| Cuffing, stereo, mugging bytes | no: those games are not implemented for a player | ||
| Gym upgrades | no | ||
| Stats screen | no: the pause menu's Stats item is not implemented |
Coney's readings: the player's health is read from the type's own +0x116 (the page does not say which record
Human_Init takes it from; every Warriors alias has its class's value), rounded to the nearest; a configuration with
no CfgWarriorClass leaves the health unscaled and the rage at class 6's.
Hats¶
CfgHat(set, type, hat, {x, y, z}, {i, j, k, r}) (Cfg_AddHatFit, 0x00228d70) fills a hat-fit set: a record of
0xa90 bytes at 0x00662b70 + set × 0xa90 with 48 slots of a 32-byte transform (the offset with w = 1, then the
rotation) from +0x00, the 48 hat names (24 bytes each) from +0x600, and the owning character type at +0xa80; a
hat goes into the first slot still named none. Placing a worn hat (0x003a3ba0): for a human whose class has brain
kind 3 (+0x11a, the Warriors), the set owned by the type it was created as (+0xd0) if there is one, else by its
class (+0xcc), and in it the slot named as the hat's object type (0x00228f40 for the offset, 0x00229028 for the
rotation); a hat in no slot of the set gets a default transform (0x005116c0). Every other human uses the hat
model's own attach point (0x00391828, 0x00391880). Confirmed (code). The 908 fittings:
Hat fittings.
Where a hat comes from. The character class's string at +0x14c (CfgChar's
argument 11, none for most classes) is the hat object's type name: when the human's character instance is
attached (Human_AttachInstance 0x00217a98, at creation or when the model streams in later) and that name is not
none, Human_SpawnHat (0x0024bfc0) creates the object by name at the human, adds it to the world's object list
pinned, stores its handle at +0x364 and sends it message 0x32 (wear) with attach point 6, a zero offset, an
identity rotation and whether the human is in a scene. HuPlaceHatOnHead (0x002385f8) goes through the same
function. Confirmed (code). So a dealer with no script hat still wears his class's hat when his class names one.
On the head. The hat's 0x32 handler (HatObject_Wear, 0x003e77c0, Script types)
zeroes its velocity and spin, attaches it (Obj_Attach) to bone 6, the head (the head strike sphere's bone,
Combat), clears its flags 0x6000000, and writes its local transform in that bone's frame:
the position is the fit offset (Human_PlaceHat(…, 0xb), the set's or the model's, above) times the human's
scale (Human_GetScale), the rotation the fit rotation (Human_PlaceHat(…, 0xc)), unscaled; then it tells the
human (message 3). Confirmed (code). That the attach composes the head bone's matrix with this local translation
and then rotation (bone × T × R), as every attached object does, is inferred.
Knocked off (Human_KnockOffHat, 0x00258330), confirmed (code):
- Never while human flag
0x10000000000is set (HuSetKeepHat), without a hat, or when the hat's object kind (+0x86) is 27. - Who calls it: every knockdown reaction (
Human_PlayReaction0x0026a6d0), except for a brain of type 1, class0x80and class 13; health running out (0x002677c8); being mounted (Pair_LinkMount0x0022c0a0); the human's messages 3 and0xc2(not for brain type 1);Human_ResetToOneHealth,Human_ScriptDrop, the hat object's destruction (Obj_Destroy), class0x80going down (0x00257ef0), and picking up another hat (kind 24) in the pick-up search (0x0024d810). Ordinary hits that do not knock down leave it on. - The throw: by human
+0x668& 3 (inferred: the side the blow came from) it picks a direction d and a spin axis a from the unit x and y axes (0: d = +y, a = +x; 1: +x, −y; 2: −y, −x; 3: −x, +y), sets the hat's spin (vtable+0x7c) to a × 8 rad/s and sends it message0x1c(drop) with d × 2 m/s. The drop (HatObject_Drop0x003e7958) turns that velocity by the wearer's orientation (inferred from the cross products), detaches the hat at its world pose (Obj_Detach), gives it flag0x4000000(and0x80for three models) and state 2, updated every 2 ticks: from here it is a loose physics object that falls and lands (Script types).+0x364is cleared. - Afterwards it stays where it lands, and may be picked up again. Only when the game state's
+0x35cis 1 (a mode flag, not traced) does it get a respawn timer, by model hash (+0xc4): none for the Warriors' hat0xbbbef927; 8000 ms for a player's0x50e8de39or0x17d7194b; none for the other0x331fd29,0x50e8de39,0x17d7194band a player's0x1d5ee290; the game state's+0x26cfor every other hat. A wearer of brain type 1 or 2 marks the hat's+0x10bwith its type.
From a type to a model¶
How HuCreate's type picks what is drawn and animated, confirmed (code) at 0x00218008 (Human_Init),
0x00228af8 (Cfg_SetCharacterClass, CfgChar's writer) and 0x001780e8:
CfgCharresolves the model name once, whenconfig_preload2.luaruns: its model-name argument (warr_re_cvfor type 32) is hashed (CRC-32 of the lower-case name, Name hashing) and looked up in the Character List (the resource manager's+0x88, the chunk0x44records of Files); the record's index is stored at+0x112(0xffffwhen the name is not in the list). The same is done for"<name>_a"into+0x114.-
Human_Initmaps the type to a class with a fixed switch. Types not in it are their own class. A type marked variant below sets a flag; the others are plain aliases of their class:Class Plain aliases Variants 1 2 3, 4 5 6 7, 8, 9, 10 11 ( 0xb)12 13, 14 15 ( 0xf)16 18 ( 0x12)19 20 21 ( 0x15)22 25 26 ( 0x1a)27 28, 29 30 ( 0x1e)31 32 33 ( 0x21)34 35, 36, 37 38 ( 0x26)39, 40 41 ( 0x29)42 43 ( 0x2b)44 45 ( 0x2d)46, 47, 48 221 ( 0xdd)222 -
The model index (kept at
+0x37c): the type's own record when the human is not a player (player index below 1) or the type is a variant; otherwise (a player created as a plain alias) the class's record. Of that record it reads+0x114(the_amodel) when the current level's number is 60-64 (the Armies of the Night bonus levels), else+0x112. So a player made as type 2 (warr_cl_gen) is drawn as type 1'swarr_cl, while type 32 keepswarr_re_cv. - The rest of
Human_Initreads the class record:+0xccholds the class from here on, and the fields read after the remap (+0x118,+0x11a,+0x14b,+0xb4, the strings at+0x14cand+0x18c) come from it; only+0x11band+0x11dare first read from the type's own record.+0xd0keeps the type as given. Whether the later systems (combat, health) read the class or the type is not traced here. - The files: the Character List record at the model index names the three resources (character data with the
animations, model, texture dictionary, Files);
Human_Initmakes the instance at once when they are resident (0x001774d0,0x00177b80, thenHuman_AttachInstance0x00217a98; confirmed (code)), else the resource manager loads them later (Creation). The anim set is that character data over the generic defaults (its 722 slots, Files) and the anim slots, the same for every type until a movement style changes them.
For an implementer with Coney's recorded CfgChar calls and its CharacterList: find the type's call, apply the
alias rule for a player, take the model-name argument (or <name>_a in levels 60-64) and look it up by name. The
character reference lists every type's model name.
Speed classes¶
CfgSpeedClass(class, ...) writes six floats at 0x006b6548 + class × 0x18. The getters read entry +0x00 (base),
+0x08 (walk), +0x0c (jog), +0x10 (run) and +0x14 (sprint); +0x04 has no getter found. Values from
config_preload2.lua (inferred from the disassembly):
| Class | +0x00 | +0x04 | walk | jog | run | sprint |
|---|---|---|---|---|---|---|
| 0 | 3 | 0.8 | 1.6 | 4.1 | 6.5 | 8 |
| 1 | 3 | 0.8 | 1.6 | 4.1 | 7.5 | 9 |
| 2 | 3 | 0.8 | 1.6 | 1.6 | 1.6 | 9.75 |
| 3 | 3 | 0.8 | 1.6 | 4.1 | 7.5 | 7.5 |
| 4 | 3 | 0.964 | 0.964 | 0.965 | 2.723 | 2.723 |
But these are not used in play. When 0x005101e0 is set (it is 1 at runtime), each getter returns the human's
own value from its 0x180-byte record times +0x3a4 instead (confirmed (code) at 0x00221710, 0x002215d0): +0x164
for the base, +0x170 for walk, +0x174, +0x178, +0x17c for jog, run and sprint.
The speeds come from the clips. 0x00254078 (called when the character is attached, Human_AttachInstance
0x00217a98, and after a movement-style change, 0x00243848, 0x00253688) computes each one from the clip in an
anim slot as the clip's horizontal root displacement over its playing time:
sqrt(dx² + dy²) / (duration / rate), with the displacement from the clip's descriptor (0x00101950), the duration
from 0x00101a00 and the rate from 0x00104a38. Confirmed (code). It writes, in the 0x180 record:
| Record field | Speed | From slot | Rembrandt (runtime) |
|---|---|---|---|
+0x164 |
base (combat walk) | 14 | 3.429 (clip 380) |
+0x16c |
sneak walk | 3 | 1.585 (407) |
+0x170 |
walk | 4 | 1.629 (408) |
+0x174 |
jog | 5 | 4.857 (409) |
+0x178 |
run | 6 | 7.801 (410) |
+0x17c |
sprint | 7 | 10.245 (411) |
Each runtime value equals the clip's displacement divided by its duration as read from the disc, confirmed (runtime). The speed class table is therefore only a fallback.
Gait from speed (0x0022aeb0, the gait stored with the velocity): below 0.5 m/s gait 0; otherwise the gait 1-5
whose speed (+0x16c, +0x170, +0x174, +0x178, +0x17c) is nearest. Confirmed (code); a jump table at
0x0055bec0. It scans from gait 5 down and stops at the first gait whose next lower one is strictly farther, so a
tie goes to the lower gait. With Rembrandt's speeds (above, +0x3a4 = 1.0) the bounds are the midpoints: gait 0
below 0.5, 1 below 1.607, 2 below 3.243, 3 below 6.329, 4 below 9.023, 5 above. So the combat
walk at 3.429 m/s is gait 3 (1.428 from the jog, 1.800 from the walk), the free walk at 1.629 is gait 2 and the
run at 7.801 gait 4. Confirmed (code) at 0x0022aeb0; the bounds follow from the runtime speeds.
Anim slots¶
The 0x180 record holds the human's anim slots: 35 anim ids at +0x28-+0xb3, copied from the default table at
0x005105d8 (0x00253608) and changed one by one by 0x002535f0(human, slot, id). A player gets slot 14 = 380
(player combat walk). 0x00253688(human, style) swaps in a movement style's ids (cases 1-20, a jump table) and then
recomputes the speeds. Record +0x20 is the anim id playing (getter 0x002266b8). Confirmed (code); Rembrandt's ids
were read at runtime and named from the clips on the disc (confirmed (runtime)).
Slots count from 0, with no offset or remapping: 0x00253608 copies the table's 0x8c bytes (35 words) to record
+0x28 as they are, and 0x002535f0 stores slot s at +0x28 + 4s. The player's slot 14 is the table's fifteenth
word (372 replaced by 380), and the movement styles write by the same numbers: styles 4 and 6 put their own "run stop"
ids (523 ANIM_BARREL_MOVEMENT_RUN_STOP, 569 ANIM_GHETTO_MOVEMENT_RUN_STOP) in slot 33, where the default is 417
ANIM_MOVEMENT_RUN_STOP. Confirmed (code) at 0x00253608, 0x002535f0 and 0x00253688. The last slot, 34, holds 0
in the table and no style writes it; whether 0 there means anim id 0 (ANIM_RUNNING_ATTACK_CHARGE) or "no clip" is not
traced (the anim id reference treats it as unset).
| Slot | Id | Clip | Slot | Id | Clip |
|---|---|---|---|---|---|
| 0 | 388 | neutral idle | 18 | 212 | mounting strike |
| 1 | 401 | step forward | 19 | 193 | grounded strike |
| 2 | 403 | dash forward | 20 | 104 | grab front attack |
| 3 | 407 | sneak walk | 21 | 605 | block start |
| 4 | 408 | walk | 22 | 608 | block high front |
| 5 | 409 | jog | 23 | 606 | block sustain |
| 6 | 410 | run | 24 | 607 | block shuffle |
| 7 | 411 | sprint | 25 | 427 | jump start |
| 8 | 390 | slow turn right | 26 | 428 | drop cycle |
| 9 | 412 | sneak walk start | 27 | 429 | drop land |
| 10 | 413 | walk start | 28 | 388 | neutral idle (again) |
| 11 | 358 | combat idle | 29 | 653 | special attack 1 front |
| 12 | 366 | combat shuffle forward | 30 | 434 | jump loop |
| 13 | 368 | combat dash forward | 31 | 668 | special action (missing_anim_filler in the generic data) |
| 14 | 372 (player 380) | combat walk forward | 32 | 317 | fire idle |
| 15 | 360 | combat turn | 33 | 417 | run to neutral (run stop) |
| 16 | 12 | attack S1 | 34 | 0 | none, or id 0 (above) |
| 17 | 11 | attack X1 |
Slots 16-24 and 28-33 are named from the ids' ANIM_* constants in royal.lua and their generic clips (inferred:
what each slot is used for has not been traced, only its default id). The full table, with every id's clips, is the
anim id reference.
Other locomotion ids seen at runtime outside the slots: 414 run start, 418 run 180° turn, 421-426 fall front / back begin, cycle and land.
Files of a character¶
A character is three resources, each a file named by the decimal CRC of a hash ("%u", 0x00551ed8) and loaded by the
resource manager (Chunk system). The resource types and their loaders are confirmed (code); the
chunks each file holds are inferred from the chunk handlers on that page:
- the model (resource type 3, loader
0x001783d0, confirmed (code)): chunk0x47with the RenderWare clump, then chunk0x28with the bones' bind offsets (34 × float4, 544 bytes); - the texture dictionary (type 4,
0x0018e9e0, confirmed (code)): one chunk0x2a; - the character data (type 5,
0x0016e8f0, confirmed (code)): the animations (a0x00keyframe chunk and a0x02descriptor for each, Animation), one Anim Range List (0x45) and one Character Data chunk (0x08).
The Character List (chunk 0x44 in warriors.glr, 543 records of 32 bytes) finds them by model name:
| Offset | Meaning |
|---|---|
+0x00 |
CRC-32 of the model name, lower case, no path prefix (Name hashing) |
+0x04 |
hash naming the character data resource |
+0x08 |
hash naming the model resource |
+0x0c |
hash naming the texture dictionary |
+0x10 |
the character data's size |
+0x14 |
the model's size |
+0x18 |
a size close to the model's (+0x14 plus 0x1df in the cases checked; meaning not traced) |
+0x1c |
the texture dictionary's size |
Confirmed (code) for +0x00-+0x0c (the loaders' lookups); the sizes are inferred from matching the WAD entries.
The clump, the same for every skinned character on the disc: one atomic, 33 frames, an HAnim hierarchy (0x11E)
of 32 bones with key size 36 and flags 0, geometry in PS2 native format (0x010200f3, two texture coordinate sets)
with the extensions 0x50e, 0x510 (native data) and 0x116 (Skin, PS2 platform 4). Plugins:
Graphics. Corroboration (disc survey).
Character Data (0x08, 2,912 bytes for Rembrandt; handler 0x0016e258, vtable 0x005389a0): a placeholder vtable
word, then 722 slots at +0x08, one per anim id, then 16 bytes. A slot of 0xffffffff means "use the default",
taken from the table at resource manager +0x70; a slot value n picks the n-th animation loaded with this
resource, counted on the stack of objects the chunk system pops last-in first-out. CharacterInstance_GetAnim
(0x00175080) answers an id with this table. For Rembrandt, id 408 is walk and 413 walk-start; ids 11 and 12 are
combo attacks (inferred from the clip names). The handler keeps the Anim Range List at +0xb50 / +0xb54. Confirmed
(code) for the slot rule; the id meanings inferred.
Anim Range List (0x45, 11,568 bytes): a count word, then 722 records of 16 bytes, one per anim id, of the form
(s16, s16 = 1000, f32, s16, s16, s16, u16): offsets, reach, ranges and rate flags (Animation).
Example, Rembrandt (warr_re_cv): character data 0xe72f9fb5 (200,640 bytes, shared with warr_re: 36
animations, one 0x45, one 0x08), model 0xdb1cdf36, textures 0x46af47d8. Corroboration (disc).
Disc survey (counts only): 153 distinct skinned models, all with 33 frames, 32 HAnim bones, key size 36, a PS2 skin and a 544-byte bone offset chunk; 1,209 Character Data and Anim Range List chunks.
Character geometry¶
What the model's bytes hold beyond the clump's sections, found by Coney's decoder and its disc test over the 128 models the Character List names (no code was read for this subsection, so it is corroboration (disc) unless marked).
- The native geometry is the world's PS2 layout (Graphics) with a fifth slot:
STCYCL 5,1and fiveUNPACKs per batch, slot 0 positions (V4_16), slot 1 texture coordinates (V4_16, two sets), slot 2 colours (V4_8unsigned, all zero in the characters checked), slot 3 normals (V4_8) and slot 4 the bone weights (V4_32, format0x6c). Vertex counts match the mesh plugin's in every model. - A weight is a float whose low 10 bits are replaced by
(node index + 1) << 2, or 0 for an unused slot; the weight is the float with those bits cleared. The same encoding as librw's PS2 skin reader. - One mesh, one material, colour
0x969696ff, and the material is not textured (no texture section): every character's texture dictionary holds exactly one texture (507 dictionaries), named after the character in 427 of them. The game binds it to the material at each draw (Drawing a human). - The skin's inverse bind matrices are the inverses of the HAnim frames' world matrices (checked to float precision for every model).
- Frames to pose bones: frame 0 is the clump's root, frame 1 carries HAnim id 0 and the hierarchy; node index
equals id on the disc, and the pose bone of HAnim id
nisn + 2(Animation), so pose bones 0 and 1 have no frame. Inferred, from the next point. - Pose axes are the clump's turned +90° about x,
(x, y, z) → (x, −z, y): the bone offset chunk's entries are the frames' translations turned so (bonen + 2against frame of idn), and of the 24 axis rotations this one gives the smallest mismatch between clip-posed joints and the bind skeleton (0.022 m for Rembrandt's walk, 0.054 m for the next best). In pose space z is up and a character faces +y (the walk's root velocity is +y). Inferred. - The parent table differs from the frames in one place: pose bone 3's parent is 1 in
Skeleton_InitParents(0x00101120), while its frame's parent is HAnim id 0 (pose bone 2). Inferred (Coney follows the table). - The float at
+4of the bone offset chunk (entry 0'sy) is 0 in every model.
Movement constants¶
Confirmed (code) at the readers; values from .data.
| Address | Value | Use |
|---|---|---|
0x005102cc |
1/30 s | the characters' step (Humans_Update runs at 30 Hz) |
0x005102e8 |
0.95 | stick magnitude above which a run is allowed |
| (code) | 0.12 | stick dead zone |
| (code) | 24 | speed change per second of step (m/s²), so 0.8 m/s per update |
0x005101b0-0x005101dc |
pairs (player, other): 11° / 1.5°, 16° / 2.5°, 18° / 4°, 18° / 6°, 20° / 12°, 24° / 24° in play | turn limits per update; .data holds 1.5°-24° in both words, and config_preload2.lua's CfgSetTurnRates(11, 16, 18, 18, 20, 24) sets the player's |
0x00510308 / 0x00510304 |
1 / 0 | turn smoothing on (eased) |
0x0051030c |
0.8 | turn step carried over from the last update |
0x00510310 |
2.0 rad in play (CfgTurnRate(true, false, 2.0, 0.8)) |
heading error at which the turn reaches its full rate |
The values "in play" were read in the street save's RAM and match every measured turn (confirmed (runtime), Feel comparison); the setters are confirmed (code) on CfgSetTurnRates.
Behaviour¶
Creation¶
HuCreate(name, type, {x, y, z}, headingDegrees, str, playerIndex, gang, flag) (0x00358428 → 0x00233d60),
confirmed (code):
- Build the rotation about
zfrom the heading (degrees × 0.0174533, half-angle quaternion with the axis at0x00511740). - Take a free slot (
0x00217f08); on failure free it again (0x00217ec8) and returnNilHandle(0x006ebd30). Confirmed (code). Human_Init(0x00218008, confirmed (code)): remap the type to the behaviour class; take the model index from the class record (+0x112, or+0x114in levels 60-64); load or find the character; snap to the ground: cast a ray from the position plus (0, 0, 1) straight down for 2.5 m throughWorldManager_RayCast(Collision) and, on a hit, put the human on it 0.01 above (skipped in game modes0xband0x11).- Name (
+0x80); the player argument goes to the hand-over priority+0x1b1as given; only the value 1 callsHuman_MakePlayer(0x00229c40, Players), whose result is ignored (confirmed (code)).Human_Inithas already set the player slot+0x1b0to -1 and added the human to its gang (Gang_AddMember). - Return the handle (
+0x2cof the slot) and write the snapped position back into the Lua table.
The fifth argument ("warr_sw" in level99.lua) is not read by 0x00233d60. At runtime Rembrandt was created at
(-284.4, 120.4, 0.3) and stood at (-289.03, 120.29, 0.25) once the intro scene ended (confirmed (runtime)).
Loading the files. When the character's resources are already resident (the level's dependency list or the
section's pack holds them), the instance is made at once (0x00177b80) and attached (0x00217a98). Otherwise the
resource manager's update (0x001897a8) loads them later and attaches them then; the dynamic animations a script asks
for (SetDynamicAnimation) go the same way into the slots at +0x3c8. Confirmed (code) for both paths.
Players: making one and giving one back¶
A player is a human in the game state's player list, confirmed (code) at 0x00419b10 and 0x00419c68:
W_GameState + 0x224 (s16) counts the players, +0x228 holds their handles (two at most), and each player's slot
number is kept in the human at +0x1b0. Adding a player (0x00419b10) does nothing when two are already listed;
with no slot asked for it appends, otherwise it shifts the later players up. Removing one (0x00419c68) sets that
human's +0x1b0 to -1, empties the entry and shifts the later players down, rewriting their +0x1b0, so player 2
becomes player 1 when player 1 goes. What the engine calls "the player" (the rings, Warrior commands, the
HuChangePlayerGang test) is slot 0, the handle at +0x228.
Human_MakePlayer(human, pad, panel, slot) (0x00229c40; HuCreate passes -1 for all three), confirmed (code):
- Pick a pad. A given pad is used when that pad record (Front end) is connected
(
+0x4c) and has no owner (+0x42= -1). Otherwise: the pad the front end assigned to the slot this human will take (byte+0x19of one of the two profiles at0x00600840/0x00600844, one per player slot), else the first of the 8 pad records that is connected and unowned. - No pad while someone is already player 1: give up. When no pad was found and the list's slot 0 holds a human, the function returns 0 having changed nothing: the human stays an AI. With an empty list it goes on without a pad.
- Clear every current player's brain enemy list; add the human to the player list (
+0x1b0= its slot). - HUD panel: attach the first free panel of the two (or the one asked for) through
HUD_AttachPlayer(HUD); a panel still attached to another human is not free. The panel index goes to+0x380; the game state's per-panel record (+0x168 + panel × 0x5c) is reset. - Pad ownership: the pad record's
+0x42= the player slot; the human's per-player record+0x19= the pad. - Brain: goals and actions cleared, type 0 (player, AI).
- War chief:
+0x3ac= 1 unless another listed player is already chief (withW_GameState + 0x158set, only one of the same gang kind counts); a chief becomes its gang's leader (gang+0x44). - Difficulty scaling: health (record
+0x144,+0x146) times a percentage, power maxima reloaded, attack damage times another percentage (bytes+5and+6of the record0x00222b58returns, confirmed (code);Human_ReleasePlayerdivides back). - The war chief's default Warrior command 0 to its gang (
WarriorCommand_Dispatch,0x0041c4e0, confirmed (code), AI), the gang's cached player data (0x00166708), physics body flag0x400and a wider body, the human flags of the table above. Returns 1.
Human_MakePlayer does not touch the camera: the follow camera keeps the human it was made with, and the hand-over
below puts the new player on the camera target list.
Human_ReleasePlayer(human, stats, releaseGrab) (0x0022a2a8), the reverse, confirmed (code): (with
releaseGrab) end a grab between it and another player; free its pad (+0x42 = -1); clear the players' enemy lists;
brain type back to its class's (as Human_Init chose it), goals and actions cleared; the per-panel record reset;
(with stats) the stats side; links between it and the players cleared (0x00226e60, 0x00226f70, not traced);
remove it from the player list (the others shift down); hide and detach its HUD panel, +0x380 = -1; per-player
record +0x19 = -1 and +0x1b = 0 (AI-controlled); Gang_AddMember with its own gang; difficulty scaling undone,
the AI flags back. The human stays in the world as an AI member of its gang.
HuChangePlayerGang(gang) (Players_ChangeGang, 0x00239b80), confirmed (code):
- When player 1 already belongs to
gang, return at once. - Every listed player of player 1's gang is released (
Human_ReleasePlayer(h, 0, 1)), its pad remembered, and taken off the camera target list (Camera). Their count is the number of players to hand over (at least 1). - A listed player already in
gangis released and made a player again with a remembered pad, and put on the camera target list. - For each player still owed:
Gang_PickNextPlayer(gang)(0x0022a770, confirmed (code)) takes the gang's member that is not a player with the lowest non-zero+0x1b1, else the first such member (with no member at all, outside level 99 and with+0x158clear, a non-player human of a kind-0 gang);Human_MakePlayer(h, -1, -1, -1)on it; on success it joins the camera target list.
Gang_PickNextPlayer is what makes +0x1b1 matter: in the Warriors' gang Rembrandt (1) is chosen before Ash (2), and
members created with 0 come last.
The level 99 checkpoint 2 hand-over¶
P2.SetupLesson1 creates a new Warriors gang, a new Rembrandt (HuCreate(..., 1, gang)) and a new Ash
(HuCreate(..., 2, gang)), calls HuChangePlayerGang on the new gang, then GangBrFlush and GangDelete on the old
Warriors2 gang (gang 0) that still holds the old Rembrandt and Ash. The script order is inferred from the
disassembly; each step is confirmed (code) as above:
- The new Rembrandt's
HuCreate: the old Rembrandt is still player 1 and owns pad 0. With one controller there is no other connected unowned pad, soHuman_MakePlayergives up at step 2: the new Rembrandt is an AI Warrior with priority 1, and nothing about the player changes. (With a second controller in port 2 it would briefly become player 2 on that pad; the next step makes the outcome the same.) - The new Ash (player argument 2): never a player; an AI Warrior with priority 2.
HuChangePlayerGang(new gang): the old Rembrandt (gang 0) is released and taken off the camera targets; the list is now empty;Gang_PickNextPlayerpicks the new Rembrandt (priority 1), which becomes player 1 on pad 0 (the front end's pad for slot 0), takes HUD panel 0 (just freed) and is added to the camera targets.GangBrFlush(0)clears the old pair's goals and actions.GangDelete(0)destroys the old Rembrandt and the old Ash at once (Destroying a human, AI: deleting a gang).
So the player moves with HuChangePlayerGang, not with HuCreate: an implementation that binds the player on any
HuCreate(..., 1, ...) while a player exists diverges here, and one that ignores HuChangePlayerGang leaves the pad on
a human that is then deleted.
Destroying a human¶
Human_Destroy(human, fromGangCleanup) (0x0021c7c8) removes a human at once, inside the call; nothing is
deferred to a later update. Confirmed (code); in order:
- Its vtable
+0x12c(not traced); for a non-player of a kind-0 gang that is down or dead, its entry in a HUD list of eight (HUD+0x10,0x001a07f0) is removed. - Two effect handles (
+0x168,+0x180) are stopped; the object at+0x364is destroyed (vtable+0x4c). - With
fromGangCleanup(0 fromGangDelete): its gang's message handlers cleared and the gang's tactic told. - The held object (
+0x338) is dropped and then destroyed (vtable+0x4c, asObjDestroy), so a weapon in the hand does not stay on the ground; the two bone objects+0x348/+0x34care destroyed; for a player, the objects its per-panel record holds are destroyed; the object at+0x358is let go with a 120 s lifetime. - A human that still has its instance is knocked out first (
Human_KnockOut) and marked. - The spinning icon (
+0x360) is sent message0x15and the handle cleared (Human_RemoveOverheadIcon,0x00227388, confirmed (code)). - Radar: a blip of the human is removed from both radars (HUD
+0x15d0and+0x3f10,HUD_RadarRemoveHandle,0x001b2b58, confirmed (code), HUD). - A spawner that made it is told (
GangSpawner_OnHumanGone); the instance is released (Human_ReleaseInstance,0x00217dd8, confirmed (code)); its script handlers and its brain are cleared. - A player: the per-panel record is reset, the HUD panel detached (
HUD_DetachPlayer,0x001b22f0, confirmed (code)) and+0x380= -1;+0x1b1= 0; its pad freed (+0x42= -1) and it is removed from the player list (0x00419c68, the others shift down). - The 0x180 record's attachment freed,
+0xd4= 0, the handle freed (the slot can be reused), the physics body released.
It does not take the human off the camera target list (the list holds handles, and a freed one no longer
resolves: inferred from 0x00122248), and it does not choose a new player: destroying the only player leaves the
list empty. HuDelete is a different path (HuDelete).
Where a level puts the player¶
No data file holds a player start. The .lev file's 18 chunks (Level loading)
and the level record (the record) carry none, and nothing in InitLevel places
a player (confirmed (code) for those readers). The level script creates player 1 with HuCreate while
InitLevel runs it, before the preload (Level loading); the values are in the
Level starts reference list, one entry per checkpoint (inferred from the
disassembly of the 29 level scripts that create a player; the list's values are read from the disc).
The checkpoint is W_GameState + 0x33a (16 bits), confirmed (code):
- the game state's constructor (
0x00418588) sets it to 1; SetCheckPoint(n)(0x0037b760→0x0041abc0→0x0041ce98) storesnand tells the inventory, the stats and the world objects (0x0041e0b8,0x00422c60,0x00397e88); scripts call it beforeMenuLoadLevelto enter a level at a checkpoint, and during a mission to mark progress, so a retry restarts there;0x0041cef0(gameState, 1)puts it back to 1 without the bookkeeping: on the paths that restart a mission or leave it (the failure menu0x00155408,0x00155648,0x001557f8, the mission-complete mode'sUpdate0x0015d160, and0x00160d38, which also selects level index 0, the front end);GetCheckPoint(0x0037b798→0x0041abe8) reads it.InitLeveluses it as the section: the pack<level>_<checkpoint>.pakand the intro movie only at 1 (Level loading).
So a level loaded without a SetCheckPoint starts at checkpoint 1, or at whatever the last one left (inferred).
The pattern of a story level's script (inferred from the disassembly; 28 story levels follow it):
- The main chunk defines the helpers, the flags (
AddFlagsBoxesPaths), the objects, then runsMain. MainreadsGetCheckPoint()into a global (checkpoint,CHAPTERormState;level84turns 0 into 1) and indexes a list with it. The list holds either the creator functions themselves (PlayerGang = {AddWarriors1, AddWarriors2, ...}, sometimes a local) or rows{creator, start function, chapter script}(tMission, as inlevel99, Scripts).- The creator makes the Warriors' gang (
GangCreate(0, "Warriors...", 0, 0)) and its humans, player 1 among them:HuCreate(name, type, {x, y, z}, heading, model, 1, gang), the position and heading literal numbers. It returns the table of humans and the gang;Mainthen setsplayer = Warriors.<name>(the name differs by checkpoint:level3plays Rembrandt for checkpoints 1-2 and Snow for 3-5). preLoadFile(chapter script, start function)loads the checkpoint's own script; when it arrives the game runs it and calls the start function by name (0x00356d00, confirmed (code)). A start function may move the player again: in five checkpoints it, or a function it calls, teleports the player (TeleportorTeleportToFlag, noted in the list).
Teleport(object, {x, y, z}, heading) (0x00385bb8) sets the transform; a heading of −1 keeps the rotation. Its
TeleportToFlag(object, flag, heading) sibling (0x00385db0) takes the flag's position and, with −1, the flag's
heading (0x00416258), and for a human also calls its vtable slot +0x14c. Confirmed (code). Neither places the
object on the ground as HuCreate does (above), but a human not in a scene (+0x280 = −1) is lowered onto whatever a
short ray (0.5) straight down hits (0x0023d440; confirmed (code)). A human in a grab, mount or mugging is first let
go, its partner playing a reaction clip (Combat).
Interiors are below the street. Starts with z near −195 to −215 (level5 checkpoint 2, level11 1 and 2,
level20 1 and 3, the hub ...) are rooms placed about 200 m under the city in the same world (inferred from the values
and the hub's clubhouse at z −194.3).
The hub (level95, Scripts): AddWarchief creates the player at the flag
fWchiefStart_1 (−188.6, 95, −194.3), heading 222, as the type the chapter script's WarchiefTable names (Cleon,
Rembrandt, Ajax, Cochise, Cowboy or Swan by chapter). StartLevel then either opens the quick map (when unlockable
(6, 3) is unlocked, or LoadLight is set) or makes the door walk: TeleportToFlag(player, fWchiefStart_<n>)
and a walk to fWchiefEnd_<n>, where n = WCLoc, random(1, 5) from the main chunk (1 to 5 inclusive, from the
game's table generator), or 5 while unlockable (6, 4) (the tutorial) is still locked, in any chapter. The five
start flags: 1 (−188.6, 95, −194.3) 89°; 2 (−188.6, 102.7, −197.5) 89°; 3 (−163.7, 80.7, −197.5) 358°; 4 (−174.4,
80.5, −194.3) 358°; 5 (−185.2, 112.7, −193.7) 182°. Inferred from the disassembly; how flags work, the generator
and the order of StartLevel's steps are on World flags.
A Rumble arena (level101-level137): the level script runs doFile("level" .. Level .. "_" ..
RumbleInfo[Rumble.gameType] .. "_init"), which adds the mode's flags, among them the list fP1 (player 1's gang)
and fP2. AddRumbleGang1 creates P11 with HuCreate("P11", Rumble.gang1[1], FlagPos(fP1[1]), 270, nil, 1,
gang, true) and teleports it to fP1[1] with heading −1, so it stands on the first flag facing the flag's heading
(inferred from the disassembly). The gang, and so the type, comes from the Rumble menu (GetRumbleModeData); the
steps are on World flags.
No player: level100 (the front end), level1 (its script only calls MenuLoadLevel("menu")), and the levels
without a .lev file.
The characters' update¶
The task manager's play tick (0x003a3148 → 0x003a2ea0, called first in mode 1's frame,
Level loading) calls Humans_Update (0x00249108) on each 60 Hz tick, once
0x4b0000 ticks (about 16.7 ms) have passed, and runs two ticks when it fell behind. Humans_Update does its work on
every second call, so the characters step at 30 Hz with dt = 1/30. In order, confirmed (code), each step behind a
debug switch that is on in play (0x005e5350-0x005e5368); the full list is on Tasks:
- The litter, ground fog and rain splashes (
0x00170c88,0x00171d38,0x00184568, Graphics);Pads_Update; the formations; the 60 player records (0x00146078, below). - The gangs, then the brains (
Brains_Update,0x00293b28, confirmed (code), AI), which write an AI human's command into its player record as a pad would. - For every human with an instance,
0x0023bd78(the instance's animation step,0x00175610), then0x00105570. - Per human: a position 1.3 above the human handed to
0x0019c3f0for humans with flag0x4000(an effect or sound, inferred), and checks against the player's gang. - Per human: vtable slot
+0x13c(its state update: the locomotion) or a flag when it is idle. - The actions, alternating the order (0 → 59, then 59 → 0) on each update:
Human_UpdateActions(0x00254e78, confirmed (code)), which runs the command dispatcher (Player_UpdateActions,0x0027c120) for player and AI humans alike; a dead or knocked-out human runs0x00221108,0x00256f28and0x00265f70instead. An earlier reading of this page called this step the brains. - The cameras' update (
0x0011e878) with dt, at least 1/30 (confirmed (code)).
From pad to intent¶
PlayerRecord_Update (0x00146078), each update, for a record with a pad and control on: flip the buffer; copy the
buttons; take the pad record's camera-turned left stick (+0x00, +0x04, Front end),
store its angle (atan2, 0x003357a8) and its length clamped to 1. With +0x1f set the angle is π/2 and the length
0. Confirmed (code). The stick is thus already relative to the camera: pushing up moves away from it, which is what
the game does at runtime (confirmed (runtime)).
Locomotion¶
Human_PlayerLocomotion (0x00240e38) for a pad-controlled human; an AI human moves by its brain instead
(An AI's locomotion).
Confirmed (code) for the steps; the state predicates are named by what they test where known.
- Current speed = the length of the velocity (vtable slot
+0x94). - Target speed. With the stick above the dead zone (0.12): walk by default; run when the magnitude is above
0.95 (
0x00225c10); jog instead of run when the human carries an object of class 4 or 6 (0x00225a50,0x00224000); sprint when the state flag0x1000000is set and the human has stamina (0x00225dc0, record+0x14a≠ 0). Three state tests override this: one (0x00223ad0) keeps the target at 0, one (0x00227d98) uses the record's walk speed+0x170unscaled, and two others (0x00228340, or the global0x0051031c) the base speed. With0x00510258set, run and sprint come from an analog button's pressure instead (more than 100 sprints); it is 0 in play. Below the dead zone the target is 0. - Skid (the test at
0x00241300). When the stored gait+0x1a8is 4 or 5, the current speed is at least the run speed (0x00221670: human+0x3a4× record+0x178, compared as floats,c.le.S) and the previous update's stick (the per-player record's other buffer) was over 0.95 (0x005102e8), then a stick now under 0.2, or one pointing more than 120° away from the velocity (dot product below -0.5; the dot is worked out only for some camera modes,0x0011f9b0), zeroes the velocity and sets the state code to 9 (the call at0x00241530), unless the human is airborne (0x00227f90) or0x00227f68holds. Code 9 is what plays the run stop: the idle builder (Human_BuildIdleTasks,0x0025f770) builds the run stop (0x0025c0e8: slot 33, 417, holding0x80000, the idle after it, a 0.1 s fade) instead of the idle when the code is 9, and the anim state choice (0x00259578) picks state 0 for code 9. Confirmed (code); the code 9 and its caller confirmed (runtime). - Turn. Target heading = stick angle − π/2; current heading = the facing from the rotation. The error is
wrapped to (−π, π]. The limit per update comes from
Human_MaxTurn(0x002213d8) through the gait lookup0x002212d0, which reads the table's player word for a pad-controlled human: in play 20° walking or standing, 18° jogging and running, 16° sprinting (600, 540 and 480 degrees per second); 4° and 6° in two special states (0x00227f68,0x00227f40), 11° in two others, 24° in a combat stance (Movement constants). With smoothing on, the step is the limit times an ease(1 − cos(π · error / E)) / 2with E = 2.0 rad in play (full from 2.0 rad), plus 0.8 times the previous step (−0.5 times it when the error changed sign), clamped to [0, limit]. When the error is smaller than the step, the heading snaps to the target. The new heading becomes the rotation (unless+0x3bblocks it) and the velocity's direction. Confirmed (code) and confirmed (runtime): a 90° turn at a run went 16.0°, then 18° per update (the facing reached in 6 updates); at a walk 17.9°, then 20°; in a sprint 14.3°, then 16°; 45° at a run 6.3°, 9.8°, 10.8°, 10.0°, 8.4°, 4.5° (Feel comparison). The.datavalues (12°, 6°, 4°, 2.5°, 1.5 rad), which an earlier reading of this page gave, are what the scripts replace. - Accelerate. Speeding up adds 0.8 m/s per update (24 m/s²) until the target; slowing down either drops to the
target at once or, in some states (
0x00223db8), at the same rate. - Velocity = facing × speed, with
z=+0x3a0(the vertical velocity is kept); some states zero it. - Lean (
0x00248df0): the heading change times a factor (0.4 ×+0x1ac, or 8 ×+0x1acwhen walking) is clamped to 2°, 3°, 5° or 7° by gait and stored at+0x298; the lean at+0x29cfollows it by 0.625 of the difference per update, at most 1°, 1.2°, 1.3° or 1.8° per update. What+0x1acholds is not traced.
Idle, walk, run. Human_GaitForSpeed (0x00221760) maps a speed to a gait: 5 (sprint) at or above the sprint
speed, else 4 (run) at or above the run speed, 3 (jog), 2 (walk), else 0 (idle). Confirmed (code). The gait does
not pick a clip: one gait blend covers walk to sprint (Clip selection), and what happens at runtime
is below.
Gaits at runtime (PCSX2 2.9.94, level99 checkpoint 1, Rembrandt standing; the left stick held straight up at
the magnitude given, by the stick-table method in Driving PCSX2;
speed is +0x1ac sampled every 5 ms, the clip is record +0x20). Confirmed (runtime):
| Stick magnitude | What happens |
|---|---|
| 0.10 | nothing (inside the 0.12 dead zone) |
| 0.13, 0.5, 0.94 | walk start (413) at a steady 0.76 m/s for about 0.45 s, then walk (408) at 1.63 m/s, gait 2: the walk speed does not depend on how far the stick is pushed |
| 0.96, 1.0 | run start (414) for about 0.36 s, the speed following the clip (2.77, 2.49, 2.56, 2.63, 2.84, 3.35, 3.90, 4.54, 5.11, 5.40, 5.61, 5.69 m/s, one value per update), then run (410), gaining 0.8 m/s per update up to 7.80 |
| released from a walk | speed 0 at once and idle (388) |
| released from a sprint (10.245 m/s) | run stop (417) for 24 updates (0.8 s), moved by the clip 1.63 m with the facing held, then the idle (Sprint) |
| released from a run (7.80 m/s) | the run stop only when the speed that update is not below the run speed by a rounding error, about one release in ten; otherwise the idle (388) at once, at 0 m/s (The run stop) |
| reversed (180°) at a run | one turn step (18°), then the same skid and run stop, sliding 1.63 m the old way; then the walk or run start toward the stick |
So the start clips drive the speed while they play (their root motion, Animation), and the gait clip's speed is the target afterwards. The walk start's "about 0.45 s" is its 0.333 s played at rate 0.75 (0.444 s). The run start's shorter 0.36 s is not explained; it may begin part-way through, as the walk-start-to-run-start swap does (speculative). A jog was not reached from the stick alone (it needs a carried object, see the locomotion steps above).
Per update (the feel pass, slot 1, read every update; confirmed (runtime)): on the update the stick is first seen the start clip begins and the body does not move; the walk start then moves it at a steady 0.762 m/s and the run start at 2.77, 2.49, 2.56, 2.63, 2.84, 3.35, 3.90, 4.54, 5.11, 5.40, 5.61, 5.69 m/s; both last 13 updates, and the gait blend then gains 0.8 m/s per update. The walk start's clip speed from the disc is 0.786 m/s: the body moves at the clip's speed × the body scale (0.97), as does the landing clip 436 (4.230 m/s against the clip's 4.361) and, inferred, every clip that moves the body. The gait speeds (walk, run, sprint) are not scaled.
The first updates in detail (the second feel pass, 2026-10-05, slot 1; speed +0x1ac and the position's change
agree to 0.003 m/s; the stored gait +0x1a8 in brackets). Confirmed (runtime):
| Stick, straight up | Update the stick is seen | Then, one value per update |
|---|---|---|
| 35 % or 60 % | walk start 413 begins, 0 m/s (gait 0) | 0.762 (1) for 12 updates, then the walk 408 at 1.562 (1), 1.629 (2) |
| 100 % | run start 414 begins, 0 m/s (0) | 2.773, 2.486, 2.558, 2.629, 2.844 (2); 3.346, 3.895, 4.540, 5.112, 5.399, 5.614, 5.686 (3); then the run 410 at 6.486, 7.286, 7.801 (4) |
| released after a run | run stop 417 begins, 0 m/s (0) | 2.244 (2), 4.996, 5.101, 5.229, 5.382, 4.193 (3), 3.190, 2.490, 2.163, 2.124, 2.056, 1.928, 1.740 (2), 1.434, 1.143, 0.886, 0.753, 0.645, 0.552 (1), 0.469, 0.390, 0.296, 0.198 (0); the idle 388 on the 25th |
The 35 % and 60 % runs were identical update for update. There is no ramp on the first moving update of any of the three clips: it is the clip's own root speed (times 0.97). A walk released stops at once (the idle on the next update, 0 m/s). The gaits matter to the camera, whose auto-follow runs only at gait 2, 4 or 5 (Camera).
The run stop at a run¶
Whether a run released plays the run stop is decided by the skid's speed test (Locomotion step 3),
and at a steady run that test compares two floats that are equal to within a few units in the last place.
Confirmed (runtime), scenario run_stop (slot 1, a straight run released, then a run circling behind the camera
released, as run_circle), with hooks on the state-code setter and on the skid test logging the gait, both stick
buffers, the run speed and the speed register f28 as raw words:
- The run speed is
0x40f9a3ad(7.8012300). The speed the locomotion measures at a steady run (the length of the velocity,0x0023fea8's vtable+0x94) jitters between0x40f9a3aaand0x40f9a3ae: of 93 updates at the run in one recording, 10 were at or above the run speed and 83 one to three units below it. - On every release seen the gait was 4, the previous stick 1.0 and the stick 0. Where the speed on the release update
was below (
0x40f9a3ab,0x40f9a3ac, straight and circling alike, three recordings) no code 9 was set and the idle 388 began on the release update itself at 0 m/s (the skid test's call re-labelled one update later after the recorder fix, Recording a trace), holding0x10000000for 5 updates; where it was not (one circling release), the state code went to 9 from0x00241538, the idle builder set it back to 0 (0x0025fa2c) and the run stop 417 played for 24 updates with0x80000, at the speeds in the table above. - So turning has nothing to do with it: the circling run of
run_circle(step 192) and a straight run both go either way. After a sprint (10.245 m/s) the speed is far above the run speed, so a sprint always ends in the run stop; a jog or walk (gait 3 or less) never does.
In the idle that follows a release without the run stop, the stick's last angle and magnitude (+0x5d8, +0x5dc)
keep turning the body: while the idle's fade holds 0x10000000, the locomotion turns toward the last stick angle with
a magnitude × 0.8 each update (0x002411cc onward), which is the 0.7°, 1.1°, 1.4°, 1.5° seen after run_circle's
release.
An AI's locomotion¶
An AI human has no stick: its goals and actions write the brain's heading +0x110, speed +0x114 (from a gait,
Brain_SetMoveGait 0x0028aac0), aim point +0x90 and radius +0x118 (AI, Moving), and its
control function, Human_UpdateControl (0x00243848, the default +0x1bc), turns them into a velocity and a
rotation each update. For a pad-controlled human the same function only picks the player's control (locomotion, air,
fight stance, button tap or wheelchair) and runs it. Confirmed (code) at 0x00243848 unless marked.
The gaits an AI asks for. Human_SpeedForGait (0x0022ae40, jump table 0x0055bec0) returns the human's own
clip speeds from its 0x180 record, unscaled by +0x3a4: gait 1 the sneak walk +0x16c, 2 the walk +0x170,
3 the jog +0x174, 4 the run +0x178, 5 the sprint +0x17c (Speed classes; Rembrandt 1.585,
1.629, 4.857, 7.801, 10.245 m/s), 0 and anything else 0. An AI's sprint drains stamina as a player's does
(Sprint, Human_UpdateMeters runs for every human), but nothing in this control reads stamina, so an AI
keeps sprinting at 0 (inferred: no reader found).
The speed this update. Start from the brain's speed, then, in order:
- Stopped (zero velocity, the rotation kept, the lean cleared) while the human is busy (
Human_IsBusy,0x00223cb0, confirmed (code)), its+0x280is not −1, or it is blocking or ducking (0x00223ad0). - Caps by state: hurt (state
0x10000,Human_IsWounded,0x00227d98, confirmed (code)) → the record's walk speed+0x170when asked for more than 0.1; in a fight stance (state0x3) → the base speed (+0x164×+0x3a4, the combat walk) when asked for more than 0.1, else 0; carrying an object of kind 4 or 6 and asked for more than the run speed → it drops the object (0x00257f38) unless its type's+0x87is 6 and+0x5ais 0; otherwise at most 25 m/s. With brain+0x0b(the turn boost, AI) above 0, a human (+0x1b0= −1) in a fight stance gets anim slot 14 = 380 (the player's combat walk) and its speeds recomputed; at 0 or below, slot 14 goes back to 372. - Speeding up from rest (current speed exactly 0): unless the state code is 3 or 7 or the anim state is 4, the
state code becomes 6 (run start) when asked for at least the run speed (8 instead when brain
+0x0cis above 0), or 5 when asked for exactly the walk speed; the clip selection plays the run or walk start from those. The speed this update is min(asked, 2.0). - Speeding up while moving: + 8 m/s² × 1/30 (0.267 m/s per update); when that still leaves more than 2 m/s to go, + 32 m/s² (1.067 per update) instead; never past the asked speed. So an AI ramps 0.267 per update for the last 2 m/s and faster before, where a player gains 0.8 (Locomotion step 5).
- Slowing down: − 32 m/s² (1.067 per update), not below the asked speed. Except for Diego and Vargas
(classes
0x78,0x77) running or sprinting and asked for 0 with no held flag0x110c0880: velocity 0 and state code 9, the run stop.
The direction. With human +0x333 (the detail level, 0-4, from the distance to the nearest camera,
Human_UpdateLod 0x0023d660) below 2, or any held flag 0x310c0880, the AI moves toward the brain's point +0x90;
at detail 2 or more it simply moves along the heading +0x110 at the speed and the rotation is set to that heading
at once: far from the camera there is no turn limit and none of the clips below. Close up (confirmed (code)):
- Turning happens only while the brain asks for a speed above 0 (or the human is hurt): the facing turns toward
the heading
+0x110by at mostHuman_MaxTurn(0x002213d8) per update, at a constant step, no easing (the player's turn eases, Locomotion step 4). For an AI the limit is the table's second word (Movement constants): 12° walking or standing, 6° jogging, 4° running, 2.5° sprinting, 24° in a fight stance, 1.5° grabbing, blocking or ducking, a quarter of 1.5° hurt and of 2.5° with a held flag0x1000080, and 4° / 6° in the two special states (0x00227f68,0x00227f40); the gait words are multiplied by brain+0x0b+ 1 (divided by 1 −+0x0bwhen it is negative,0x002212d0). The turn decides the facing; the velocity is the new facing × the speed, so an AI runs wide arcs (a run turns 120° per second).- Which word. Each pair is (player, AI);
Human_MaxTurntakes the AI word, the pair's second (+4), for a human with no player slot (+0x1b0= −1) or whose brain is dead (+0x09). The stance's AI word is0x005101dc(24° in.data); nothing in the executable stores to it.CfgSetTurnRates(0x0023a7a0, script0x0035e140) writes only the six player words0x005101b0,0x005101b8, ...0x005101d8(degrees in [0, 90), × π/180), so its sixth argument (24 inconfig_preload2.lua) sets the player's stance turn,0x005101d8, and never an AI's. The two are equal in play only because both are 24. Confirmed (code) at0x00221520and0x0023a964. - Wounded first. The hurt test (state
0x10000) comes before the stance test, so a hurt AI in a fight stance turns a quarter of0x005101b4(1.5°): 0.375° per update, not 24°. While hurt it also turns with the brain's speed at 0, and never starts a turn on the spot. Confirmed (code) at0x002213d8,0x00243848.
- Which word. Each pair is (player, AI);
- In a fight stance the velocity is instead along the line to the point (it strafes: the facing follows
+0x110, the feet go to+0x90), and record+0xdcgets the direction of the movement relative to the facing, 0 to 2π (0x002726d8), which drives the eight-direction blend (Clip selection). In detail (confirmed (code) at0x00243848): the speed is the stance walk when the brain's+0x114is above 0.1 and 0 otherwise (step 2 above), the direction is the unit vector from the human's position to+0x90(speed 0 when the point is on him), and the turn toward+0x110runs whenever+0x114is above 0, so an ask between 0 and 0.1 turns him without moving him. Wounded comes first: a hurt human in a stance gets the walk speed, not the stance walk.
The move request is rebuilt every update (confirmed (code)). Brain_UpdateGoals (0x0028fbb0, every brain,
every character step) begins with Brain_StopMove (0x0028ac18): +0x114 = 0 and +0x110 = the human's current
facing (from his rotation). It leaves +0x90 and +0x118 alone. The brains run before this control in the same
step (Tasks, steps 5 and 8), so the control sees only what a goal or action wrote in this
update; nothing a finished goal or action wrote survives into the next update. With nothing written, the AI asks for
speed 0 along its own facing: no velocity, no turn, and no turn on the spot (the heading error is 0). A stale +0x90
does nothing then, because the speed is 0. The only writers of +0x110 are the reset, the movement actions (move
0x002fc5c0, move-to-human 0x002fd4e8 and its abort 0x002fd0a8, 0x002fe740, the shuffle 0x002feb48, the turn
0x002fdd08, the step 0x002fe3a8), the route legs (0x0029b848, 0x0029baa8, 0x0029bca0) and a few goals that
are not fights (pedestrian reaction, avoid enemies, the ledge thrower, run from the train, the phone, the parlay) and the
player's brain (0x003035d8); found by a scan of every store to +0x110.
- Held flag
0x20000000(a turn clip playing, below) or any of0x110c0880, or the state code 5 or 6 (a start clip): no velocity; the clip moves the body. - The vertical velocity is kept (
+0x3a0), and the lean (0x00248df0) gets the heading change, as for a player.
Turning on the spot (0x0025c240): with a speed of 0 this update, the heading more than 15° off the
facing and no held flag, the AI plays a turn clip and turns the rest of the way over the clip's length
(Human_TurnToOver, 0x0023cf88: a constant rate on top of the clip's own turn, which is taken as 60° or 120°,
inferred from the subtraction). Positive angles turn left. Confirmed (code):
| Heading off by | Clip (most AI) | Clip (brain +0x0b below 0, or move style 4 or 6) |
In a fight stance |
|---|---|---|---|
| 15°-90° left | 396 ANIM_FAST_TURN_LEFT |
slot 8 + 1 (391, slow left) | slot 15 + 1 |
| 15°-90° right | 395 ANIM_FAST_TURN_RIGHT |
slot 8 (390, slow right) | slot 15 |
| over 90° left | 398 ANIM_FAST_TURN_QUICK_LEFT |
slot 8 + 3 (393) | slot 15 + 3 |
| over 90° right | 397 ANIM_FAST_TURN_QUICK_RIGHT |
slot 8 + 2 (392) | slot 15 + 2 |
The clip is played by 0x0025cb90: a fade of 0.1 s, the clip, then the idle (slot 0, or slot 11 in a fight stance)
looping, holding the given flag (0x20000000 here) while it plays, state code 0. This is the turn (398) seen at
runtime before an AI's run-in (AI, Moving).
Arriving with one clip (0x0025c5b8, the global 0x005104e8 = 1): while the brain asks for more than the jog
speed, its +0x11c is 0, the human is not down, not hurt and holds no flag, and its point is at least 0.1 m beyond
the radius (d = the distance in plan − +0x118), the AI may cover d with a single clip instead of the gait.
Confirmed (code):
- From rest (stored gait
+0x1a80): for an asked gait below 4 a step, slot 1 + (0 ahead within 45°, 1 behind beyond 135°, −1 right, −2 left: 401, 402, 400, 399); for 4 or more a dash, slot 2 + (0, 1, 3 right, 2 left: 403, 404, 406, 405); in a fight stance slots 12 and 13 instead of 1 and 2. - At a run or sprint (gait 4 or 5) with the point within 45° ahead: the run stop, slot 33 (417).
- With
Lthe clip's root displacement: the clip plays whenL≥d, or from rest whenL× 1.25 ≥d. WhenL<d+ the radius it plays as it is; otherwise the body is turned so the clip's displacement points at the point and moved over the clip's time so it ends there (Human_MoveToOver). The clip holds0x80000, and the control sets no velocity that update.
So an AI running at its point stops with the run stop once the point is closer than the run stop's slide (about 1.6 m for Rembrandt), and one starting near its point takes a step or dash. Whether a given goal's move reaches this test is the move action's business (AI, The move action).
Other effects: when the brain asks for any gait above 0 (Brain_UpdateGait, 0x0028ab08, tested in
Human_UpdateControl; confirmed (code)), the human lets go of carried props (Human_ReleaseCarriedProps, 0x0024ce40)
and, after a state 18 or with held flag 0x20000, its animation is rebuilt (0x0025f450); a human in a scene state
also lets go of its props.
Step control¶
The TakeStep and GiveWay actions (AI code) move an AI one step by swapping its control
function for the step control (Human_StepControl, 0x00243420) for a single update. Confirmed (code) at the
addresses cited.
Starting a step. TakeStepAction_Start (0x002fe3a8) writes the action's one argument, a heading in radians,
into the brain's heading +0x110, then calls Human_EnterStepControl (0x002432b0). The step has no distance
argument: the clip decides how far the body goes. Human_EnterStepControl does nothing when either of these holds:
- the human has any of state bits
0x180f3ff0. That is0x10-0x2000: grabbed or grabbing, mugging or mugged, the tackle and mount, and a paired power move (Combat: State flags). It is also0x10000-0x80000(0x10000is hurt) and0x8000000and0x10000000, whose meanings are not traced; - the step control is already installed.
Otherwise it saves the current control (Human_SaveControl: +0x1bc copied to +0x1c4) and installs the step control
(the 8-byte member-function pointer at +0x1bc = 0x00243420ffff0000: +0x1be = −1 and the function 0x00243420 at
+0x1c0), confirmed (code).
One update of the step control, for an AI. The per-player record's +0x1b is 0 for an AI.
- No move from the control. The two outputs are the zero vector (
0x005116c0) and the human's current rotation (transform table0x00714b10). Any displacement comes from the clip's root motion; inferred from the one-clip arrival, which uses the same clips and measures their root displacement. - Wait while not free. The control returns at once, still installed, while any of these holds:
Human_IsBusy: state0x79b9e1e0f30, airborne, state0x100000000, the per-player+0x1e, or a held flag of0xaeebf7ff;- grabbing;
+0x280is not −1;- wounded;
- state
0xe0000; - any held flag.
The step then starts on the first free update.
3. Pick the clip. Let d be the brain's heading +0x110 minus the facing, wrapped to −π..π. The turn boost is
brain +0x0b (AI). The facing to end on is the heading, offset so that the clip's own
direction points along the heading:
d |
End facing | Fight stance | Boost 0 or below | Boost above 0 |
|---|---|---|---|---|
| within ±22.5° (π/8) | heading | 366 ANIM_FIGHT_MOVEMENT_STEP_FORWARD |
401 ANIM_MOVEMENT_STEP_FORWARD |
403 ANIM_MOVEMENT_DASH_FORWARD |
| 112.5° (5π/8) or more either way | heading − π | 367 ..._STEP_BACKWARD |
402 ANIM_MOVEMENT_STEP_BACKWARD |
404 ANIM_MOVEMENT_DASH_BACKWARD |
| 22.5° to 112.5°, positive | heading − π/2 | 365 ..._STEP_RIGHT |
400 ANIM_MOVEMENT_STEP_RIGHT |
406 ANIM_MOVEMENT_DASH_RIGHT |
| 22.5° to 112.5°, negative | heading + π/2 | 364 ..._STEP_LEFT |
399 ANIM_MOVEMENT_STEP_LEFT |
405 ANIM_MOVEMENT_DASH_LEFT |
In a fight stance the boost is ignored. The clip ids are the anim ids.
Which way is positive. Both angles are the game's headings (Quat_Heading, Maths): a heading h
faces (sin h, cos h) in the ground plane, so it grows from +y toward +x, clockwise seen from above (+z
up). A positive d therefore means the heading lies to the human's right: from the facing f, his right
(forward × up) is (cos f, −sin f), the heading f + π/2. Confirmed (code): Quat_AxisY (0x003363b0) gives
the forward (−sin θ, cos θ) for a turn θ about +z, Vec_Heading (0x00335d98) is atan2(x, y), so the heading
is −θ. The clips agree: the generic gen_step_right clip's root moves along body +x (root velocity +0.98 m/s on
x at its first key; gen_step_left −0.86; gen_step_forward +1.23 on y; read from the disc as numbers), and
body +x turned by the end facing h − π/2 points along h. So the human always steps toward the heading:
STEP_RIGHT with a positive d, ending side-on with his right toward it; with d = +90° the end facing is his
current one and he does not turn. Runtime was not needed: the code and the clip data leave no other reading.
A port whose headings grow anticlockwise (the turn θ, minus the game's heading) sees every sign flipped: there a
negative d picks STEP_RIGHT, and the end facings are heading + π/2 for the right step and heading − π/2 for
the left.
4. Play it. Human_PlayOneShotClip (0x0025cb90) plays the clip:
- a blend-out of the current pose over 0.1 s, then the clip chained in over 0.2 s;
- after the clip, the idle loops (slot 0, or slot 11 in a fight stance);
- while the clip plays it holds held flag 0x80000, with state code 0 and the push sphere off.
The clip-end callback (0x0025c5b0) is an empty function (jr ra, confirmed (code)).
5. Turn during the clip. The human is not turned first. Human_TurnToOver (0x0023cf88, called from
Human_StepControl; confirmed (code)) turns it to the end facing at a constant rate over half the clip's play
time (Anim_GetPlayTime × 0.5), while the clip plays. The angle is negated before the call, the same convention as
turning on the spot: Human_TurnToOver takes the turn θ about +z (anticlockwise seen from
above), which is minus the heading. A turn under 0.01 rad is skipped.
6. Leave. Human_LeaveStepControl (0x00243360, confirmed (code)) runs in the same update. It restores the saved
control (Human_RestoreControl), then sets +0x1bc to Human_UpdateControl (0x00243848), or to
Human_MoveAttached (0x00244e78) while grabbed or being mugged. So the step control is installed for a single
update, unless step 2 held it.
For a pad player (+0x1b set) the step control restores the saved control. It then picks the player's control and
runs it in the same update: air when turn-limited (state 0x400000000), the fight stance with a target, button tap,
the wheelchair, or else PlayerLocomotion (0x00240e38). Confirmed (code).
How the action ends.
- Update (
TakeStepAction_Update,0x002fe428, confirmed (code)) returns running (0) while held flags0x20080000(Human_HasHeld20080000,0x00228448:0x80000the step clip,0x20000000a turn clip) are set, or while the step control is still installed. Otherwise it returns done (2). The step lasts as long as its clip. - Abort (
TakeStepAction_Abort,0x002fe3d8, confirmed (code)) always callsHuman_LeaveStepControl. With argument 0 it agrees; with a non-zero argument it refuses while0x20080000is held.
GiveWay. GiveWayAction_Start (0x002fe5d8) runs these steps in order (confirmed (code)):
- The AI looks at the other human for 2000 ms (
Brain_LookAtTarget, weight 0.5). - With the action's boost flag (
+0x14), it saves the turn boost+0x0band raises it by 1. A boost of 0 therefore becomes 1, which selects the dash clips. - It runs
TakeStepAction_Start.
Abort (GiveWayAction_Abort, 0x002fe658) restores the saved boost once TakeStepAction_Abort agrees. It also runs
when the step ends normally: a finished action is popped through Action_AbortAndDestroy, which calls Abort with
argument 0 (always agreed) for any started action (AI: Actions). So the boost goes back however the
GiveWay ends; nothing else restores it. The destroy (GiveWayAction_Destroy, 0x002fe6a8) then clears brain
+0xcc bit 1. Confirmed (code). Brain_GiveWayTo (0x00289ed0) sets the boost flag when the mover is a pad player
(+0x1b0 ≠ −1) whose stored gait +0x1a8 is 3 (jog) or more, so an AI dashes out of a running player's way and
steps out of a walker's.
Who may give way (Brain_GiveWayTo, before its free-sector search, which is in
AI: Giving way). All of these must hold:
- the stander's brain has a task (
+0x04≠ 0) or its+0x09is set; - neither human is a threat to the other:
Human_IsThreatTo(0x00222a48, confirmed (code)) is false both ways. It isBrain_IsThreaton the two brains: the gangs are enemies (Gang_IsEnemyOfon brain+0x20c), or both are pad players, with a Warrior's (type 3)+0x2e5mode able to force it either way. It tests only; nothing is changed; - the stander is idle under its control:
Human_IsIdleUnderControl(0x00225390, confirmed (code)). Its control isHuman_UpdateControlor the step control, it has no state bit of0x7bf9e1f3ff0, and it has no held flag at all.
Then a free sector gives a GiveWay action on the sector's heading (the stander's facing + sector × 45°), started after a random 0-124 ms.
Wheelchair control¶
HuSetWheelchairControl (0x00234188), confirmed
(code):
- On: look-behind switch 11 (
0x0050b23c) = 0 (Camera); unless the human already runs it: state flag0x80000000000, the current control saved (0x00221038:+0x1bccopied to+0x1c4), the control set towheelchairControl(0x002427e8,0x00244620), every player command off for the human, and commands 46 and 47 added (kind 7, pad bits0x4L1 and0x8R1). - Off: switch 11 = 1, the flag cleared; if the human runs
wheelchairControl, the saved control comes back (0x00221080), commands 46 and 47 are removed and all commands are on again.
wheelchairControl (0x002427e8), each update. A human whose per-player record +0x1b is 0 (inferred: not
pad-driven) or without the flag goes back to its normal control and runs that instead, so the Rumble CPU drives its
path with its usual locomotion. Otherwise it reads only L1 (0x4), R1 (0x8) and cross (0x40) from
the pad; the sticks are not read.
speed= the length of the velocity;max= the sprint speed (0x002216c0: human+0x3a4× record+0x17c).- L1 and R1:
speed+ 0.5, at mostmax, no turn. From a standstill (and anim state not 4) it instead sets the record's state code (+0x14) to 5, and while the code is 5 or 6 the control asks for no velocity (inferred: a start clip plays first). - L1 alone:
speed− 0.04 (not below 0) and a turn of +(0.03 + 0.05 × (1 − speed / max)) rad; R1 alone: the same turn negated. So one wheel turns 0.08 rad an update at a standstill and 0.03 at top speed. Positive turns raise the heading (counter-clockwise from above), so L1 (the right wheel) turns left and R1 right. - Neither:
speed− 0.04 (not below 0). Cross (the brake) then takes 0.5 more off, not below 0, whatever the shoulders do; with neither shoulder held it replaces the 0.04 decay. - The new heading is the old one plus the turn (written as a rotation about z); the velocity is
speedalong it, zero while any of state flags0x110c0880is set or the state code is 5 or 6. Above 4.5 the human's animation layer gets flag 2 and event groups 3, 6, 18, 19, 24, 25, 29, 30, 32 and 33 (Human_StrikeAllOn,0x00248170, confirmed (code)); at or below 4.5 they are cleared (0x00248270). The pending turn fields+0x298/+0x29care zeroed.
The constants, confirmed (code) in Human_MoveWheelchair, are at 0x005104d4 (0.5 push), 0x005104d8 (0.04 decay),
0x005104dc (0.5 brake), 0x005104e0 (0.03) and 0x005104e4 (0.08). Changes are per update; whether the update is
1/30 s or 1/60 s here is not traced.
Buttons¶
The pad's buttons become command ids through tables of 12-byte entries {u16 mask, u32 command, u16 buttons,
u16 extra} that AddCommand fills. Each update 0x00147940 matches them and stores the command in the per-player
record's +0x20, or in the pending +0x24 when the entry's mask is 0xfe (0xff enables it for pad 0). The buttons
are the pad word's bits (Pad record). The nine tables, what each trigger means, the
order in which they are matched and every command they make are on Combat, Commands
(confirmed (code) and (runtime)). An earlier reading here took square (0x80) for d-pad left and cross (0x40) for
down: the combinations are L2 + square (0x21), L2 + cross (0x20), cross + square (0x22) and circle + triangle
(0x24), and the d-pad combinations 0x29-0x2c and L3's 9 need select held as well.
Traversal uses two buttons: triangle pressed (command 10) starts a climb, a context action or a jump
(Jumping, Climbing), and L2 sprints. The sprint reads the button itself
(0x00147f98(record, 1), "is the button of mask 1 held") rather than command 5. Confirmed (code) at 0x0027c120 and
0x0027ce90.
Sprint and stamina¶
Sprint is held, not toggled. Every update Player_UpdateSprint (0x0027ce90, called from 0x0027d5a0, which
Player_UpdateActions calls) first clears state flag 0x1000000, then sets it again (and calls 0x00230140,
and clears state flags 0x8008) only when all of these hold. Confirmed (code):
- L2 is held;
- stamina (record
+0x14a) is not 0; - record
+0x08bit0x10is clear.
So the sprint lasts exactly as long as L2 is held and stamina lasts: letting go of L2 ends it on the next update, and
nothing latches it. It also ends when stamina reaches 0 (below) and when Player_UpdateActions takes its block branch
(0x0027c6b0): R1 held in a fight, or L1 released or L1 + R1 while already blocking
(Combat, Dispatcher), which clears the flag and sets state flags 0x8001. At runtime
(level99 and a street, stick 1.0 straight up and L2 held
through patched pad input, Driving PCSX2) the flag was set every
update, the command was 5, and the speed rose by 0.8 m/s per update from 7.80 to 10.245 m/s (gait 5).
Confirmed (runtime).
The stick still decides. The flag only asks for a sprint: the locomotion's sprint test (0x00225dc0) also needs
the stick above 0.95 and stamina, as for a run (Locomotion). With 0x00510258 set
(CfgPlayerRunButton), L2's pressure decides instead (pressure byte 10 above 100 sprints, otherwise a run); it is 0 in
play. Confirmed (code).
Stamina is the s16 at record +0x14a. Its maximum is Human_StaminaMax (Power classes): 135 for
Rembrandt (confirmed (runtime)). Confirmed (code) unless marked:
- Drain (
0x002562d0, called from the brain0x00254e78): while the gait stored with the velocity (+0x1a8) is 5 and record+0x08is 0 (0x00223a98), stamina loses 20 per second of game time (0x005101f4,CfgBurnRatesrate 5; 0 when0x005102a8is set, which it is not in play), with the fraction carried at+0x158and the time at+0x154. When it reaches 0 or below it is set to 0 and the sprint flag is cleared. Gait 5 is the speed nearest the sprint speed, above 9.02 m/s for Rembrandt, so the drain starts on the third update of the speed-up. - Refill (
0x00256a60, every update, fromHuman_UpdateActions; confirmed (code)): stamina gains the power class's+0x2e, 40 per second for Rembrandt, up to the maximum. It gains nothing (and the time is not carried over) while any of these holds: gait 5 (0x00223a98); state flags0x1c18003ff0(the airborne bits among them); a jump or fall (0x00227f90, flags0x1c00000000); gait 4 (run) with L2 held (0x00223a60and the button); record+0x08flags0x41a420. Human flag0x4000000fills stamina and the second meter every update. In three states (0x00223b98,0x00223bc0,0x00223b70) the function returns before taking the time, so the time spent in them is refilled at once afterwards. - No delay and no threshold: the refill starts on the first update the blocks are gone, and the sprint test is only "not 0", so one point of stamina sprints again.
At runtime (the street, Rembrandt, stamina 135 of 135; stick and L2 through patched pad input; stamina, speed and flags read every update). Confirmed (runtime):
- From a standstill with stick 1.0 and L2 held, stamina still refilled through the run start (gait 2-3), stood still at gait 4 (the "run with L2" block), and fell by 1 every 1.5 updates (20 per second) from the update the speed reached 9.686 m/s (gait 5, the third update of the speed-up from 6.486).
- When it reached 0 the sprint flag cleared and the speed dropped from 10.245 to 7.801 m/s in one update; with L2 still held for 1.5 s more, stamina stayed at 0.
- With L2 let go and the stick still at 1.0 (a run), stamina refilled at 40 per second (0 to 39 in about 1 s); L2 pressed again sprinted at once.
- Standing, it refilled by 4 every 3 updates (40 per second) to 135.
- Letting go of the stick in a sprint played the run stop (417) for about 0.8 s with record
+0x08=0x80000, the body moved by the clip (about 1.6 m) while stamina refilled, then the idle: the skid of Locomotion step 3. - Stamina also drains in a jump from a run (135 to 132): the gait is taken from the whole velocity, and the jump's vertical speed puts it above 9.02 m/s (Jumping).
What a player sees, from the code and the runs above:
| Stick | L2 | Result |
|---|---|---|
| 0.5 or 0.8 | held | walk at 1.63 m/s; the flag is set but there is no sprint and no drain; stamina refills (inferred) |
| 1.0, or a full diagonal | held | run, then sprint at 10.245 m/s after 4 updates; stamina drains from the third, 135 lasts 6.75 s |
| 1.0 | released | run at 7.80 m/s; stamina refills at 40 per second, 3.4 s from empty to full |
| 1.0 | held at 0 stamina | run at 7.80 m/s, and stamina stays at 0 until L2 is let go |
In an input script: stick left 0 100 and press l2 to sprint, release l2 to refill.
Measured (PCSX2 2.9.94, the stick held fully forward from a standstill by the W key, magnitude 1.0, positions read over PINE): 4.10 m after a 0.75 s hold, 10.08 m after 1.5 s: 5.98 m in the second 0.75 s, 8.0 m/s, close to Rembrandt's run speed of 7.80 m/s (confirmed (runtime); the hold times are those of the key presses, so about ±1 frame).
Clip selection¶
Each update the human's animation controller Human_ChooseAnimState (0x00259578, called from the characters'
update at 0x00255510 and 0x00256c34) picks an anim state and, when it differs from the one in record +0x18
(or human flag 0x20000000 forces it), calls that state's builder from the table at 0x005106e0 (one function per
state; most entries are an empty stub 0x0025f4c8). The builders make animation tasks
and change animation by "fade over d, new task at the bottom" (Task stack).
Confirmed (code) at the cited addresses unless marked.
Choosing the state (the plain locomotion case; grabs, combat, carried objects and scripted moves test their own
flags first and are not described here), confirmed (code) with the builders read from the table at 0x005106e0:
| State | When | Builder |
|---|---|---|
| 0, idle | speed below a quarter of the speed 0x00221580 returns |
0x0025f770 |
| 4, move | speed at or above that, or record +0x14 is 5, 6 or 8 |
0x0025f4d0 |
| 11 | a combat stance (0x00228340) at speed 0.01 or less |
0x0025fa48 |
| 14 | a combat stance while moving | 0x0025f4d0 |
| 21, 24 | human flag 0x8000: 24 when the pending turn +0x5e4 exceeds Human_MaxTurn, else 21 (turns in place, inferred) |
0x0025fc30, 0x0025fc70 |
Move (state 4). 0x0025f4d0 calls Human_BuildMoveTasks (0x0025b200) with a fade time of 1/15 s (1/6 s when
record +0x14 is 18 or human flag 0x40000000 is set), which builds one of:
- From standing (the top task is not a gait blend, task flag
0x80clear): a gait blend of slots 4, 5, 6, 7, 7 (walk, jog, run, sprint, sprint) at value 0, flags0x2c1; then a clip-then-next task (type 3) that plays slot 10, the walk start (413), with a blend time of 0, and hands over to the gait blend. The fade before it has a duration of 0: the start clip replaces the idle at once. Human flag0x10000000is set while the start clip plays (it is what stops the locomotion setting a velocity, Root motion). - Run start (
0x0025b9f8, confirmed (code) inHuman_BuildMoveTasks: outside a shadow, when the stick asks for a run and record+0x14is 6, 7 or 8, or for a jog with state flag0x1000000,Human_WantsJogbeing what tests a carried object of class 4 or 6): the same, but the gait blend starts at 2 (run) and the start clip is slot 10's id + 1 (414, run start). - Already moving (the top task is a gait blend): a fade of 0.1333 s and a new gait blend that starts at the old
one's value (
0x0025ff50rounds it down to 0, 1, 2 or 3; confirmed (code)) and the old one's normalised time, so the cycle carries on. - Other cases: a looping single clip of slot 14 (combat walk) when the global
0x0051031cis set (for a player, in0x0025f4d0; confirmed (code)); a four-clip task of slot 0 and slot 4 under0x00227d98; a two-clip mix of 633STEALTH_WALKand 636STEALTH_READY_WALKwhile stalking a target (Human_IsStalkingTarget,0x00228188, Combat moves).
Walk start to run start. While the walk start plays (flag 0x10000000), if the stick asks for a run or sprint
(Human_WantsRun, 0x00225c10; Human_WantsSprint, 0x00225dc0) above the magnitude at 0x005102e8 (0.95), and less
than half of the clip has played, the controller swaps it for the run start: a fade of min(0.1333 s, the clip's
duration), a gait blend at 2, and the run start begun at the walk start's normalised time (0x00259578, confirmed
(code)).
Gait value from the speed. In states 4 and 14, every update, 0x0025eee0 sets the top gait blend's target to
Gait_BlendForSpeed (0x0025ec28), a piecewise-linear map through the speed getters:
speed > run → 2 + (speed − run) / (sprint − run)
jog < speed ≤ run → 1 + (speed − jog) / (run − jog)
walk < speed ≤ jog → (speed − walk) / (jog − walk)
otherwise → 0 (clamped to 0-3)
So the walk clip plays alone up to the walk speed, the blend reaches the run clip at the run speed and the sprint at
the sprint speed; the fifth slot (sprint again) is never more than a neighbour. The blend's value then eases toward
the target at 10 units per second. When the top task is the eight-direction blend (type 13), the target is instead
record +0xdc × 4/π (a direction, inferred); for the four-clip task, speed / walk speed clamped to 0-1.
Idle (state 0). Human_BuildIdleTasks (0x0025f770) calls 0x0025f1b8 with slot 0 (388) as a looping
clip with no task flags, after a fade of 0.15 s; if a start clip is still playing (flag 0x10000000) the fade is
1/15 s when less than 0.1333 s of it has played and 0.2 s otherwise. Some ids replace slot 0 in special cases (355,
357, 394, 634). With the state code 9 (the skid) it builds the run stop instead (0x0025c0e8, slot
33, 417); otherwise releasing the stick fades straight to the idle, which matches the runtime samples below. This
corrects the earlier reading that slot 33 is not used.
At runtime (PCSX2 2.9.94, level99 checkpoint 1, Rembrandt; the task stack read from the instance every 50 ms).
Confirmed (runtime):
- Standing: one looping task, idle 388, rate 0.75, no flags.
- Stick 0.5 straight up: a fade of 0 s and a type-3 task playing the walk start (413, 0.333 s) at rate 0.75 with human
flag
0x10000000; at its end a gait blend (flags0x2c1, rate 1.0, speed 10) with target and value 0, the walk clip leading and the jog clip muted. - Stick 1.0: the run start (414) the same way, then the gait blend at 2. The first update after the start clip ran at 6.49 m/s, so the target was 1.553 and the value fell to 1.825 (jog and run mixed) before going back to 2.0 at 7.80 m/s: the target follows the speed every update.
Animation callbacks (AddAnimCallback)¶
Scripts register Lua functions to run when a human starts an animation (AddAnimCallback,
AddAllAnimCallback, DelAnimCallback). Confirmed (code) at the
addresses given:
- The table (
0x006b6710): 16 slots of 16 bytes, {human handle, anim id, all-humans flag, interned Lua name}.AddAnimCallback(0x0023a9f8) takes the first slot whose handle no longer resolves;AddAllAnimCallback(0x0023aab0) the first whose handle does not resolve and whose flag is 0, and sets the flag. A full table returns nil.AddAnimCallbackdoes not test the flag, so it can take (overwrite) an all-humans slot, whose handle never resolves.InitLevel(0x0015fe90) empties every slot (0x0023a9b8); an entry of a deleted human becomes free by itself. - When it fires:
CharacterInstance_GetAnim(id)(0x00175080), called with its third argument 0, runs the dispatch (AnimCallback_Dispatch,0x0023ac50) for the instance's human (instance+0x2b8) when the id resolves to a clip. The animation-task code calls it that way each time it takes a clip for an id: every task constructor (a clip started,0x00105678,0x00105990,0x00106140, ..., the gait blend0x0010a310) and the blend tasks' advance when they move to another clip (0x0010a5b8,0x00109588). So the callback fires when the human starts playing that anim id, not at the clip's end, and again every time it is started (inferred for the advance cases: they re-resolve when the blend crosses to a new clip). Lookups for other uses (0x00221bf0, third argument 1) do not fire it. - The match: slots in order; a slot matches when its anim id equals the id and either its all-humans flag is set or its handle resolves to this human. Only the first match fires; the dispatch then returns.
- The call: synchronous, inside the animation code: the name is looked up (script slot
+0x4c, Scripting), then called with two arguments, the human's handle and the anim id (slots+0x5cand+0x6c, call+0x8cwith 2). - Not one-shot: the slot stays until
DelAnimCallback(human, anim)(0x0023ab80; the first slot whose id matches and whose flag is set or whose handle is the human's) or the nextInitLevel. - A paired task (
0x00108450,0x00108a78) looks the id up in the other human's instance, so the dispatch runs for that human. Confirmed (runtime): at a strong grapple's connect the victim's 658, started byPairedTask_Init(0x00108a78, caller0x00108af4), was dispatched for the player (the grabber, whose set holds 658). - A follow-on task fires when it is built, not when it starts playing. At a grab's connect,
Grab_Connect(Combat) builds the connecting clip and, under it, the hold's four-clip mix (type 11,AnimTaskFourMix_Construct,0x00106f50) on both stacks in the same update; the four-mix's constructor takes its clips (82 and 86 for the grabber, 83 and 87 for the victim) and so fires the callbacks of the hold ids at the connect, the length of the connecting clip before the hold plays. Confirmed (runtime), PCSX2 2.9.94,level99checkpoint 1 (hooksanim-dispatchandanim-callback-match, the queue at runtime): for each grab the dispatch of 82 for the player (from0x00106fe0) and of 83 for the victim came in the tick the connecting clip started (657 fromAnimTaskChained_Construct,0x00105990); in the strong grapple the hold played and the tutorial callback received 82 34 updates later, and no second dispatch of 82 came when the hold began. So level99'sPlayerState(registered on the player's 82 and 84) runs at the connect, and lesson 5'sP1.Power(the hit scored with 82, Strong grapple) 34 updates after it, never in the same update. A plain grab fires 82 at its connect the same way (lessons 2, 6 and 8). - Coney's implementation (
ScriptedBrains::runAnimCallbacks): the starts are gathered over the characters' step and the callbacks called after it. A human that starts the same id more than once in one step gets one call (Coney's reading). Coney hands a chain over to its loop in every layer, the fading ones too, so two attack chains ending in the fight idle 358 started it twice in one update.level99'sPlayerStatethen ran twice: each timeHUDSetTutorialText(nil)and a hint, and the second nil found nothing showing yet, so one hint stayed queued per double start. The box backed up behind them, and the Targeting and Light Combos hints never showed (Hints). A grab's hold ids (82 / 83, 84 / 85 from the rear) fire at the connect, as above:Fighter::connectreports the start of the hold its chain and the victim's end in (AnimTaskStack::announceFollowOn), and the hold does not report again when it begins, soPlayerState's grab hint andP1.Power'sTT_24dcome the connecting clip's length apart. Coney's reading: the four-clip mix's second clips (86 / 87) are not reported, since Coney's hold plays the one clip; the paired reaction (658 for the strong grapple) is still dispatched for the victim, not for the grabber as above. - Coney's order in a step (inferred from the original's frame, A frame of play):
the animation callbacks run before the tutorial callback, since the original calls
that one when the victim applies the damage, with the id the attacker has already started; then the hint box
steps, once a frame, where the original's
HUD_Updatestands; then the scheduled calls. A text set on a free box shows within the call that set it (The queue at runtime), so texts set by a callback and a scheduled call in one step never back up (before, fromlevel99's lesson 6 on, the box showed each grab hintTT_19*one text late, in place of the lesson's: user checklist item 33b).
Moving, standing on the ground and falling¶
The locomotion above only sets a velocity. Moving the human, keeping it on the ground and making it fall happen in
the human's state update (vtable slot +0x13c, 0x0023fea8) and its move step (0x0023d8c8), with the physics
world (0x00597198, Physics/) doing the sweep against walls. Confirmed (code) unless marked.
Where the transform lives. The authoritative position and rotation of every task-manager object are in a table at
0x00714b00, 32 bytes per object (position vec4, then rotation quat), indexed by the object's handle index
(+0x92). SetPosition (0x0021b0b8) writes the table; the human copies it into +0x10 / +0x20 at the start of
each update (vtable +0xac, 0x004ed828). The velocity is at +0x30 (vec4), read through 0x003a1f00 (zero while
flag 0x600 is set in +0x54); writing it (vtable +0x74, 0x0023cf00) also stores its length at +0x1ac and the
gait for that speed (0x0022aeb0) at +0x1a8. So +0x1ac (used by the lean) is the current speed.
Airborne flag. Bit 0x4000000 of the object flags at +0x54 means "in the air". 0x003a2158 sets it (and clears
0x2000000, "on the ground"); 0x003a21c0 does the reverse. Confirmed (code).
One update, in order (the parts that move the body; the human's other duties are left out):
- Copy the transform from the table; run the current state's function (for the player, the locomotion, above), which sets the velocity.
- Speed sanity check: a velocity longer than 50 × the human's scale (
+0x65c, 1.0 normally;Human_GetScale,0x0021d020; confirmed (code)) is zeroed. - Gravity, only while airborne: from the second airborne update on (the counter
+0x384counts airborne updates),vz -= 15.68 × dt(0.5227 m/s per 1/30 s update; 15.68 m/s² is 1.6 g), clamped at −50 m/s. The result is written back with the velocity. On the ground+0x384is reset to 0. - Fell out of the world: if the position is more than 20 m below the collision mesh's lowest vertex (mesh
header
+0x98, Collision), the human is flagged dead (0x200000000), its velocity zeroed, and for a player in a mission whose game-state flags+0x150have bit 1,W_GameState + 0x14cis set to 1 and+0x152to 2 (a mission failure, Level loading). - Stuck in the air: after 60 airborne updates, if the physics body's "could not move" counter (body
+0x70) is also above 59, the human is put back at its last ground position (+0x390) and landed (Human_Land,0x0023e090). Confirmed (code). - Move (
Human_Move,0x0023d8c8, below; confirmed (code)).
The move. With velocity v (and an external push +0x2f0 added, which knock-backs use):
- A human without a physics body (
+0x1a0= 0), riding something (+0x280≠ −1) or in state0x40simply moves:position += (v + push) × dt. - Otherwise, on the ground,
v.zis set to 0 first: walking never climbs by velocity, only by the ground snap below. On a slope (ground normalz,+0x238, below 0.95) and outside some states (0x00227f68; confirmed (code)), the velocity is scaled byclamp(0.6 + 0.3 × (n.z − 0.5), 0.5, 1.0): 0.735 just below the threshold, 0.71 on a 30° slope (n.z= 0.87), 0.5 fromn.z≤ 0.17. (The code also computes the facing just before, but the scale does not depend on it, so it slows uphill and downhill alike; and there is a step from 1.0 to 0.735 atn.z= 0.95.) - The physics world then sweeps the body (
PhysicsBody_Sweep,0x0033e278(dt, world, body, &v, &out), confirmed (code)): the body's pending push-out (body+0x50, divided by dt) is added to the displacementv × dt, which is swept up to three times, sliding along what it hits. If it is still blocked after three passes, the body stays where it is, its horizontal velocity is zeroed and its "could not move" counter (+0x70) goes up; otherwise the counter is reset. Walls come from the level's collision mesh through the body's shape: a sphere swept along the move, below (Walls and steps). - Ground snap, on the ground only (and not in game modes 8,
0xbor0x11,0x00221950),0x0023eab8: cast a ray straight down from 1.0 m above the feet, 1.5 m long (the 1.0 is vtable+0x5c,0x004ed818, a constant).- Hit: put the feet exactly on the hit point (no gap), whatever the hit triangle's normal: the snap has
no floor test (Slopes), remember it as the last ground position (
+0x390before the snap,+0x240after), store the ground normal at+0x230(itszat+0x238, used by the slope rule) and the material at+0x1d8. The triangle's flag bits 4 and 5 are passed on (bit 4 to a per-player "under cover" state,0x0028ef00; bit 5 to0x002195e0; meanings inferred from what the callees touch). - Miss (nothing within 0.5 m below the feet): the human starts to fall through vtable
+0x154(0x0023dc58) unless it is in state0x800. So a step or kerb up to 1.0 m high is climbed in one update when the sweep lets the body over it, and a drop of up to 0.5 m is followed without falling; anything deeper is a fall. The sweep lets the body over a step only when the step's wall triangles are under 0.25 m tall (Walls and steps).
- Hit: put the feet exactly on the hit point (no gap), whatever the hit triangle's normal: the snap has
no floor test (Slopes), remember it as the last ground position (
Walls and steps¶
The physics body of a human holds a capsule shape (type 3, shape +0x30; radius 0.35
at shape +0x40, 1.886 at +0x44; read at runtime), and the physics world handles a shape through a table of
functions by type (world +0xe0, 0x0033d2d8, which passes −0.65). For type 3 that is
PhysicsMesh_SweepCapsule (0x00347c08), which treats the body as one sphere for walls. Confirmed (code):
- The sphere: radius
r= shape+0x40× body+0x60× the human's scale (Human_GetScale0x0021d020:+0x65c, or 1.0 in a scene state when it is under 1.1), centrer + 0.05above the feet (0x00347c98-0x00347cd8). The shape's+0x40is itself 0.35 × the scale (Human_GetCapsuleRadius0x00219860, written byPhysicsBody_ApplyHumanScale0x00341f28), so the scale counts twice:r= 0.35 × scale² × body+0x60. Confirmed (code). For the player, body+0x60(and+0x64) is 1.4286 (0.5 / 0.35), sor= 0.5 × scale²: Rembrandt (scale 0.97) hasr= 0.3395 × 1.4286 × 0.97 = 0.4705 m, and the sphere spans 0.05 to 0.99 m above the feet. Confirmed (runtime), PCSX2 2.9.94, thesweep-radiushook at0x00347cbcin thesweep60scenario (a copy of slot 1, level99's street, stick 60 % up for 80 updates): all 39 calls logged the player's body with shape+0x40× body+0x60= 0.4850, body+0x60= 1.4286 andHuman_GetScale= 0.9700, and the hookedmul.smultiplies the two. A measured 0.485 m is that product before the second scale; the radius the walls see is 0.4705 m. The stops measured before (0.476-0.480 m from a full wall, a wall at an angle and the fence of slot 7; the step faces below) sit 0.006-0.010 m beyond where a 0.4705 m sphere touches, which fits a small clearance left by the sweep (inferred; where it comes from is not traced). Only a player's body has+0x60= 1.4286 (Human_CreatePhysicsBody0x0021ce08for a brain of type 0,Human_MakePlayer0x00229c40;Human_ReleasePlayer0x0022a2a8puts back 1.0, Physics). Every AI human's is 1.0, so its sphere is shape+0x40(0.35 × its class scale) × its scale: 0.350 m for Vermin, 0.357 m for level99's bums and civilians of scale 1.01 (fields read in slot 6). Confirmed (code), values confirmed (runtime). - For each enabled triangle of the grid cells it covers that is a wall (
|n.z| ≤ 0.65) and that the sphere is in front of (a two-sided triangle is turned to face it), and that the move goes into (n · move < −0.001): - Skip low and thin triangles: take the edge whose unit vector is steepest; if its two ends differ in height by
less than 0.25 m, skip the triangle. Then take the longest edge; unless it is nearly vertical (
|unit z|≥ 0.8), skip the triangle when the third corner lies less than 0.25 m from that edge's line within the triangle's plane. - Sweep the sphere along the move against the triangle (
Sweep_SphereTriangle,0x0034ee60; confirmed (code)); a hit at a fraction from 0 to 1 is a contact. The contacts go to the body's contact handler (Human_OnContact,0x00219d50), which for the level's triangles slides (code0x20001), lands on a floor contact (flag0x80), and during a climb (record+0x080x40) ignores triangles of materials 30 (LOW_FENCE), 31 (OPAQUE_FENCE) and 122 (RAILING), so the body passes through the fence it climbs. No other material has a rule here: a glass pane'sGLASStriangles are a wall like any other until the pane breaks (Moving into a pane).
So on the ground the original has no step height of its own: a wall face shorter than 0.25 m is not a wall, and the ground snap (1.0 m up, above) then lifts the feet onto it; any face 0.25 m or taller that reaches into the sphere (0.05-0.99 m above the feet for the player) stops the body. A kerb of 0.2 m is walked onto; a ledge of 0.5 m or 0.75 m is a wall to walk into and needs a climb (a short wall from 0.7 m, Climbing) or a jump.
The step at runtime (confirmed (runtime), PCSX2 2.9.94, a copy of slot 1 in level99's world, 2026-10-05). The
street there has no kerb (no wall face under 0.35 m rises from the street level), so a box of the level's collision
mesh (material 41, 2.62 × 0.62 m, against a wall; its eight vertices are used by its own ten triangles only) was
made into a step by writing its vertices' heights in RAM: bottom at the floor (0.213), top 0.10 to 0.30 m higher.
The player was put 1.26 m in front of it, facing it, and walked in with the stick at 35 %, 50 % or 100 %, turned by
the camera so that the move pointed straight at the face:
| Step height | Stick | Result |
|---|---|---|
| 0.100, 0.200, 0.240 m | 35 %, 50 % (walk 1.63 m/s) | walked on: the feet rose by the step's height in one update, on the update the body's centre passed over the edge, with no change of speed or clip |
| 0.245 m | 100 % (run start, 5.6 m/s) | walked on, the same way |
| 0.255 m | 100 % | stopped 0.395 m short of the face, idle with the stick held |
| 0.260, 0.300 m | 50 % | stopped 0.399 m and 0.423 m short of the face |
So the 0.25 m test is exact, and the stopping distances are those of the 0.4705 m sphere centred 0.5205 m above the
feet touching the face's top edge with about 0.006 m to spare: sqrt(0.4765² − (0.5205 − h)²) gives 0.396, 0.399
and 0.422 m for h = 0.255, 0.26 and 0.30, and against a full-height wall the same walk stopped 0.476 m from it
(0.4705 + 0.006). The clearance is fitted to these four stops (inferred). An earlier reading fitted the three step
stops to a 0.485 m sphere without clearance, but that missed the full wall by 0.009 m, and the radius is 0.4705 m
(step 1).
Sliding along a wall (confirmed (runtime), slot 1): the velocity the sweep leaves after sliding is the human's
velocity, so the next update's current speed is its length and the 0.8 m/s gain starts from there. Running into a
wall 20° off its normal, the speed fell from 7.80 to 4.00, 1.55, 0.86, 0.65 and 0.56 m/s (every second update) and
settled near
0.8 k / (1 − k) (k = sin 20°: 0.42 m/s measured, 0.416 predicted), until it fell under the idle threshold
(a quarter of the sneak-walk speed, 0.396 m/s) and the idle played with the stick still pushed. A walk does the
same. So the player brakes against a wall met at a steep angle instead of sliding along it at speed.
Slopes between floor and wall¶
A face too steep to be floor and not steep enough to be a sheer wall (n.z between 0.65 and about 0.5) is a wall
the body slides up, not a wall that stops it. Confirmed (code) unless marked.
The thresholds. Only two tests read a normal, and the ground snap reads none:
| Test | Rule | Where |
|---|---|---|
| The walking sweep's walls | n.z from −0.65 to 0.65, the move going into the face, 0.25 m tall or more |
PhysicsMesh_SweepCapsule 0x00347c08 (the −0.65 comes from 0x0033d2d8), above |
| An airborne body's landing | n.z > 0.65 |
0x0023e408 (Falling and landing) |
| The ground snap | none: the feet go onto the first triangle the ray meets (mask 0) | Human_SnapToGround 0x0023eab8; confirmed (runtime) below, walking at full speed down a face with n.z 0.54 |
| The slope's speed | n.z < 0.95: × clamp(0.6 + 0.3 × (n.z − 0.5), 0.5, 1.0) |
the move 0x0023d8c8 (above); confirmed (runtime) below |
Nothing else changes them:
- The material.
Human_OnContact(0x00219d50) answers0x20001(slide) for every level triangle; its only material rule is the climb's pass-through of 30, 31 and 122.GRAVEL(106) and the rest have no movement rule. - The triangle's bits 14-15. The sweep reads them as the edge of a corner slide; 3 names none, and the gravel faces below hold 3.
- The route. A leg of kind 1 is walked with no handler of its own (AI: link kinds). The
sweep, the contact handler and the snap read neither the brain nor its route, and an AI's body moves through the
same move as the player's. Two things differ: the sphere's radius (body
+0x60, about 0.35 m for an AI, above), and the slid velocity, which is written back only to a player's body (body flag0x400, Physics). An AI asks for its full speed again each update, so a slide never brakes it.
How the sweep slides up such a face:
- A face the sphere already reaches (start distance to the plane at most
r) is sorted by where the centre lies (Sweep_SphereTriangle0x0034ee60; edgekruns from cornerktok + 1, and the centre is inside it when(e_k × n) · c ≤ (e_k × n) · v_k):- inside all three edges (case 7): a contact at fraction 0;
- outside one edge (cases 3, 5, 6): that edge alone (
SphereSweep_Edge0x0034e3b8). When the edge segment already passes withinrof the centre (Segment_SphereIntersect0x0034e120: the segment's roots against the sphere overlap 0 to 1) the contact is at fraction 0; otherwise the move is swept against the edge as a cylinder and the first root from 0 to 1 is the contact; - outside two edges, past a corner (cases 1, 2, 4;
SphereSweep_VertexRegion0x0034e5d0): a corner already inside the sphere (|v − c|<r) gives no contact at all, nor does a move going away from it (move · (v − c)≤ 0); otherwise one of the corner's two edges is swept as above.
A sphere not yet at the plane is sorted the same way at the point where it first touches the plane. Every case
hands back only a fraction: there is no edge or corner normal and no push-out. Nothing pushes a walking sphere
out of a face it overlaps: PhysicsBody_PushOutOfWalls runs only in the air (below).
2. The contact carries the triangle's own unit normal, turned to the sphere for a two-sided one (contact +0x50,
copied in 0x00347c08), with its z, in every case of step 1: a sphere that touches the face only past its
edge slides along the face's plane exactly as one inside it.
3. The slide (PhysicsBody_ResolveContacts 0x0033d9d8, code 1) removes from what is left of the move, and from
the velocity, the part that goes into that normal: v −= (v · n) n on x, y and z, only when v · n < 0
(Vec_RemoveNormalPart 0x0033d870). The move then goes along the face, rising.
4. On the next pass the slid move no longer goes into the face (n · move ≥ −0.001), so the face is skipped. There
are up to three passes.
5. The velocity keeps the slid z, but the next update sets v.z to 0 before its sweep (The move). The
snap then puts the feet on whatever is under them.
So walking at a face of n.z 0.53 at 20° off its normal, the move keeps its part along the face. The snap holds the
feet on the floor triangle in front of the face, and the body climbs that floor along the face's foot until it rises
past the face. The sphere sinks steadily into the face, and nothing corrects that. The body has no step height of
its own: it relies on the 0.25 m wall rule and the snap's 1.0 m reach, above.
At runtime (confirmed (runtime), PCSX2 2.9.94, a copy of slot 1, 2026-10-08). The test used level99's gravel
heap, where the path data routes a kind-1 walk from (−271.7, 129.4, 0.3) up to the lesson-2 platform at (−277.7,
132.0, 3.1). The faces on that line:
- triangle 679:
GRAVEL, flags0xc001,n(0.736, −0.134, 0.664), a floor; - triangle 680:
GRAVEL, flags0xf001,n(0.849, −0.055, 0.526), a wall to the sweep: 1.77 m tall, its steepest edge rising 1.77 m.
Rembrandt was put at the walk's start facing its end. The stick was pushed straight up from update 60, and the camera put the move about 64° from north toward the west. Every run took the line onto 679 first:
| Run | What happened |
|---|---|
| walk, stick 60 % | 1.63 m/s on the street. On 679 the walk was turned about 30° north along 680's foot, at 0.41 m/s across the ground and 0.29 m/s up: the feet followed 679's surface (ground normal 0.664 every update) while the slid velocity held z 0.36. The sphere's distance to 680 fell from 0.48 to 0.33 m (radius 0.4705). After 3.5 s at z 1.44 the body passed 680 onto faces with n.z 0.77 at 1.107 m/s across the ground (1.63 × 0.6797, the slope rule), crested at z 2.01 and walked down the far side, including a face with n.z 0.54 at full walk, onto the street |
| run, stick 100 % | the same: stopped for an update at 679's foot, then 0.55 m/s up 679 along 680, then a run at 7.80 m/s over the crest at z 2.28 |
walk, stick 60 %, body +0x60 set to 1.0 (radius 0.34, an AI's size if its +0x60 is 1.0) |
the same path: up 679 along 680 (distance to 680 0.32 down to 0.20 m), over at z 2.36, down the other side |
So a human on that leg does climb the heap in the original: by sliding along 680, not by walking up it. The lesson's walk reaching the platform itself was not run (the stick's line passed 1 m north of it).
An AI on the same leg (confirmed (runtime), PCSX2 2.9.94, a copy of slot 6, 2026-10-08, scenario
repo:research/traces/scenarios/heap_ai.toml). Vermin (dead brain, radius 0.350) was put at (−271.7, 129.4, 0.3) with
the player standing 4 m behind, and his own GoalMoveToFlag was re-aimed at (−285.2, 124.2, 0.3), gait 2
(walk, his 1.654 m/s), radius 2, and his actions cleared. The planner
gave him the same route as Coney's: (−277.71, 132.00, 3.12), (−280.76, 130.48, 3.76), a drop leg, the target. Each
update, read over PINE:
| Stretch | What happened |
|---|---|
| street, updates 1-81 | 1.654 m/s straight at the first waypoint, 20° off 680's normal in plan |
| 679, along 680's foot, 82-110 | the centre's projection 0.36 to 0.02 m outside the crease edge (679-680) with the edge 0.50 down to 0.27 m from the centre (radius 0.35): the edge case of step 1. He moved 0.013-0.015 m across the ground (0.42 m/s) and 0.012 m up per update, turned 20-30° north of his aim, while his asked speed (+0x1ac) and velocity stayed 1.654 m/s; the "could not move" counter stayed 0 |
| inside 680, 111-155 | the projection inside the face (step 1's first case), the centre sinking to 0.21 m from its plane; feet still on 679 until update 140, then on 680 itself (n.z 0.526): 0.33 m/s across and 0.45 m/s up |
| crest | the route moved on to (−280.76, 130.48) at update 160, before he reached the first waypoint (the route's straight-line skip, AI); he crested at z 3.7, took the drop leg and stopped 1.9 m from the target at update 487 |
The same run with his body +0x60 raised to 1.3429 (radius 0.470, Coney's) was alike: the crease edge 0.49 down
to 0.35 m from the centre, the projection 0.27 to 0.05 m outside it, 0.013-0.015 m across and 0.012 m up per
update, never blocked; this passes within 0.5 m of the point where Coney's bum stalled before 2026-10-11. Neither
run tripped the move action's stuck test (0.2 m in 60 updates, AI): about 1.1 m was covered
in each 60 updates on the heap. The waypoint was not skipped to get round the heap, and nothing warped him.
The push-out PhysicsBody_PushOutOfWalls (0x003477c0, a sphere out of the nearest wall by n × (r − distance))
is not the walking case: its only caller Human_PushOutInAir (0x0021a490) runs while the human is airborne
(object flag 0x4000000), in a few states (0x00227ef8, 0x00223b48, record +0x08 0x400000, anim id 2 or 4),
and not in states 0x00223920 / 0x00223980. Its sphere has radius 0.35, or 0.5 for a player, times the scale,
centred r + 0.05 above the feet (at bone 2 in states 0x00227ef8 / 0x00223b48). In a long fall (state flag
0x1000000000) and some states it instead pushes out a 0.2 m capsule between bones 6 and 3 (0x0033e7f0, the
average of the contacts). Confirmed (code).
Falling and landing¶
Starting to fall (0x0023dc58): march a ray along the velocity (Collision_MarchRay, steps of 0.1 m, up to 5 m)
to find where the fall will end (kept at +0x2c0; the current position if nothing is hit), set the airborne flag
(0x003a2158) and switch the body to its falling mode (body slot +0x2c with 2). For a player whose follow camera is
in its normal mode, a ray 2.5 m down from 0.75 m ahead of the feet checks for a drop and, if there is no ground,
tells the camera (0x0012da20 with the heading, −1.0): the camera reacts to a ledge.
Landing. While airborne, the sweep tests the segment from the body's upper point (feet + +0x4e8 − 0.16 × scale)
to the moved feet against the collision mesh (0x0023e408, through WorldManager_RayCast); a hit on a triangle with
n.z > 0.65 is a landing contact. The contact handler (0x00219d50) then calls Land(human, 1, 1)
(0x0023e090): clear the airborne flag, apply fall damage by the vertical speed at impact
(vz ≥ −14.9 m/s: none; between −14.9 and −20.5: a share of the maximum health, (vz + 14.9) / −5.6, at least the
class's minimum +0x118; below −20.5 m/s: the maximum health, so a fall that fast kills), tell a player's camera
when vz < −1.5 (camera slot +0x15c with the landing kind 1-3), set +0x3a0 (the vertical velocity) to 0 and
write the velocity back. The thresholds are 0x00510674 (−14.9) and 0x00510678 (−20.5). Free fall from rest
reaches 14.9 m/s after about 7.1 m and 20.5 m/s after 13.4 m (inferred from the gravity above).
When the landing happens (the feel pass's second runtime run, 2026-10-05, PCSX2 2.9.94, slot 1, a run jump with stick 100 % and triangle tapped 35 updates in; the arc's phase shifted by adding 0.015 to 0.15 m, or taking 0.05 or 0.12 m, to the feet's height at the 7th update in the air; ground at 0.2231). Confirmed (runtime):
| Feet at the start of an airborne update, below the ground | That update |
|---|---|
| above it, or 0.041, 0.091, 0.111, 0.141 or 0.161 m below | still airborne: the body falls a whole step (0.20-0.22 m) more |
| 0.176 or 0.191 m below (and 0.241-0.379 m) | lands: Land, the feet snapped up onto the ground, full horizontal speed |
So the feet are not stopped at the ground on the update they pass it: the jump lands on the first update that
starts with the feet about 0.17 m (between 0.161 and 0.176 m) or more below the ground, and until then the body
keeps falling through it, up to 0.38 m below in one phase. The unshifted jump (the street's arc) crosses with the feet
0.009 m above the ground, ends that update 0.191 m below and lands on the next: the 0.19 m dip of the per-update
trace is this, not a sampling artefact. The code path is the sweep's landing contact (0x0023e408, run from
0x0033d340 when body flag 0x40 is set; segment from feet + +0x4e8 − 0.16 × scale, 0.880 m for the player,
to the moved feet), which records the contact's fraction as (hit − 1.0) / (length − 1.0), or 0 when the hit is
nearer than 1.0 m (the human's vtable +0x5c, the constant 1.0 at 0x004ed818); why the hit is not taken until the
feet are about 0.17 m under is not traced. What the code shows (confirmed (code)):
- the segment's upper point is
0x00226a20: the feet ++0x4e8− 0.16 × the scale; - only a hit with a normal
zabove 0.65 records a landing contact; 0x0033d340marks the contact (+0xb0=0x80) and its handler0x00219d50callsLand(1, 1)(confirmed (code));- contacts are kept sorted by fraction (
0x0033d718), and the resolution0x0033d9d8acts on the first contact whose handler returns a code that is not 0, then returns (confirmed (code)); - the body's move loop
0x0033e278runs the sweep up to 3 times (confirmed (code)).
Read alone, this lands on the update the moved feet pass the ground, which the runtime contradicts; the 0.17 m does not follow from the 0.88 / 1.0 m geometry either. A log of the contacts (a code patch that copies each contact's fraction and handler code to free memory) would settle it.
A fall at runtime (PCSX2 2.9.94, level99, no stick input; Rembrandt raised 10 m by writing the transform table's
z). Confirmed (runtime):
vzchanges by −0.5227 m/s per update from the second airborne update, andz(n+1) = z(n) + vz(n+1) × dt: the velocity is updated before the move.- Clip 428 (drop cycle) plays while falling.
- Landing after 34 updates at
vz= −17.25 m/s, inside the damage band; then 429 (drop land), 294 (a hit reaction) and 198 (a ground roll).
Anim states in the air. Human_ChooseAnimState (0x00259578) checks the state flags first: 0x2000000000 →
anim state 27 (landing), 0x800000000 or 0x1000000000 → 26 (falling), 0x400000000 → 25 (jump).
Confirmed (code). The builders, confirmed (code):
- 25 (
0x0025fba0→0x0025cf30): the jump loop (434) after a 0.1 s fade, then the launch (Jumping). - 26 (
0x0025d020, confirmed (code)): the drop cycle (428), or the long-fall cycles 422 / 425 in a long fall (0x400000,0x00223b48,0x00227e60). After a jump the jump loop keeps playing (runtime). - 27 (
0x0025d390): after a jump, 436 (jump end running) handing over to a gait blend when the stick is above 0.12, else 435 (jump end) and then the idle; after a drop, 429 (drop land); after a long fall (0x1000000000), the clip after the current one, or 198 (ground roll).
Walking off an edge at runtime (the roof of a car, 1.48 m up; stick 0.5). Confirmed (runtime): state 26 with 428
and record +0x08 0x400000 for the first two updates, gravity as above, then state 27 with 429 for about
0.5 s and the idle.
Jumping¶
The button. On command 10 (triangle pressed), Player_UpdateActions (0x0027c120) tries, in order, and stops at
the first that succeeds. Confirmed (code):
- a climb (
Climb_TryStart, below), if human flag0x20000000is clear and the stick is above 0.12; - a context action (
Player_TriangleAction,0x002811f0, confirmed (code)), if L2 is not held; - a jump (
Player_TryJump,0x002829e8, confirmed (code)), if human flag0x10000000is clear and the stick is above 0.95; - the drop of what is in hand (
Human_DropHeld,0x00257f38), if L2 is not held (Triangle with something in hand).
The object action is the drop: 0x00226ff0 is Human_GetHeldObject (+0x338) and 0x00257f38 is
Human_DropHeld, which gives the held object back its physics body and clears the hand. Confirmed (code). There is
no jump buffer in this order: triangle is acted on in the update it is pressed. So, inferred from the order and the
jump's refusal while carrying a class 4 or 6 object: triangle with a weapon in hand and the stick pushed toward a
climbable wall that is still out of the climb's reach (the climb fails, the jump is refused) drops the weapon, as
checklist item 2 reports of Coney; whether that is what the original does at runtime is not checked.
Nothing happens while state flags 0x7bf9e9f7ff0 or record +0x08 flags 0x5cfeafb are set.
Checks (Player_TryJump, then Human_BeginJump 0x0023db48). Confirmed (code). The jump is refused when the human
carries an object of class 4 or 6 (0x00224000), is in a combat stance (0x00228340), moves at 3.3 m/s or less
(0x0051018c, speed +0x1ac), is not a player, or a ray from 1.7 m above the feet, 5.5 m long in the stick's
direction (0x0021d228), hits a climbable triangle (the rule of Climbing): near a climbable wall the
button climbs or does nothing. Human_BeginJump then needs the gait for the speed (Human_GaitForSpeed) to be 3 or
more and |v| at least 3.3; it keeps the gait at +0x3c0 and the height at +0x560 and sets state flag
0x400000000.
Launch (Human_LaunchJump, 0x002217f0, called by the state 25 builder). Confirmed (code):
- horizontal velocity: the current direction times the run speed for a take-off gait of 3 or 4, the sprint speed for gait 5 (the jog speed below 3);
- vertical velocity 5.5 m/s (
0x00510188, read byHuman_LaunchJump; confirmed (code)); Human_StartFall(vtable+0x154), then the state functionHuman_AirControl(0x00240898, set through0x00227c28) and anim state 26.
In the air (Human_AirControl). The horizontal speed is kept. With the stick above 0.12 the heading turns toward
it at the Human_MaxTurn limit with the same ease as on the ground, and the velocity turns with it; with the stick
centred nothing changes. Gravity, the airborne counter and the landing are those of a fall. Confirmed (code).
At runtime (the street, Rembrandt on flat ground, triangle tapped for one update). Confirmed (runtime):
| Case | What happened |
|---|---|
| stick 0.8 (walk at 1.63 m/s), triangle | nothing |
| stick 1.0, run at 7.80 m/s, triangle | state 26 and clip 434 at once, state flags 0xc00000000; vz 5.5 then −0.5227 per update; apex 1.06 m above the take-off; horizontal speed 7.80 throughout; landed after about 25 updates (0.83 s), 6.4 m further |
| stick 1.0 and L2, sprint at 10.245 m/s, triangle | the same arc at 10.24 m/s horizontal, about 23 updates in the air |
| the same, stick turned 90° right just after take-off | the heading turned by about 4° per update toward the stick, the horizontal speed staying 10.24; that is the run's limit, not the sprint's 2.5° (which gait the air turn uses is not traced) |
| landing | state 27, 436 for about 0.4 s at its own 4.23 m/s, record +0x08 0x1000000, then the gait blend from a jog back to the run |
| per update (the feel pass, slot 1) | 24 updates in the air from a run: the last airborne update leaves the feet 0.19 m below the ground and the next one lands them (the landing rule); that landing update still moves at the full 7.80 m/s, then 436 holds 4.23 m/s for 10 more updates (11 in all) and the gait blend gains 0.8 m/s per update from there; take-off to landing 6.24 m |
| triangle during the run start (414), at 3.35 m/s | a jump at once: the run start does not block it, only the 3.3 m/s and gait tests do (slot 7) |
Clips 427 (slot 25) and 430-433 (jump from idle, walk or either foot) did not play in the player's jump, nor in
Vermin's route jump in level99 (clip 434 at take-off, 435 on landing; runtime): what plays them is not traced.
An AI's jump. Human_BeginJump's gait and speed tests apply only to the player's brain (type 0): an AI may jump
from any gait, standing too, and its +0x3c0 is not written. Human_LaunchJump gives a human whose per-player record
+0x1b is 0 the velocity of the route follower's arc instead (0x0029ade0): the lowest of the vertical speeds 0.5,
1.25, 2.0, ... m/s whose arc lands on the route's landing point at no more than 10 m/s in plan. Confirmed (code), and
at runtime for Vermin (3.5 m/s up, 9.55 m/s across a 4.1 m gap); the rest is in
AI: Jump legs.
In an input script: stick left 0 100, wait until the run start is over and the speed is above 3.3 m/s, then
tap triangle; add press l2 beforehand for a sprint jump.
Triangle with something in hand¶
User checklist item 2 (an early press at a wall drops the weapon instead of climbing). The four tries above, read
again for a held object; confirmed (code) at Player_UpdateActions (0x0027c120, both its triangle blocks),
Climb_TryStart (0x002826f0), Climb_Start (0x00281c20), Player_TriangleAction (0x002811f0),
ContextAction_Use (0x0024d530) and Human_PickUpSearch (0x0024d810):
- There is no buffer. Command 10 is one sample (Commands) and each try runs once, on that
update. An "early" climb is only the long ray: 4.5 m ahead for a player (human flag
0x2) at gait 4 or 5, else 1.5 m. - The climb comes first and ignores what is in hand.
Climb_TryStartrefuses only on record+0x080x84240; nothing about a held object. When the climb starts,Climb_Startdrops an object of held set 4 (overhead: barrels, crates) always, and one of set 6 (the ghetto blaster) unlessCfgClimbWithGhetto(0x00510250) is set (confirmed (code) inClimb_Start); a weapon (sets 1-3) or a one-handed throwable (5) stays in hand through the climb. - A press slightly out of reach is swallowed.
Climb_Startreturns "done" for a player (per-player+0x1bnot 0), without climbing, when the face is beyond the clip's maximum distance but closer than twice a clip's reach (which clip's reach the decompiler hides; open question), and when the space at the top is not clear (Climb_IsSpaceClear). The dispatcher then returns with nothing more: no jump and no drop. An AI gets "not done". - The context action never drops for a player with nothing to take.
Player_TriangleAction→ContextAction_Use→ (no context record) the pick-up search. With an object found and something in hand (not kind 12 or 24), it drops what is held and sets human+0x5b8; the history-hold triangle (0xb, 7 samples) then takes the new one (Player_TriangleActionagain,+0x5b8cleared). With nothing found, a pad-controlled human gets 0 (no drop); only an AI (+0x1b= 0) drops here. - The jump: refused while carrying held set 4 or 6 (
Human_HoldsKind4Or6), in a fight stance, at 3.3 m/s or less, or with a climbable face within 5.5 m (above). A weapon does not refuse it. - Only then the drop: with L2 not held and an object in hand,
Human_DropHeld(0x00257f38) and+0x5b8= 1 (confirmed (code)).
So with a bat at a run toward a climbable wall: within 4.5 m (and inside the clips' distances) he climbs with the bat; just beyond the clips' distance the press may be swallowed; farther but within 5.5 m nothing climbs or jumps and the bat drops (the original does this too); beyond 5.5 m, at a run, he jumps with the bat. Empty-handed, the last two cases do nothing and jump.
Coney fixes (item 2; today PlayLevelMode's context action returns TriangleResult::Drop at try 2, before the
jump, src/platform/play_level_objects.cpp):
- Remove the drop from the context action for the player: with nothing to take it returns "nothing" (an AI keeps
the drop there). With an object to take and something in hand, drop and remember the swap (
+0x5b8); the triangle history hold (7 samples) picks the new object up. - Add try 4 in
Human::tryActions(src/human/human.cpp): after a refused jump, with L2 not held and an object in hand, drop it. - The climb with an object: keep weapons and set-5 objects in hand; drop set 4, and set 6 unless
CfgClimbWithGhetto. Check that a player carrying a weapon at a run is gait 4 or 5 for the 4.5 m ray (a slower armed run speed must not shorten the reach to 1.5 m). - Add the swallow of try 3 when Coney's climb distances say "too far by less than twice the reach": the press ends there.
- Tests: with a bat, at a run, triangle 3 m before a climbable fence climbs and keeps the bat; 5 m before it drops the bat; 8 m before in the open it jumps and keeps the bat; with a barrel (set 4) the climb drops it.
Climbing¶
Climb_TryStart (0x002826f0) runs on triangle with the stick above 0.12 (Jumping). Confirmed (code):
- Refused while record
+0x08has any of0x84240. - Direction
d: the stick's direction when its magnitude is above 0.01, else the facing (Player_GetStickDirection,0x0021d228; confirmed (code)). - Two horizontal rays along
dfrom 1.7 m and 0.69 m above the feet, 1.5 m long, or 4.5 m when human flag0x2is set (players) and the gait (+0x1a8) is 4 or 5. - If both hit and their distances differ by less than 0.1, the obstacle is tall (the high hit is used); otherwise it is short (the low hit is used; a hit by the high ray alone is not a climb).
- The hit must be climbable: material 30 (
MATERIAL_LOW_FENCE), or triangle flag0x4for a player, or triangle flag0x80(Collision). Both probes areWorldManager_RayCast, the level's static mesh only, so a parked car is climbed through its shell in that mesh (every triangle with flag0x4, material 0, never0x80, so only players climb cars; below). Onlevel99_2's sedan shells the 0.69 m ray meets a side (0xf00dor0x700d) and the 1.7 m one passes the 1.71 m roof or meets the sloped screen at another distance, so the car is short. The down probe from 1.8 m finds the sill or bonnet at 1.18-1.30 m above the ground, inside 0.7-1.7 m: a short wall climb (455 / 458). Inferred from the code and the disc's mesh; not run. Climb_ProbeTop(0x00282370) with, for a tall obstacle,H= 3.0, a window of 1.7-2.91 m and the fence ids 437 / 440; for a short oneH= 1.8, a window of 0.7-1.7 m and the short-fence ids 443 / 446.
Climb_ProbeTop, confirmed (code):
- The face must look at the player:
n · d < −0.7(within about 45°). - A ray straight down from
Habove the point 0.4 m beyond the face (pos + d·t − 0.4·n),H + 0.5long, finds what lies just behind the face; its top isHminus the hit distance, a height above the feet. - A top inside the window is a wall climb: the ids + 12 (449 / 452 wall, 455 / 458 short wall).
- Otherwise a top of 0.25 m or more refuses the climb.
- Otherwise (no floor just behind the face, or one near the feet' height: a fence), for a tall obstacle a ray
along
dfrom 2.5 m above the feet must miss (the fence is lower than 2.5 m), and the fence ids are kept.
Climb_Start (0x00281c20), confirmed (code):
- The first id is the standing clip; the running one (+3) is used when human flag
0x2is set and the gait is 4 or 5. Each clip's reach is the length of its type-8 event vector (0x00101558, through the Anim Range List,0x002544a0). A running climb needs the face between(r1 + r2) × 0.4andr2 × 2.2away (the reaches of the first and second clips); a standing one at most(r1 + r2) × 0.4.CfgClimbWithGhetto(0x00510250) is also read here. - The start point is the hit at the feet' height plus
n × 0.9 × reach, kept at+0x2c0; the human is turned to face the wall and moved there over 1/60 s or 1/15 s. - The three clips play in turn (P1, P2 = id + 1, P3 = id + 2). At P1's end (
0x00281450running,0x00281838standing; confirmed (code)) the forward probe is made again from 0.69 m (2.5 m long running, 1.5 m standing). On success the body is moved at once by P2's root displacement turned to the facing, body+0x40gets0x80000000 | 0x4000, and record+0x08gets0x40(the fences stop blocking, Walls and steps); on failure the climb ends in the idle or the combat idle. A running climb ends in a gait blend, a standing one in the idle. - While clips 437-460 play,
+0x5b9is 1 (Human_StateUpdate). - From P2 on nothing collides. While record
+0x08has0x40,Human_Move(0x0023d8c8) skips the wall sweep (PhysicsBody_Sweep) and the ground snap and adds the velocity and the clip's root motion (+0x2f0), height included, straight to the position (Object_SetPosition); the other path zeroes the height and leaves it to the snap. The teleport at P1's end goes through body vtable+0x1cwith no sweep,0x4000stops the body re-posing (0x003422d0), andHuman_GetPositionreturns+0x2c0while0x40is set. So a climbed face of any material (a parapet of material 107 with triangle flag0x80, say) stops the body only during P1; the skip of materials 30, 31 and 122 inHuman_OnContactcovers only contacts that still reach it. Confirmed (code): the branch isHuman_HasHeld40(0x0023d8c8, the test beforeVec4_Add(..., +0x2f0)andObject_SetPosition); the sweep andHuman_SnapToGroundsit only on the other branch. Confirmed (runtime) on a railing of material 7, below. - Over a drop there is no third clip. When P2 ends with nothing under the feet (a railing above a lower floor),
the next update's ground snap misses and the human falls: the fall clip 428 (record
+0x080x400000) in place of P3, gravity from the third update, the landing (436) on the floor below. Confirmed (runtime), the hangout railing below; that the switch isHuman_SnapToGround's miss starting the fall is inferred. - The first clip runs during the move. The same call sets the move to the start point (
0x0023d2b8: over 1/60 s, or 1/15 s when the turn0x0023cf88takes 1/30 s) and installs the three clip tasks (0x00105990,0x001754e8), so P1's clock starts on the climb's first update, not after the move. Confirmed (code). At runtime (the slot 7 fence from a run, Feel comparison) 440 lasted 11 updates: the start update still at run speed, the two snap updates (1.11 and 1.17 m), then 8 updates of the clip's root motion at full speed from the first (5.79 m/s falling to 0.36 m/s), with no fade-in. The first update of 441 and of 442 moved the body 0 m. Confirmed (runtime).
What gets climbed, from the rules above (heights above the feet; inferred from the code, the three rows marked runtime were seen):
| The obstacle | Just behind it (0.4 m past the face) | Climb |
|---|---|---|
| reaches 0.69 m but not 1.7 m | ground lower than 0.25 m | short fence (443-448) |
| reaches 0.69 m but not 1.7 m | a top at 0.7-1.7 m | short wall (455-460); runtime: a trash can (1.28 m) and a car (1.48 m) |
| reaches 1.7 m | a top at 1.7-2.91 m | wall (449-454); runtime: a roof 2.64 m above the trash can |
| reaches 1.7 m, lower than 2.5 m | ground lower than 0.25 m | fence (437-442); runtime: a street fence, from a run |
| lower than 0.69 m, or a top outside the windows, or a fence of 2.5 m or more | no climb |
At runtime (the street; stick and triangle through patched pad input). Confirmed (runtime):
| Climb | Input | What happened |
|---|---|---|
| fence from a run (440, 441, 442) | stick 1.0, triangle tapped every 0.1 s | the tap 4.9 m from the face did nothing (no climb, and no jump: the 5.5 m climbable check); the one about 4.4 m away started 440. The body was snapped 2.5 m forward in two updates (37-39 m/s), then 440 moved it at 5.4 to 4.5 m/s to the start point (0.34 m before the face, 0.3 s); 441 (0.47 s, record +0x08 0x40) carried it through the fence at 2.4-3.4 m/s with the feet at ground height; 442 (0.43 s, 0x80000) and the run went on; 1.27 s in all |
| the same, per update (the feel pass, slot 7, the tap 4.5 m from the face) | stick 1.0, triangle every 2 updates | 440 for 11 updates: the snap moved 1.11 m and 1.17 m, then the clip carried the body until it stopped against the fence, 0.48 m from its face (its walking sphere); 441 for 15 updates through the fence at 2.4-3.6 m/s; 442 for 13; the run's gait blend 39 updates after the tap |
| short wall and wall, per update (slot 8) | stick 0.5, triangle | the rise at 456's start took 2 updates (1.12 m, then 0.17 m by the ground snap); at 450's start 2.30 m, then 0.097 m per update for 4 updates to the roof (2.64 m in all) |
| short wall, standing (455, 456, 457) | stick 0.5, triangle | 455 for 0.3 s (0x80000); at 456's start the feet jumped 1.06 m forward and 1.29 m up in one update onto the trash can; 456 for 0.6 s (0x40), 457 for about 0.6 s, then the walk |
| wall, standing (449, 450, 451) | stick 0.5, triangle, on the trash can | 449 for 0.37 s; at 450's start the feet jumped 0.98 m forward and 2.4 m up, and the ground snap settled them on the roof (2.64 m above the start) over the next updates; 450 for 1.4 s, 451 for 0.37 s |
| short wall onto a car | stick 0.5, triangle | as the trash can, 1.48 m up |
| short fence over the hangout railing (443, 444, then the fall), per update | stick 0.5, triangle every 2 updates | below |
The hangout railing (confirmed (runtime), PCSX2 2.9.94, 2026-10-10, scenario railing_vault: level95, Cleon put
on the clubhouse balcony at (−172.0, 98.3, −194.38) facing +y, 0.63 m from the railing's face, stick 0.5 straight
up from update 60, triangle every 2 updates from 66). The railing is 0.90 m tall, material 7 (LAMPOST) with
triangle flag 0x80, its face at y ≈ 98.93, and the floor behind it is 3.20 m lower
(Collision):
| Updates | Clip, record +0x08 |
The body |
|---|---|---|
| 61-66 | 413, 0x10000000 |
the walk start stops against the railing at y 98.44 (0.49 m from the face) |
| 67-79 (13) | 443, 0x80000 |
0.04 m back toward the start point, then forward to 98.443 |
| 80-98 (19) | 444, 0x40 |
through the railing at up to 1.64 m/s, y 98.443 to 99.326 (0.40 m past the face), the feet at the balcony's height all the way (z −194.378) over the drop |
| 99-118 (20) | 428, 0x400000 |
the fall: z held for 2 updates, then vz −0.5227 per update; at 102 pushed 0.17 m on to y 99.50, clear of the railing's back; 445 never plays |
| 119 | 436 | landed on the floor below (z −197.576), 3.20 m down, 0.57 m past the face; then the landing and the walk |
So the railing's triangles do not stop the body during 444, though material 7 is not among the materials
Human_OnContact skips: the wall sweep is not run at all (above).
The vertical part of a climb is the single move at P2's start: the feet do not rise during P1 or P2.
In an input script: standing, stick left 0 50 toward the obstacle and tap triangle within reach; from a run,
stick left 0 100 and tap triangle when the face is 4.5 m away or less.
Parked cars are shells in the level mesh¶
Nothing builds collision triangles from a car: Car_MakeBodies (0x00387d50) gives a car one body with two boxes
sized from its model's record (0x0057e4c0 + model × 0x5f0), and the only code that switches mesh triangles is the
script's ChangeCollision (0x00379c20 → CollisionMesh_SetEnabledInVolume). Confirmed (code). The level files
carry the shells. level99_2's mesh has three closed car-shaped boxes, the number of cars SetupCars spawns
(car_coupe, car_osedan, car_osedan01), so they are inferred to be these cars. Two are 5.74 × 2.2 m, with the
sill at 1.18-1.30 m, the roof at 1.71 m and sloped screens; one is 5.11 × 2.3 m with the roof at 1.67 m. All are
material 0. Every triangle has bits 0, 2 and 3 (low bits 0x00d) and a top value of 3-15: the long sides 0xf00d /
0x700d, the ends 0x400d / 0x700d, the roof and bonnet 0xc00d / 0xd00d. No triangle has bit 7 (0x80). Read
from the disc. Across the disc's levels, 2,530 triangles with these low bits and material 0 sit in 19 levels. So a
car's climbable surface is wherever the level put a shell, not where the car is: a car that CreateCar or CarSpawn
places away from a shell, or one moved off its shell, has no mesh surface for a walker, a climb probe or a ray (rays
and climbs test the level mesh only, WorldManager_RayCast 0x0040db88). Bit 2 is the player-climbable bit
(Collision, and what stops a walker at a car is on Physics); the climb
itself is above.
The spinning icon¶
The marker over a human's head (a mission target's star, a dealer's sign, the co-op player markers, the crown in
Rumble) is a 3D world object of class dyn_icon, attached to the human; it is not a HUD sprite. One per human,
its handle at human +0x360. Confirmed (code) at the cited addresses unless marked; not checked at run time. The
dealer's case is also on AI: the dealer's icon.
Making one (Human_ShowOverheadIcon, 0x002271f0; reached from HuAttachSpinningIcon 0x00238a88,
GangAttachSpinningIcon 0x0016b2c8 for each of the gang's 16 member slots, and the game's own callers):
- The name is the object type to spawn (any
dyn_icontype:dyn_rockstar,dyn_crown,dyn_exclamation,dyn_gangrat,dyn_weapdeal, ...; the object list). A name whose first 9 characters aredyn_p_onebecomesdyn_play_one, anddyn_p_twobecomesdyn_play_two, each with_euroappended when game state+0x120is set (inferred: a European language). - When the human already has an icon of that type nothing happens; another icon is removed first (message
0x15). Task_CreateObjectByNamespawns it with two message arguments, the human's handle and the attach bone (the script's third argument; every script and every game caller passes 0, the human's own transform).
The script arguments (from the bindings 0x0035aeb0, 0x0035f128, 0x0035af80):
| Function | Arguments |
|---|---|
HuAttachSpinningIcon(human, name, bone) |
the human's handle; the type name; the pose bone the icon hangs from, 0 = the human |
GangAttachSpinningIcon(gang, name, bone) |
as above, for every current member; gang −1 does nothing |
HuSetSpinningIconColor(human, steps, colour) |
a fade: the step count, then the target colour word; the colour defaults to 0xFFFFFF00 (white, alpha 0) |
HuRemoveSpinningIcon(human) |
message 0x15, the handle cleared (0x00227388) |
HuSetSpinningIconColor's two numbers are not two colours, as its argument names in the
binding reference suggest: the message's argument stack
is last in, first out, so DynIcon_SetColourFade (0x003e93d0) pops the second script argument as the step count
and the third as the target colour. The game's own call, (human, 3, 0xFFFFFF00) in 0x0024b2a8, fades an icon
out in 3 steps. No level script calls it.
Init (DynIcon_Init, 0x003e8fa0):
- Model: the object type's own model (
Obj_SetModel(obj, 0)), at its own size: no scale is applied. - Attachment:
Obj_Attach(obj, human, bone); its world pose is the human's transform (position and rotation) ∘ the bone's (none for bone 0) ∘ the icon's local pose (World objects: the attachment). Flags+0x54=0x91. Update every 2 ticks. - Local position: (0, 0, 2.25) m; (0, 0, 2.5) for
dyn_weapdeal,dyn_flashdealanddyn_spraydeal(model hashes0x30f09efb,0x51d27ab6,0xd939c21d); the type record's offset (property 11) fordyn_cross(0xe3f92003). The human's transform is at his feet (inferred), so the icon floats 2.25 m above the ground. There is no bob: nothing moves the local position afterwards. - Local rotation: the quaternion (0, 0, 1, 0) (
0x00511700), a half turn about z. - Spin: angular velocity (0, 0, π) rad/s (
π × (0, 0, 1),0x00511740): a half turn a second about the local z axis, which is the human's up axis, one turn in 2 s. The attached pose advances the local rotation by it (World objects: the attachment). - No spin for model hashes
0x8b7fe995,0x7644ee5d,0x1de2e2ca,0x1a72a32d,0x687e742b: the angular velocity is zeroed and flag bit 1 cleared. These are the player markers (dyn_play_one,dyn_play_twoand their_euroforms) anddyn_lizziestarget(AI); they keep the half-turn local rotation, so they turn with the human. dyn_cuffs(0x464ac521) takes the type record's offset and rotation (properties 11 and 12), turns at π/2 rad/s, and is detached at once, so it stays where it appeared (Crimes: arrest).- Colour state (5 words): not fading, colour
0xFFFFFFFF(white, opaque), the rest 0.
Colour (DynIcon_Update, 0x003e92e0, every 2 ticks). The colour is the world object's tint word (+0xcc,
copied to +0xc8 at the next update; 0xRRGGBBAA), which multiplies the model's colours and its alpha its opacity
(World objects: the tint):
- Not fading:
+0xcc:= the stored colour, each update. - A fade (message
0x34,DynIcon_SetColourFade): steps 0 sets+0xcc:=0xFFFFFFFFand stores the colour, which the next update writes (not fading, above). Otherwise step := (+0xc8− target) ÷ steps, an unsigned division of the whole 32-bit word, not per channel; each update the counter goes up, and+0xcc:=+0xc8− step until the counter reaches the step count, when+0xcc:= the target and the fade ends. The game only fades the alpha byte, where the word arithmetic is a plain linear fade; a port that fades other channels must keep the word arithmetic to match. Because the step is truncated, the alpha jumps to the target at the end: from 255 to 0 in 80 steps it falls by 3 an update to 18, then to 0.
Hidden (confirmed (code); the visible result inferred from the tint rule, an alpha under 10 is not drawn):
- While the letterbox is up: when not fading and player 1's screen-effects record (
0x005fdeb8) has the letterbox state (+0x1e8) set or its level (+0x1ec) non-zero (Screen effects),+0xcc:=0xFFFFFF00and+0xc8:=0xFFFFFF01: alpha 0. Not fordyn_cross; not during a fade. - Message
0x0a(argument 0 hides, non-zero shows) sets or clears the hidden flag (+0x54bit 4);0x20sets it and also clears the attached bit (0x10); 8 detaches;0x15removes the icon (DynIcon_OnMessage,0x003e9470). - Distance: an attached object uses its parent's
ObjShowdistance and is not drawn beyond it (World objects: the tint). - Removal: when the human is beaten (
Human_CreditDefeat,0x00230740) or destroyed (Destroying a human, message0x15to+0x360), by the death sequence's camera (which removes the players' icons, Camera), and by the goals that made it.
Drawing: as any world object (ObjectRender_Draw 0x0017fd78, World objects: the tint):
the model with its own materials, lit and Z-tested like the other world objects, the tint multiplying it, alpha
scaled by the distance and fade-in rules there (inferred: nothing in dyn_icon changes the draw).
The co-op player markers (0x0024b2a8 at a respawn, Reticules_UpdateTarget 0x0024b3d8 at the hints): with
two players on one screen, not in Rumble and not during the letterbox, dyn_p_one / dyn_p_two (so
dyn_play_one / dyn_play_two) on the players, then a fade to 0xFFFFFF00: from alpha 255 over 80 updates (160
on an armies level, GameState_IsArmiesLevel 0x0041d110), or at a respawn from the alpha byte passed in over 3
updates (+0xc8 and +0xcc set to 0xFFFFFF00 | alpha first). HUD has the context.
Code index¶
Every function of the human code in 0x00217a70-0x00288000 (inferred: the range is our grouping, and each row is
graded) that the sections above do not walk through, in address order, with what it does. Names are ours, as in the
local Ghidra project, where each function also carries a plate comment. Combat functions are indexed on
Combat and the crimes' on Crimes.
The human core¶
0x00217a70-0x00233ef0, around Human.cpp's path string (0x0021c7c8; the range inferred, each row graded): pose
writes, fire and hit effects, the grab sound, voice lines and anim sounds, the record's accessors and state-flag tests,
the class tables, targets, getting up and the overlay clips. Field offsets are on the human object
(above) and the record (above); the pose slot is the human's entry (+0x92) in the
task pose table at 0x00714b00, 0x20 bytes each. A human flag is a bit of +0xe0, a state flag one of record
+0x00, a held flag one of record +0x08.
| Address | Name | What it does | Evidence |
|---|---|---|---|
0x00217a70 |
Human_GetClump |
The human's clump from its render instance (+0xd8), or 0 without one. |
confirmed (code) |
0x00217dd8 |
Human_ReleaseInstance |
Knocks the human out, clears state bit 0, releases its dynamic anim slots (+0x3c8) and gives its character instance back to the resource manager. |
confirmed (code) |
0x00219238 |
Human_StartBurning |
Sets the burn end time, sets state 0x4000 (burning), starts the fire effect (0x0021adf8), deals health + 1 damage, drops a held kind 4 or 6 object, breaks any pair and says the burning command. | confirmed (code) |
0x00219320 |
Human_FindPickupNear |
Overlap test around the human for world objects it may pick up: skips door leaves and objects it cannot see, then picks up the first that fits. | confirmed (code) |
0x00219638 |
Human_ApplyTypeScale |
Per character type (+0xcc) model scale (for example 1.03 for type 0x21, 0.98 for 0x15); stores it with Human_StoreScale. |
confirmed (code) |
0x00219a28 |
HazardSphere_Contains |
True when the human is inside any active fire hazard sphere of the table at 0x0065ff50 (gated by 0x00510170). | confirmed (code) |
0x00219ad0 |
Human_IgniteInHazard |
Sets the human on fire when HazardSphere_Contains says it stands in a fire hazard. | confirmed (code) |
0x0021ada0 |
Human_EmptyStub1 |
Empty function (returns at once); a vtable placeholder. | confirmed (code) |
0x0021ada8 |
Human_EmptyStub2 |
Empty function (returns at once); a vtable placeholder. | confirmed (code) |
0x0021adb0 |
Human_StopHeatEffect |
Stops the heat-distortion effect +0x6c4 and resets it to -1. |
confirmed (code) |
0x0021adf8 |
Human_StartFireEffect |
Spawns fire particles on random bones (eight tries) and starts the heat-distortion effect stored in +0x6c4. |
confirmed (code) |
0x0021b048 |
Human_SetPosition |
Writes the position into the human's pose slot (0x00714b00 + slot x 0x20) and pushes the pose to the task. | confirmed (code) |
0x0021b100 |
Human_SetRotation |
Writes the quaternion into the pose slot (+0x10) and pushes the pose to the task. |
confirmed (code) |
0x0021b148 |
Human_PrepareGrabSound |
Prepares anim sound 0x3c (the grab sound) at the human for the partner's sound handle +0x180, when not already prepared. |
confirmed (code) |
0x0021b1f0 |
Human_StopGrabSound |
Stops the prepared grab sound +0x180. |
confirmed (code) |
0x0021b228 |
Human_StartGrabSound |
Starts the prepared grab sound +0x180; when it was not prepared plays anim sound 0x3c at the human instead. |
confirmed (code) |
0x0021c080 |
Human_ClearStrikeContacts |
Clears the physics body's struck-body contact list when the body exists. | confirmed (code) |
0x0021c418 |
Human_CapsuleOverlaps |
Builds a capsule of the human's height at a pose and asks physics whether it collides with anything; used by a placement check (0x002b8ad8). | confirmed (code) |
0x0021cce8 |
Human_Destruct |
Human destructor: releases speech, effects, the body and the record before the base object destructor. | confirmed (code) |
0x0021cf20 |
Human_FreePhysicsBody |
Destroys the human's physics body +0x1a0 and clears the pointer. |
confirmed (code) |
0x0021cf80 |
Human_GetHeadLookOffset |
Rotates the offset +0x500 by the human's orientation; Human_UpdateHeadLook uses it as the look origin. |
confirmed (code) |
0x0021d080 |
Human_GetPosition |
The human's position: the carried pose +0x2c0 while grabbed or carried, else the pose slot. |
confirmed (code) |
0x0021d0e0 |
Human_GetHeading |
The heading angle of the human's orientation (Quat_Heading on the pose slot). | confirmed (code) |
0x0021d118 |
Player_GetMoveHeading |
The player's stick heading when the stick is pushed past 0.01, else the human's facing (0x00254310). | confirmed (code) |
0x0021d310 |
Human_ResetStats |
Resets the brain, then health, power and stamina from the character class and reapplies the class damage ranges. | confirmed (code) |
0x0021d530 |
Human_GetScriptPrompt |
Returns the prompt of the human's script handler (+0xe8). |
confirmed (code) |
0x0021d550 |
Human_GetScriptValue |
Returns the value of the human's script handler (+0xe8). |
confirmed (code) |
0x0021d570 |
Human_HasScriptHandler |
True when the script handler (+0xe8) has a handler or the force flag +0x1b2 is set. |
confirmed (code) |
0x0021d848 |
Human_SetPushable |
Clears (on) or sets (off) body flag 0x200 and stores the choice in +0x3bf. |
confirmed (code) |
0x0021d880 |
Human_SpawnParticleAbove |
Spawns a named particle at the human's feet plus 1 m. | confirmed (code) |
0x0021d928 |
Human_SpawnHitEffect |
A hit on a human: a sub_punch_flash whose blood count comes from the attacker's weapon and the victim's hurt and blocking state; a kind-39 object queues a blur pulse and hint 26 instead (Combat: hit blood). |
confirmed (code) |
0x0021de48 |
Human_SpawnAnimEffect |
A clip's type-0x21 effect event: spawns sub_ + its code at the human's bone (bld blood, pch blood or sweat by hurt, blo blood and a decal, and the non-blood codes) (Combat: hit blood). |
confirmed (code) |
0x0021e3a8 |
Human_GetSoundVolumeScale |
1.0, or the combat-framing volume (game state +0x24c) for a player while the camera frames a fight. |
confirmed (code) |
0x0021e940 |
Human_UpdateSpeech |
Per update speech: when the line ends, clears speaking state and runs the end callback once; while it plays, moves the stream to the head and faces the partner. | confirmed (code) |
0x0021ec30 |
Human_GetSpeechHandle |
Returns the current speech line handle +0x178. |
confirmed (code) |
0x0021ec38 |
Human_StopSpeech |
(human, force): stops the current line when it is alive and either forced or cut-able (+0x194 set); ends the speaking pose, sets +0x194 and +0x195 and drops the queued follow-up (+0x184); never touches the Lua callback, which runs at the next Human_UpdateSpeech (Sound). |
confirmed (code) |
0x0021ed28 |
Human_SayCopLine |
Says a cop command line; on success stores a callback hash so the script hears when it ends. | confirmed (code) |
0x0021ede0 |
Human_PlayNextVoiceLine |
Plays the next line of voice bank 0x29 at the human when the shared voice slot 0x005e6d80 is free, and takes that slot. | confirmed (code) |
0x0021ee50 |
Human_PlayVoiceByName |
Hashes a fixed name (0x005d91e0) and plays it through Human_PlayVoiceAt. | confirmed (code) |
0x0021ee88 |
Human_PlayVoiceAt |
Plays a voice line by hash at the human's position into +0x168 unless one is already playing. |
confirmed (code) |
0x0021efb0 |
Human_UpdateVoicePosition |
Moves the voice line +0x168 to the human's position; false when none plays. |
confirmed (code) |
0x0021f058 |
Human_StopVoice |
Stops the voice line +0x168 when it plays. |
confirmed (code) |
0x0021f0a0 |
Player_PrepareLine |
For a player: prepares a line by hash at the human into +0x17c, reusing it when the same hash is already prepared. |
confirmed (code) |
0x0021f1d0 |
Player_StopPreparedLine |
For a player: stops the prepared line +0x17c. |
confirmed (code) |
0x0021f208 |
Player_StartPreparedLine |
For a player: stops other speech and starts the prepared line +0x17c as the current line +0x178. |
confirmed (code) |
0x0021f290 |
Human_PlayFootstep |
Plays the material pair of a material and the ground under the human (+0x1d8, remapped): twice as loud for a player, half when hidden in shadow (Sound). |
confirmed (code) |
0x0021f410 |
Human_SayAnimLine |
An anim entry's first sound as a speech line, only when the human may speak; over a playing line only when asked, cutting it when asked (Sound). | confirmed (code) |
0x0021f548 |
Human_PlayAnimSound |
Plays an anim entry of the sound matrix at a point, owned by the human; a player's louder, with extra columns under combat framing (Sound). | confirmed (code) |
0x0021f700 |
Human_OnAnimSoundEvent |
Clip event 11 (message 0x8b): by the anim sound id plays the matrix entry, a footstep or material pair, a speech line or a command (Sound). | confirmed (code) |
0x00220360 |
Human_HasIdleDialogLine |
True when the idle dialogue line for the human's voice, level and line counter exists. | confirmed (code) |
0x00220430 |
Human_PlayIdleDialogLine |
Plays the human's next idle dialogue line and advances its counter. | confirmed (code) |
0x002207d0 |
Human_SayObjectLine |
Says a command about an object unless the game mode is 0xb or the object is one of three types (by hash). | confirmed (code) |
0x002208f0 |
Human_SayStateResponse |
Plays a state response line when the human can speak and the listener is within the line's range less 20 m. | confirmed (code) |
0x00220df0 |
Hit_ResolveBlock |
Decides a hit's outcome: blocking or ducking (or class 13 with flag 4) blocks; else picks a reaction from the attack flags and height difference and plays the impact sound. | confirmed (code) |
0x00221080 |
Human_RestoreControl |
Restores the control function +0x1bc (a member-function pointer: +0x1be -1, +0x1c0 the function) from the copy saved at +0x1c4; with none saved, a human without a brain task gets Human_PlayerLocomotion, others Human_UpdateControl (0x00227c48). Ends the wheelchair's control. |
confirmed (code) |
0x00221a00 |
Human_GetDynamicAnim |
Clip for an anim id: an override slot (+0x3c8, 7 x 0x28) else the gang clip table entry of the variant +0x3c4. |
confirmed (code) |
0x00221aa0 |
Human_SetAnimOverride |
Puts a clip into one of the seven override slots (+0x3c8) for an anim id, or releases the slot when the clip is 0; marks the state when the current anim is overridden. |
confirmed (code) |
0x00221c20 |
Human_TurnToFacePoint |
Heading from the human to a point (Math_Atan2), then Human_TurnToOver over the given time. | confirmed (code) |
0x00221dd8 |
Human_FaceHuman |
Turns the human toward another (or away with the flag) through Human_TurnToOver. | confirmed (code) |
0x00221ed0 |
Human_SetCarryMoveStyle |
Pushes the move style for a carried object kind; on a wheelchair (style 0x14) kinds 4 and 6 go through 0x00253e68; 0 pops it. | confirmed (code) |
0x002221b0 |
Human_CanWalkStraightToHuman |
Human_CanWalkStraightTo on another human's position. | confirmed (code) |
0x00222370 |
Human_HasClearRayTo |
Ray_IsClear from the human's position (eye height added) to a point. | confirmed (code) |
0x00222508 |
Human_CanSeePoint |
A point within range, in the field of view and with a clear ray. | confirmed (code) |
0x002225d8 |
Human_MaySpectate |
For the spectate goal: true unless the brain is a cop; gang members (class type 10) with fewer than three fights, and AI watching a player standing lower than 1.9 m above, may spectate. | confirmed (code) |
0x002226e0 |
Human_IsHumanInFieldOfView |
Human_IsInFieldOfView on another human's position. | confirmed (code) |
0x00222710 |
Human_IsInFieldOfView |
Whether a point is within the human's field of view (cosine of half the angle). | confirmed (code) |
0x00222868 |
Human_AddSprayPaint |
Adds spray cans: an AI keeps its own count +0x374 (0-9); a player's goes to inventory item 4. |
confirmed (code) |
0x00222908 |
Human_GetCuffCount |
Handcuffs held: a player's from inventory item 5; an AI with brain type 1 always has one; other AI use +0x378. |
confirmed (code) |
0x00222980 |
Human_AddCuffs |
Adds handcuffs: inventory item 5 for a player, else +0x378 clamped at 0. |
confirmed (code) |
0x00222a48 |
Human_IsThreatTo |
Whether human b is a threat to human a: Brain_IsThreat on their two brains (gangs enemies, or both pad players; a Warrior's +0x2e5 mode overrides). A test only; Brain_GiveWayTo needs it false both ways (Step control). | confirmed (code) |
0x00222ad8 |
Human_GetPadRecord |
The human's per-player record in the game state (+0x168 + player x 0x5c), or player 0's for an AI. |
confirmed (code) |
0x00222c98 |
Human_ApplyClassFlags |
Copies the class record's switches (+0x3a-+0x3f) into the human flags +0xe0 (bits 0x80, 0x1000000000, 0x2000000000, 0x800000000, and the paired bits 0x200000/0x400000 and 0x400/0x200). |
confirmed (code) |
0x00222e40 |
Human_GetHealth |
Health, record +0x144 (100 without a record). |
confirmed (code) |
0x00222e60 |
Human_GetMaxHealth |
Maximum health, record +0x146 (100 without a record). |
confirmed (code) |
0x00222e80 |
Human_SetHealthPercentRaw |
Sets health to a percentage of the maximum, rounded. | confirmed (code) |
0x00222f48 |
Human_GetPowerPercent |
Power +0x148 as a percentage of the maximum power. |
confirmed (code) |
0x00223280 |
Human_GetStaminaDrain |
Stamina cost of the current state: blocking, tackling, grabbing with a hold timer, or grabbing (globals 0x005101e4-0x005101f0); 0 for a brainless human when 0x005102a8 is set. | confirmed (code) |
0x00223348 |
Human_GetSprintDrain |
Stamina cost of sprinting (0x005101f4), else 0. | confirmed (code) |
0x002233a8 |
Human_GetClassChainDelay |
Class record byte +0x36 (used by the chain and grab timings). |
confirmed (code) |
0x002233c8 |
Human_GetClassReactFlag |
Class record byte +0x38 (read by Human_PlayReaction). |
confirmed (code) |
0x002233e8 |
Human_ClearPower |
Sets power +0x148 to 0. |
confirmed (code) |
0x00223400 |
Human_FillPower |
Sets power +0x148 to the maximum. |
confirmed (code) |
0x002234f8 |
Human_BeginTalkPose |
Moves the talk state +0x1c from an even value to the odd "talking" value when a line starts. |
confirmed (code) |
0x00223548 |
Human_EndTalkPose |
Moves the talk state +0x1c back to the even value when a line ends. |
confirmed (code) |
0x002235a0 |
Human_SetTalkState |
Sets the talk state +0x1c unless a line is playing (odd value). |
confirmed (code) |
0x00223720 |
Human_GetStunTime |
Class stun time +0x30, scaled by +0x10 while hurt. |
confirmed (code) |
0x00223790 |
Human_GetDownTime |
Class down time +0x34, scaled by +0x14 while hurt. |
confirmed (code) |
0x002238e0 |
Human_IsTackled |
State flag 0x800 (the tackled victim). | confirmed (code) |
0x00223900 |
Human_IsTackling |
State flag 0x400 (tackling, mounted). | confirmed (code) |
0x00223940 |
Human_IsGrabbedFromFront |
State flag 0x10. | confirmed (code) |
0x00223960 |
Human_IsGrabbedFromRear |
State flag 0x20. | confirmed (code) |
0x002239a0 |
Human_IsGrabbingFront |
State flag 0x40. | confirmed (code) |
0x002239c0 |
Human_IsGrabbingRear |
State flag 0x80. | confirmed (code) |
0x00223a00 |
Human_HasState2000 |
State flag 0x2000 (paired attacks and the camera power read it). | confirmed (code) |
0x00223b98 |
Human_IsKnockedDown |
State flag 0x80000, with a record. | confirmed (code) |
0x00223bc0 |
Human_IsStunnedStanding |
Stunned (0x100000) and not knocked down (0x80000). | confirmed (code) |
0x00223c30 |
Human_HasHeld300000 |
Held flags 0x300000 (the taunt and spectate code test it). | confirmed (code) |
0x00223c50 |
Human_IsHiddenFromBrain |
With the player record flag +0x1b set: hidden in shadow and the brain flag +0x2d5 clear. |
confirmed (code) |
0x00223d38 |
Human_IsBusyForScriptAnim |
Holds a kind 4 or 6 object, is busy, or has any of the action state flags 0x7bf9e9f7ff0. | confirmed (code) |
0x00223e48 |
Human_IsClassType12 |
Character class type +0x11b is 12. |
confirmed (code) |
0x00223e78 |
Human_HoldsSet6 |
The held object's anim set is 6. | confirmed (code) |
0x00223ea0 |
Human_HeldIsThrowable |
Held object anim set 5; set 1 with type 11 (knife); or sets 1-3 for a player or when asked. | confirmed (code) |
0x00223f58 |
Human_HeldCanBeThrown |
Like Human_HeldIsThrowable for the throw velocity: set 5, a knife, or sets 1-3 for a player. | confirmed (code) |
0x00224080 |
Human_HeldIsHeavy |
The held object's anim set is neither 1 nor 5. | confirmed (code) |
0x002240e8 |
Human_CanUseAttackKind |
Whether the human can start attack kind n on a target now: per-kind checks of its own and the target's state flags, held objects, spray paint, cuffs, and the brain's targets for kind 10. | confirmed (code) |
0x00224ea0 |
Human_ClassCanFlee |
Class record byte +0x41 set (read by Brain_Think). |
confirmed (code) |
0x00224ec8 |
Human_ShouldFleeHurt |
Class record byte +0x42 set and health under 30 %. |
confirmed (code) |
0x00224fd0 |
Human_IsFreeForCommand |
None of the state flags 0x2c01c0f0c00 or held flags 0xc08000. | confirmed (code) |
0x00225030 |
Human_IsFreeForSnap |
Not in a high or mid busy state and neither grabbed from the front nor grabbing. | confirmed (code) |
0x002250a0 |
Human_IsMidOrBusy |
The other is 0.7-1.3 m above (or under -0.7 m), or the human is in an attack phase that blocks a mid attack. | confirmed (code) |
0x00225200 |
Human_IsHighOrBusy |
The other is 1.3-1.8 m above (or under -0.7 m), or the human is in a grab or attack phase that blocks a new attack. | confirmed (code) |
0x00225390 |
Human_IsIdleUnderControl |
Under Human_UpdateControl or the step control with no state 0x7bf9e1f3ff0 and no held flag; the stander test of Brain_GiveWayTo (Step control). | confirmed (code) |
0x00225500 |
Human_IsNotDown |
Not in states 0xe0000. | confirmed (code) |
0x002256a0 |
Human_CanBeGrabbed |
Whether the human may be grabbed or tackled by another: not down or in a scene, within 0.25 m of height, and none of the blocking state or held flags. | confirmed (code) |
0x00225998 |
Human_IsAtRunSpeed |
Running or sprinting and moving at the run speed (less 0.01). | confirmed (code) |
0x00225f58 |
Human_IsFreeForFidget |
No held flags, no action state flags and nothing held: the fidget and taunt actions may start. | confirmed (code) |
0x00225fc8 |
Human_SetCarriedItemName |
Copies an item name into the human's 31-character buffer +0x257 and clears +0x276. |
confirmed (code) |
0x002262f0 |
Human_CallMugCallback |
Calls the script's mug callback (HuSetMugCallback, +0x5a4) with the human's handle and the outcome, through the script system 0x00512b04. |
confirmed (code) |
0x002265d0 |
Human_SetStateFlag |
Record +0x00 |= bits. |
confirmed (code) |
0x00226620 |
Human_ClearStateFlag |
Record +0x00 &= ~bits. |
confirmed (code) |
0x00226688 |
Human_ClearHeldFlag |
Record +0x08 &= ~bits. |
confirmed (code) |
0x002266d8 |
Human_GetAttackerHandle |
Writes the attacker handle for the attack table: record +0xc0 when it resolves, else the human's own handle. |
confirmed (code) |
0x002267c8 |
Human_ClearFoe |
Record +0xcc = null. |
confirmed (code) |
0x002267e8 |
Human_SetLastHitObject |
Records the world object that hit the human (record +0xd0 handle, +0x124 type, +0x11c position); an object of kind 8 sets it on fire and gives the foe player combat statistic 6. |
confirmed (code) |
0x00226910 |
Human_SetPartner |
Record +0xc8 = another human's handle. |
confirmed (code) |
0x00226950 |
Human_GetPartner |
Record +0xc8 resolved. |
confirmed (code) |
0x00226978 |
Human_ClearPartner |
Record +0xc8 = null. |
confirmed (code) |
0x00226998 |
Human_SetGrabOther |
Record +0xc4 = the other human of a grab. |
confirmed (code) |
0x002269d8 |
Human_GetGrabOther |
Record +0xc4 resolved, the other human of a grab. |
confirmed (code) |
0x00226a00 |
Human_ClearGrabOther |
Record +0xc4 = null. |
confirmed (code) |
0x00226a20 |
Human_GetHeadPoint |
The upper point of the human's capsule segment: feet + +0x4e8 - 0.16 x scale. |
confirmed (code) |
0x00226aa0 |
Human_GetLedSlotPoint |
Aim point n (+0x1e0 + n x 0x10) after refreshing them (0x0023cd30); point 0, the head, is the led attack steer's target (Combat: the led steer). Not a formation. |
confirmed (code) |
0x00226af8 |
Human_SetTargetFromHandle |
Sets the target from a handle: a player sets its own human or object target; an AI sets the brain's target and +0x128; skipped while a player with record flag +0x1b is locked on. |
confirmed (code) |
0x00226e20 |
Human_GetTargetHandle |
The target handle +0xc8, refreshed from the brain first for an AI. |
confirmed (code) |
0x00226ea8 |
Human_GetTargetResolved |
The target +0xc8 resolved, refreshed from the brain first for an AI. |
confirmed (code) |
0x00226ee8 |
Human_SyncTargetFromBrain |
Copies the brain's object target (+0x128) and human target into the human's targets. |
confirmed (code) |
0x00227010 |
Human_SetHeldObject |
Stores the held object's handle in +0x338 (null for none). |
confirmed (code) |
0x00227060 |
Human_GetObjectTarget |
+0x33c resolved (door, flag, lock). |
confirmed (code) |
0x002270d0 |
Human_CanPickUpType |
For class kind 3, false for types 0x29, 0x2b, 0x2d and 0x31-0x36; other classes may pick up an item unless their class list (+0x14c) excludes it. |
confirmed (code) |
0x002271c0 |
Human_ResetRenderState |
Clears the render instance's flag +0x2b and resets it (0x004df008); used when a preview or flag human is shown again. |
confirmed (code) |
0x00227408 |
Human_HasOverheadIcon |
The overhead icon +0x360 resolves. |
confirmed (code) |
0x00227428 |
Human_HasOverheadIconType |
True when the overhead icon +0x360 is a world object of the given type. |
confirmed (code) |
0x002274a0 |
Human_EmptyStub3 |
Empty function called by Human_StateUpdate. | confirmed (code) |
0x00227738 |
Humans_FindNear |
Collects up to n humans of the game state's list (+0x224, +0x228) within a radius of a point, skipping one human and an optional filter; distances squared go beside each. |
confirmed (code) |
0x002278a8 |
Humans_FindNearSegment |
Finds humans whose pose slot lies near a segment (Segment_ClosestPoint), skipping one; used by Route_IsJumpLegClear (AI: Giving way). | confirmed (code) |
0x00227a38 |
Human_IsButtonTapControl |
State flag 0x100000000000 (button-tap movement, Human_SetButtonTapControl). | confirmed (code) |
0x00227a60 |
Human_IsInWheelchair |
State flag 0x80000000000 (wheelchair control). | confirmed (code) |
0x00227b78 |
Human_SetFightStanceControl |
Control function +0x1bc = Human_FightStanceMove (lock-on). |
confirmed (code) |
0x00227c28 |
Human_SetAirControl |
Control function +0x1bc = Human_AirControl (jumps). |
confirmed (code) |
0x00227c48 |
Human_SetUpdateControl |
Control function +0x1bc = Human_UpdateControl, the default. |
confirmed (code) |
0x00227c68 |
Human_HasTarget |
State flag 0x4. | confirmed (code) |
0x00227c88 |
Human_IsLocked |
State flags 0x8 and 0x4 (the L1 lock). | confirmed (code) |
0x00227e18 |
Human_HasStateFlag80000000 |
State flag 0x80000000 (true without a record). | confirmed (code) |
0x00227fb8 |
Human_IsOnFire |
State flag 0x4000. | confirmed (code) |
0x00228000 |
Human_IsWorkingOut |
State flag 0x20000000000 (the workout). | confirmed (code) |
0x00228028 |
Human_IsInSpecial |
State flag 0x40000000000 (a special move, Human_StopSpecial). | confirmed (code) |
0x00228070 |
Human_IsMugging |
State flag 0x100. | confirmed (code) |
0x00228090 |
Human_IsBeingMugged |
State flag 0x200. | confirmed (code) |
0x002280b0 |
Human_HasState8000000 |
State flag 0x8000000 (read by the death and lethal reactions). | confirmed (code) |
0x002280d0 |
Human_HasState10000000 |
State flag 0x10000000 (read by the lethal reaction and AI). | confirmed (code) |
0x002280f0 |
Human_IsInMugMeter |
State flags 0x18000000000 (the mug meter shows). | confirmed (code) |
0x00228118 |
Human_HasState8000000000 |
State flag 0x8000000000 (read with holds and pair breaks). | confirmed (code) |
0x00228140 |
Human_HasState10000000000 |
State flag 0x10000000000 (read with pair breaks). | confirmed (code) |
0x00228188 |
Human_IsStalkingTarget |
State flags 0x8 and 0x200000. | confirmed (code) |
0x002281d8 |
Human_IsThrowingAt |
State flags 0x1000 (throwing) and 0x200000; brains treat it as an attack warning. | confirmed (code) |
0x00228258 |
Human_IsBusy |
State flags 0x1f80974000, or record +0x108 still ahead of the game time. |
confirmed (code) |
0x002282d8 |
Human_IsFree |
No held flags 0x2fefefff and no state flags 0x7bf9e9f7ff0. | confirmed (code) |
0x00228360 |
Human_HasState800000 |
State flag 0x800000 (read when pending damage applies). | confirmed (code) |
0x002283a8 |
Human_ClearDownLatch |
Clears state flag 0x40000000. | confirmed (code) |
0x002283c8 |
Human_HasDownLatch |
State flag 0x40000000 (down; read by the corpse cull). | confirmed (code) |
0x002283e8 |
Human_HasHeld400000 |
Held flag 0x400000 (an airborne body; car and thrown hits read it). | confirmed (code) |
0x00228408 |
Human_HasHeld4000 |
Held flag 0x4000 (picking up an item). | confirmed (code) |
0x00228448 |
Human_HasHeld20080000 |
Held flags 0x20080000: a turn clip (0x20000000) or a step clip (0x80000) plays; TakeStep waits on it and refuses an abort (Step control). | confirmed (code) |
0x00228468 |
Human_HasHeld8000000 |
Held flag 0x8000000 (reviving). | confirmed (code) |
0x00228500 |
Human_HasHeld20000000 |
Held flag 0x20000000 (turning). | confirmed (code) |
0x00228520 |
Human_HasHeld100 |
Held flag 0x100 (a fidget plays). | confirmed (code) |
0x00228540 |
Human_HasHeld20000 |
Held flag 0x20000. | confirmed (code) |
0x00228630 |
Human_SetState20000000 |
Sets state flag 0x20000000 (the victim just before a tackle hit). | confirmed (code) |
0x00228658 |
Human_GetLastHitObject |
Record +0xd0 resolved: the world object that last hit the human. |
confirmed (code) |
0x00228680 |
Human_GetLastHitObjectHandle |
Record +0xd0 as a handle (null without a record). |
confirmed (code) |
0x002286a0 |
Human_IsSwingingHeld |
Held flag 0x800000, or 0x1000010 while holding an object and playing one of the eleven object throw clips (467, 471, 472, 491, 502, 505-507, 551-553; a throw wind-up, not a melee swing). A hit drops the held object while it is true (Combat: dropping what is held). | confirmed (code) |
0x002287e0 |
Humans_StaticInitAll |
Static initialiser: Humans_StaticInit(1, 0xffff). | confirmed (code) |
0x00228818 |
SpeedClass_Get |
Speed class record 0x006b6548 + class x 0x18. |
confirmed (code) |
0x00228830 |
PowerClass_Get |
Power class record 0x006619a0 + class x 0x44. |
confirmed (code) |
0x00228848 |
WarriorClass_Get |
Warrior class record 0x006b65c0 + class x 14. |
confirmed (code) |
0x002288c0 |
Cfg_SetPowerClass |
CfgPowerClass: fills a power class record. |
confirmed (code) |
0x00228a70 |
Cfg_SetWarriorClass |
CfgWarriorClass: fills a Warrior class record. |
confirmed (code) |
0x00228ad8 |
Cfg_SetWarriorUpgrade |
CfgWarriorUpgrade: four bytes at 0x006b6650. |
confirmed (code) |
0x00228ca8 |
Cfg_SetAttackDelay |
CfgAttackDelay: one word per attack at 0x006b6658. |
confirmed (code) |
0x00228cc0 |
HatSet_Reset |
Clears a hat-fit set's owner and names its 48 slots none. |
confirmed (code) |
0x00228e68 |
HatSet_FindSlot |
Index of the slot named as an object type in a hat-fit set, or -1. | confirmed (code) |
0x00228ed0 |
HatSet_SetSlotName |
Writes a slot's 23-character name in a hat-fit set. | confirmed (code) |
0x00228f08 |
HatSet_HasOwner |
True when one of the 51 hat-fit sets is owned by the character type. | confirmed (code) |
0x00229110 |
Anim_LevelVariant |
Maps an anim id to a level's own variant for ten levels (by the current level id), else 0. | confirmed (code) |
0x002294c8 |
Humans_ResetClassTables |
At start-up: clears the power classes' in-use bytes (+0x43) and resets all hat-fit sets. |
confirmed (code) |
0x00229550 |
Humans_StaticInit |
Humans_ResetClassTables(1, 0xffff). | confirmed (code) |
0x00229570 |
Human_GetPortraitKind |
The crew-portrait kind for the character id +0xcc (kinds 0-11). |
confirmed (code) |
0x00229620 |
Human_DropOverlayTask |
Removes a type-5 top task, turns every strike shape off and clears the combo count +0xbc. |
confirmed (code) |
0x002296c0 |
Task_SendMessage28 |
Sends message 28 (a handle and a point) to a task, stamping a type-2 object's time +0x50 one frame back. |
confirmed (code) |
0x00229770 |
Task_SendMessage4 |
Sends message 4 (int, float, int, int) to a task. | confirmed (code) |
0x00229820 |
Object_AsKind40 |
The object when its kind flags (vtable +0x24) include 0x40, else 0. |
confirmed (code) |
0x002298b8 |
Human_DistanceSq |
Squared distance between two objects' positions (0 if either is null). | confirmed (code) |
0x00229aa8 |
Human_DistSqToLedSlot |
Squared distance from an object to a led formation's slot point. | confirmed (code) |
0x0022a980 |
Player_UnbindPad |
Unbinds a player's pad slot from the HUD and raises the pad-error message. | confirmed (code) |
0x0022aa78 |
Player_BindPad |
Binds the human as a player on a pad: player record +0x19 = pad, +0x1b set when asked. |
confirmed (code) |
0x0022aae8 |
Human_SimulateMove |
Simulates a walk toward a heading at a trial speed (gait turn rates), giving the end point; route tests use it. | confirmed (code) |
0x0022ae30 |
Human_ScaleTurnRate |
Multiplies by the turn-rate factor 0x005102cc. | confirmed (code) |
0x0022c618 |
Human_ClearState8000000 |
Clears state 0x8000000, restores push weight and puts the human down when knocked down. | confirmed (code) |
0x0022d2d8 |
Human_BeginSpecialState |
Sets state 0x40000000000 and starts the special (0x002349a0). | confirmed (code) |
0x0022d310 |
Human_EndSpecialState |
Clears state 0x40000000000 and ends the special (0x00234b30). | confirmed (code) |
0x0022d348 |
Human_StartWorkout |
Sets state 0x20000000000 and calls the workout start callback. | confirmed (code) |
0x0022d380 |
Human_EndWorkout |
Clears state 0x20000000000, stops the workout clip and calls the end callback. | confirmed (code) |
0x0022dd08 |
Human_GetClassSpinAngle |
2 pi, or (n - 1) x 6 pi for a Warrior class byte +0xb above 1. |
confirmed (code) |
0x0022f3c8 |
Human_GetUp |
Ends being down unless dead or knocked out: restores push weight, pops the down styles (0xe, 0xf); a stunned human (0x100000) goes to style 0xe with a fresh stun timer, else into the fight stance; clears 0x4000080000. | confirmed (code) |
0x0022f4f8 |
Human_SetDownTimers |
Sets the down end time (record +0x104, class down time, doubled by an attack with anim flag 0x100, +2 s from a class-13 attacker) and the stun end +0x100 (doubled by anim flag 0x200); stuns when asked and not already stunned. |
confirmed (code) |
0x0022f9e0 |
Human_SetStunTimer |
Sets the stun end time record +0x100 from the class stun time, doubled by an attack with anim flag 0x200. |
confirmed (code) |
0x0022fa78 |
Human_StartWounded |
Clears the fight states, drops what is held, pushes the wounded style and tells the brain the human is knocked out. | confirmed (code) |
0x0022fc00 |
Human_EndWounded |
Clears state 0x10000, restores push weight and pops the wounded style (0xd). | confirmed (code) |
0x0022fce8 |
Human_Ignite |
Sets the human burning (Human_StartBurning) unless fireproof, down, in a scene or busy; AI catch fire only when walking, jogging or running. | confirmed (code) |
0x0022fe10 |
Human_Extinguish |
Clears state 0x4000, the burn time and the heat effect. | confirmed (code) |
0x0022fe48 |
Human_EnterFightStance |
Starts the stance timer and sets state 0x1. | confirmed (code) |
0x0022fed0 |
Human_LeaveFightStanceThunk |
Calls Human_LeaveFightStance. | confirmed (code) |
0x002300c0 |
Human_LeaveShadow |
When hidden (0x200000), not running and with a target, clears the hidden state. | confirmed (code) |
0x002301f0 |
Player_UpdateHiddenLoopSound |
For a player: prepares the hidden-in-shadow loop line when hidden, stops it when not. | confirmed (code) |
0x002302a8 |
Human_ResetToOneHealth |
Unless dead: knocks off the hat, clears flag 0x10, sets the counters +0x118 and +0x5d2 to 100, clears the foe and sets health to 1. |
confirmed (code) |
0x00230598 |
Human_WakeUp |
Wakes a knocked-out human: body back on, state cleared, styles popped, the cuffs icon removed, brain resumed. | confirmed (code) |
0x002306e8 |
Human_ReleaseBody |
Stops speech, clears the brain's enemies, clears the pose-slot entry at 0x00715390 and frees the physics body. | confirmed (code) |
0x00230740 |
Human_CreditDefeat |
When a human is beaten: credits the foe player (combat statistic 7 for a gang brain, crime statistics for others), tears down the brain, removes it from the radar and its overhead icon. | confirmed (code) |
0x00231ac8 |
Human_PlayHeldIdle |
Plays a held-object idle clip (0x25a for a knife, 0x25c when asked) when no held flags are set. | confirmed (code) |
0x00231b60 |
Human_StopFidget |
With a fidget playing (held 0x100): drops the overlay while moving, else rebuilds the idle tasks. | confirmed (code) |
0x00231bd8 |
Human_PlayLookAround |
Pushes overlay clip 0x29e with held flag 0x20000 when free. | confirmed (code) |
0x00231c20 |
Human_StopLookAround |
Drops the overlay while held flag 0x20000 is set. | confirmed (code) |
0x00231c58 |
Human_StopTaunt |
With held flags 0x300000: drops the overlay while moving, else rebuilds the idle tasks. | confirmed (code) |
0x002320a0 |
Human_CanPush |
Whether one human pushes another on contact, by their stances and states (from Human_OnPushContact). | confirmed (code) |
0x002323d0 |
Humans_DestroyAll |
At level unload: flushes deferred anim tasks and destroys every human handle. | confirmed (code) |
0x00232430 |
Human_GetClipTimeLeft |
Time left in the top clip, or in the one under it (at least 0.1 s) when the top has ended. | confirmed (code) |
0x00232538 |
Humans_EndAllRage |
Ends rage mode for every human in rage (flag 0x80000). | confirmed (code) |
0x00232b78 |
Human_CancelThrowAim |
Leaves throw aiming when the control function is Human_MoveThrowAim. | confirmed (code) |
0x00233830 |
Human_CopRadioChatter |
For a cop brain, every 360 updates plays the next radio voice line. | confirmed (code) |
0x00233890 |
Human_SayGoalLine |
Speaks the line for an AI goal event: commands or cop lines chosen by the event and the level. | confirmed (code) |
0x00233b08 |
Human_SwapHeldObjects |
Swaps two humans' held objects, each picking up the other's with its pickup clip. | confirmed (code) |
0x00233c40 |
Cfg_SetAutoCloseMode |
CfgAutoCloseMode: sets 0x00510228. |
confirmed (code) |
Script functions¶
The engine side of the Hu* and Cfg* script functions (0x00233ef0-0x0023b838, inferred from where the rows lie;
each row is graded); each row names its script function. A setter with a flag turns that bit of the human flags +0xe0
on or off; a Cfg* setter writes one global the human code reads.
| Address | Name | What it does | Evidence |
|---|---|---|---|
0x00233ef0 |
Human_Delete |
Marks the human dead (0x200000000), clears its foe and its script handlers. | confirmed (code) |
0x00233f60 |
Human_SwapPlayerControl |
Moves player control from one human to another, swapping their camera targets. | confirmed (code) |
0x00234038 |
Human_SetNoTarget |
Human flag 0x100000000000 on or off: the human cannot be targeted. | confirmed (code) |
0x002340a8 |
Human_SetNoAutoLock |
Human flag 0x8000000000 on or off: no automatic lock-on. | confirmed (code) |
0x00234118 |
Human_SetAutoCombat |
Human flag 0x200000000000 on or off. | confirmed (code) |
0x00234318 |
Human_SetButtonTapControl |
Puts the human under button-tap movement (or back): saves control, swaps the commands and sets the state. | confirmed (code) |
0x00234530 |
Cfg_SetWorkoutParams |
Fills the workout parameter tables at 0x00510690-0x005106b0. | confirmed (code) |
0x002345d8 |
Human_SetWorkoutParams |
Sets three workout values at 0x00510680. | confirmed (code) |
0x002345f8 |
Human_SetWorkoutCallbacks |
Interns the workout start, rep and end Lua callbacks. | confirmed (code) |
0x002346a0 |
Workout_CallStart |
Calls the workout start callback with the human. | confirmed (code) |
0x00234768 |
Workout_CallRep |
Calls the workout repetition callback with the human. | confirmed (code) |
0x00234830 |
Workout_CallEnd |
Calls the workout end callback with the human. | confirmed (code) |
0x002348f8 |
Human_SetSpecialCallbacks |
Interns the special-move start, step and end Lua callbacks. | confirmed (code) |
0x002349a0 |
Special_CallStart |
Calls the special start callback with the human. | confirmed (code) |
0x00234a68 |
Special_CallStep |
Calls the special step callback with the human. | confirmed (code) |
0x00234b30 |
Special_CallEnd |
Calls the special end callback with the human. | confirmed (code) |
0x00234bf8 |
Human_StartSpecial |
Script: puts a clip on anim 0x2a0 and starts the special action. | confirmed (code) |
0x00234cb0 |
Human_StopSpecial |
Script: ends a running special with action 0x1e. | confirmed (code) |
0x00234d08 |
Human_SetAccurate |
HuSetAccurate: human flag 0x40000 on or off. |
confirmed (code) |
0x00234d70 |
Human_MakeBeatUp |
Script: holds the brain and knocks the human down lying, dropping what it holds. | confirmed (code) |
0x00234e58 |
Human_MakeGrounded |
Script: lays the human down, or lifts the down latch. | confirmed (code) |
0x00234ef8 |
Human_SetLockMovement |
HuLockMovement: human flag 0x200000000 on or off; the rumble menus use it on preview fighters. |
confirmed (code) |
0x00234f68 |
Human_GetHandleByIndex |
HuGet: the handle of human n, or null. |
confirmed (code) |
0x00234fb8 |
Human_GetPlayerHandle |
HuGetPlayer: the handle of the game state's human n. |
confirmed (code) |
0x00235010 |
Human_FindByName |
HuFind: the first human whose name matches. |
confirmed (code) |
0x002350c8 |
Human_SetTireless |
Human flag 0x4000000 on or off. | confirmed (code) |
0x00235130 |
Human_SetManualStun |
Human flag 0x8000000 on or off. | confirmed (code) |
0x00235198 |
Human_SetNoReact |
Human flag 0x800 on or off. | confirmed (code) |
0x00235200 |
Human_SetAutoEscape |
HuSetAutoEscape: human flag 0x20000 on or off. |
confirmed (code) |
0x00235268 |
Human_SetPushable |
HuSetPushable: resolves the handle and calls the human's pushable setter (0x0021d848). |
confirmed (code) |
0x002352b0 |
Human_GetPositionRef |
HuGetPosition: the human's position, or the zero vector 0x005116c0. |
confirmed (code) |
0x002352f8 |
Human_GetHeadingDegrees |
HuGetHeading: the heading of the pose-slot rotation, from an arctangent approximation. |
confirmed (code) |
0x00235478 |
Human_GetGangType |
HuGetGangType: the brain's gang (+0x20c), -1 without a human. |
confirmed (code) |
0x002354b8 |
Human_SetStunned |
HuSetStunned: stuns the human or ends the stun. |
confirmed (code) |
0x00235530 |
Human_GetHat |
HuHasHat: the handle of the worn hat +0x364, or null. |
confirmed (code) |
0x002355a0 |
Human_SetStrong |
HuSetStrong: byte +0x3b9. |
confirmed (code) |
0x002355e0 |
Human_IsGrabbed |
HuIsGrabbed: any of state flags 0x830 (grabbed from the front or rear, tackled). |
confirmed (code) |
0x00235628 |
Human_IsAlive |
HuIsAlive: not down or dead and up. |
confirmed (code) |
0x00235688 |
Human_IsDead |
HuIsDead: dead (true without a human). |
confirmed (code) |
0x002356c8 |
Human_IsInScene |
HuIsInScene: in a scene state. |
confirmed (code) |
0x00235718 |
Human_IsArrested |
HuIsArrested: cuffed. |
confirmed (code) |
0x00235758 |
Human_IsPlayer |
HuIsAPlayer: player index +0x1b0 is not -1. |
confirmed (code) |
0x00235798 |
Human_IsHidden |
HuIsHidden: the brain's hidden byte +0x2d4. |
confirmed (code) |
0x002357d8 |
Human_IsMuggedByHandle |
HuIsMugged: being mugged. |
confirmed (code) |
0x00235818 |
Human_IsProne |
HuIsProne: in states 0xe0000 or 0x2000. |
confirmed (code) |
0x00235890 |
Human_GetCharType |
HuGetCharType: the character type +0xcc. |
confirmed (code) |
0x002358c8 |
Human_SetDamage |
HuSetDamage: queues pending damage from an attacker's held object. |
confirmed (code) |
0x002359a0 |
Human_SetDemiGodMode |
HuSetDemiGodMode: human flag 0x20000000000 and the minimum health 0x0051024c. |
confirmed (code) |
0x00235a28 |
Human_SetGodMode |
HuSetGodMode: human flag 0x10; the tired goal also uses it. |
confirmed (code) |
0x00235a88 |
Human_SetKillerMode |
HuSetKillerMode: human flag 0x20. |
confirmed (code) |
0x00235ae8 |
Human_SetUntouchable |
AI only (two goal functions): human flags 0x2c0 together (ungrabbable, ungroundable, reduced reactions). | confirmed (code) |
0x00235b68 |
Human_SetFastClimber |
HuSetFastClimber: a human flag on or off. |
confirmed (code) |
0x00235bc8 |
Human_SetUngrabbable |
HuSetUngrabbable: human flag 0x40. |
confirmed (code) |
0x00235c28 |
Human_SetUngroundable |
HuSetUngroundable: human flag 0x80. |
confirmed (code) |
0x00235c88 |
Human_SetUnstunnable |
HuSetUnstunnable: human flag 0x100. |
confirmed (code) |
0x00235ce8 |
Human_SetIncreasedReact |
HuSetIncreasedReact: human flag 0x200000. |
confirmed (code) |
0x00235d50 |
Human_SetReducedReact |
HuSetReducedReact: human flag 0x200. |
confirmed (code) |
0x00235db0 |
Human_SetRevivable |
HuSetRevivable: a human flag on or off. |
confirmed (code) |
0x00235e10 |
Human_SetDoubleDamage |
HuSetDoubleDamage: human flag 0x4000. |
confirmed (code) |
0x00235e70 |
Human_SetUnarrestable |
HuSetUnarrestable: human flag 0x8000, removing a pending arrest prompt. |
confirmed (code) |
0x00235f10 |
Cfg_SetClimbWithGhetto |
CfgClimbWithGhetto: sets 0x00510250. |
confirmed (code) |
0x00235f20 |
Human_SetOnFire |
HuSetOnFire: ignites or extinguishes the human. |
confirmed (code) |
0x00235f80 |
HumanHandle_IsOnFire |
HuIsOnFire: state 0x4000. |
confirmed (code) |
0x00235fc0 |
Human_SetFireProof |
HuSetFireProof: human flag 0x800000, extinguishing when set. |
confirmed (code) |
0x002360f8 |
Human_ScriptDrop |
HuDrop: knocks off the hat, drops the held object and gear as asked. |
confirmed (code) |
0x00236188 |
Human_SetUnlockedGear |
HuAttachGear: attaches or removes the unlocked gear. |
confirmed (code) |
0x002361e8 |
Cfg_SetGearData |
CfgGearData: fills the gear transforms at 0x00715480. |
confirmed (code) |
0x00236420 |
Cfg_SetButtonHeldFrames |
CfgButtonHeldFrames: frames for a held button, clamped to 2-7 (0x0050b708). |
confirmed (code) |
0x00236450 |
Cfg_SetGrabCounterWithAttack |
CfgGrabCounterWithAttack: sets 0x00510254. |
confirmed (code) |
0x00236460 |
Cfg_SetPlayerRunButton |
CfgPlayerRunButton: sets 0x00510258. |
confirmed (code) |
0x00236470 |
Cfg_SetStickDeflection |
CfgStickDeflection: sets 0x005102e8. |
confirmed (code) |
0x00236480 |
Cfg_SetPlayerCombatWalkOnly |
CfgPlayerCombatWalkOnly: sets 0x0051031c. |
confirmed (code) |
0x00236490 |
Cfg_SetQueueGrappleCombos |
CfgQueueGrappleCombos: sets 0x0051025c. |
confirmed (code) |
0x002364d8 |
Cfg_SetAttackFromIdle |
CfgAttackFromIdle: sets 0x00510270. |
confirmed (code) |
0x002364e8 |
Cfg_SetDamageEndurance |
CfgDamageEndurance: sets 0x00510274. |
confirmed (code) |
0x002364f8 |
Cfg_SetPowerEndurance |
CfgPowerEndurance: sets 0x00510278. |
confirmed (code) |
0x00236978 |
Human_ForceEnableReticule |
HuForceEnableReticule: sets 0x005104f8. |
confirmed (code) |
0x002369b8 |
Human_SetRageFrac |
HuSetRageFrac: sets the rage meter to a fraction of its maximum. |
confirmed (code) |
0x00236a68 |
Human_SetFullRage |
HuSetFullRage: fills the rage meter. |
confirmed (code) |
0x00236ad0 |
Human_SetPreventRage |
HuSetPreventRage: human flag 0x2000000. |
confirmed (code) |
0x00236b38 |
Human_SetLockedRage |
HuSetLockedRage: human flag 0x100000 and the rage meter held where it is. |
confirmed (code) |
0x00236be0 |
Rage_ResetConfig |
At level unload: clears the stereo, lock-pick and rage handlers and the rage handler table 0x006b6810. | confirmed (code) |
0x00236c28 |
Cfg_SetRagePoints |
CfgRagePoints: three values at 0x005108d8-0x005108e4. |
confirmed (code) |
0x00236c48 |
Cfg_SetRagePowerMode |
CfgRagePowerMode: sets 0x00510298. |
confirmed (code) |
0x00236c58 |
Cfg_SetRageHandlers |
CfgRageHandlers: interns the rage Lua handlers (0x00510290, 0x00510294, table 0x006b6810). |
confirmed (code) |
0x00237128 |
Human_SetRageMode |
HuSetRageMode: starts or ends rage (human flag 0x80000) for a player. |
confirmed (code) |
0x002371d8 |
Human_SetBlurMode |
HuSetBlurMode: a player's screen blur (0x0051029c). |
confirmed (code) |
0x002372a8 |
Human_GiveCuffs |
HuGiveCuffs: adds handcuffs (inventory item 5 for a player). |
confirmed (code) |
0x00237328 |
Human_SetKeepWeapon |
HuSetKeepWeapon: human flag 0x2000; Diego's boss goal uses it. |
confirmed (code) |
0x00237388 |
Human_SetKeepHat |
HuSetKeepHat: human flag 0x10000000000. |
confirmed (code) |
0x002373f8 |
Human_SetHardToStun |
HuSetHardToStun: human flag 0x1000000000. |
confirmed (code) |
0x00237468 |
Human_SetNoThrowWeapon |
HuSetNoThrowWeapon: human flag 0x4000000000. |
confirmed (code) |
0x002374d8 |
Human_SetNoEscape |
HuSetNoEscape: human flag 0x2000000000. |
confirmed (code) |
0x00237548 |
Human_SetCanBlockKD |
HuSetCanBlockKD: human flag 0x400000000. |
confirmed (code) |
0x002375b8 |
Human_SetBlockFromReact |
HuSetBlockFromReact: human flag 0x800000000. |
confirmed (code) |
0x00237628 |
Human_SetWounded |
HuSetWounded: starts or ends the wounded state. |
confirmed (code) |
0x002376a0 |
Human_SetWoundable |
HuSetWoundable: a human flag on or off. |
confirmed (code) |
0x00237700 |
Human_SetArrested |
HuSetArrested: arrests or frees the human (Crimes); the bum goal uses it too. |
confirmed (code) |
0x00237778 |
Human_SetConscious |
HuSetConscious: knocks out or wakes the human. |
confirmed (code) |
0x002377f8 |
Human_Revive |
HuRevive: revives the human. |
confirmed (code) |
0x00237848 |
Human_SetHealth |
HuSetHealth: health +0x144, at most the maximum. |
confirmed (code) |
0x002378a8 |
Human_SetHealthPercent |
HuSetHealthPercent: health as a percentage of the maximum. |
confirmed (code) |
0x00237958 |
Human_SetBlockJump |
HuBlockJump: human flag 0x10000000. |
confirmed (code) |
0x002379c0 |
Human_SetBlockClimb |
HuBlockClimb: human flag 0x20000000. |
confirmed (code) |
0x00237a28 |
Human_BlockGrab |
HuBlockGrab: human flags 0xc0000000 (both grabs). |
confirmed (code) |
0x00237a98 |
Human_SetBlockFrontGrab |
HuBlockFrontGrab: human flag 0x40000000. |
confirmed (code) |
0x00237b00 |
Human_SetBlockRearGrab |
HuBlockRearGrab: human flag 0x80000000. |
confirmed (code) |
0x00237b70 |
Human_SetBlockTackle |
HuBlockTackle: human flag 0x100000000. |
confirmed (code) |
0x00237be0 |
Human_SetMaxHealth |
HuSetMaxHealth: maximum health +0x146, and health with it. |
confirmed (code) |
0x00237c38 |
Human_GetHealthPercent |
HuGetHealthPercent: health as a percentage of the maximum. |
confirmed (code) |
0x00237c78 |
Human_SetMass |
HuSetMass: the body's mass. |
confirmed (code) |
0x00237d08 |
Human_GetHeldObjectType |
HuWhatAmIHolding: the held object's type. |
confirmed (code) |
0x00237d48 |
Human_GetHeldObject |
HuGetHeldObject: the held object's handle, or null. |
confirmed (code) |
0x00237db8 |
Human_IsInCombat |
HuInCombat: whether the human fights. |
confirmed (code) |
0x00237e00 |
Human_SetIdleAnim |
HuSetIdleAnim: overrides the idle clip. |
confirmed (code) |
0x00237e70 |
Human_Kill |
HuKill: kills the human. |
confirmed (code) |
0x00238030 |
Human_SetShadow |
HuShadow: the render instance's shadow switch (+0x2b4). |
confirmed (code) |
0x00238078 |
Human_SetName |
HuSetName: the human's name. |
confirmed (code) |
0x002380c0 |
Human_GetControlName |
HuGetControlName: the name of the control the human is under. |
confirmed (code) |
0x00238158 |
Human_GetMoney |
HuGetMoney: money +0x370. |
confirmed (code) |
0x002381f0 |
Human_RemoveItemInPocket |
HuRemoveItemInPocket: clears the pocket item (+0x250, +0x254). |
confirmed (code) |
0x00238230 |
Human_SetCarriedItemName |
HuSetCarriedItem: the carried item name (+0x278). |
confirmed (code) |
0x00238288 |
Human_CanSee |
HuCanSee: whether the human sees another. |
confirmed (code) |
0x002383a0 |
Human_SetLOSRange |
HuSetLOSRange: sight range +0x130. |
confirmed (code) |
0x002383e8 |
Human_SetHearRange |
HuSetHearRange: hearing range +0x134. |
confirmed (code) |
0x00238430 |
Human_SetHelpHearRange |
HuSetHelpHearRange: the range for hearing calls for help +0x138. |
confirmed (code) |
0x00238478 |
Human_ExitWorld |
HuExitWorld: takes the human out of the world. |
confirmed (code) |
0x002384f8 |
Human_EnableController |
HuEnableController: the player record flag +0x1b (pad control on or off). |
confirmed (code) |
0x002385f8 |
Human_PlaceHatOnHead |
HuPlaceHatOnHead: puts a hat object on the human's head. |
confirmed (code) |
0x00238640 |
Human_SetPedReaction |
HuSetPedReaction: the pedestrian reaction +0x270. |
confirmed (code) |
0x00238690 |
Human_UseAnim |
Plays an anim by id on the human (scripts and many AI goals). | confirmed (code) |
0x00238748 |
Human_UseAnyAnim |
HuUseAnyAnim: plays any clip by name. |
confirmed (code) |
0x002387a8 |
Human_AreActionsBlocked |
HuAreActionsBlocked: whether the human cannot act. |
confirmed (code) |
0x002387e8 |
Human_IsTagging |
HuIsTagging: state 0x2000000. |
confirmed (code) |
0x00238828 |
Human_PlayDynPair |
HuPlayDynPair: a paired dynamic clip on two humans; the dealer goal uses it to sell. |
confirmed (code) |
0x00238948 |
Human_PlayDynAnim |
HuPlayDynAnim: a dynamic clip on the human. |
confirmed (code) |
0x002389e8 |
Human_PlayDynamicAnim |
HuPlayDynamicAnim: a dynamic clip by name. |
confirmed (code) |
0x00238a88 |
Human_AttachSpinningIcon |
HuAttachSpinningIcon: the spinning icon over the human (+0x360); AI crime and dealer goals use it. |
confirmed (code) |
0x00238ae0 |
Human_RemoveSpinningIcon |
HuRemoveSpinningIcon: removes it. |
confirmed (code) |
0x00238b18 |
Human_SetSpinningIconColor |
HuSetSpinningIconColor: messages the icon's colour. |
confirmed (code) |
0x00238bd8 |
Human_StartWorkout |
HuWorkout: starts a workout on the human (Human_StartWorkout state, start callback). |
confirmed (code) |
0x00238cd8 |
Human_SetWorkoutBlend |
HuSetWorkoutBlend: the workout blend +0x670. |
confirmed (code) |
0x00238d30 |
Human_StopWorkout |
HuStopWorkout: ends the workout and its clip. |
confirmed (code) |
0x00238f10 |
Cfg_SetTagStartCallback |
CfgTagStartCallback: interns the tag-start Lua callback (0x006b6870). |
confirmed (code) |
0x00239080 |
Human_SetTagColour |
HuTagColor: the tag colour +0x640. |
confirmed (code) |
0x00239188 |
Tag_SetPattern |
HuTagPattern: the tag's button pattern. |
confirmed (code) |
0x002391a8 |
Human_SetTagDifficulty |
HuTagDifficulty: the three tag difficulty values 0x0051093c-0x00510944. |
confirmed (code) |
0x002391c0 |
Human_SetIdleDialogMaxIter |
HuSetIdleDialogMaxIter: the idle-dialogue line count +0x197. |
confirmed (code) |
0x00239200 |
Human_EnableSpeaking |
HuEnableSpeaking: switch +0x198. |
confirmed (code) |
0x00239240 |
Human_EnableSoundCommands |
HuEnableSoundCommands: switch +0x199, which every speech path checks. |
confirmed (code) |
0x00239280 |
Human_EnableOnFireSpeech |
HuEnableOnFire: the on-fire speech switch +0x19b. |
confirmed (code) |
0x002392c0 |
Human_EnableTagDoneSpeech |
HuEnableTagDone: the tag-done speech switch +0x19a. |
confirmed (code) |
0x00239300 |
Human_EnableTagCheer |
HuEnableTagCheer: the tag-cheer switch +0x19c. |
confirmed (code) |
0x00239340 |
Human_Say |
HuSay: Human_Speak with the default arguments. |
confirmed (code) |
0x00239370 |
Human_Speak |
HuSpeak: plays a named speech line on the human with an optional Lua end callback (interned through the script system). |
confirmed (code) |
0x00239558 |
Human_ShutUp |
HuShutUp: stops the human's line; the roof boss banter uses it. |
confirmed (code) |
0x002395a0 |
Human_SpeakInterrupt |
HuSpeakNI: the variant of HuSpeak that Human_ActionDialog and AI dialogue call. |
confirmed (code) |
0x00239788 |
Human_ActionDialog |
HuActionDialog: a line spoken to a partner (+0x18c), whom the speaker faces. |
confirmed (code) |
0x00239850 |
Human_GetHUD |
HuGetHUD: the HUD slot of the human's player (+0x380). |
confirmed (code) |
0x00239888 |
GameState_SetWarChiefHUD |
HuSetWarChiefHUD: the game state's war-chief HUD switch. |
confirmed (code) |
0x002398b0 |
Human_SetWarChief |
HuSetWarChief: makes the human the war chief (the commanding player), re-binding player slots; GameState_SyncPlayers uses it. |
confirmed (code) |
0x00239e08 |
GameState_SwitchPlayer |
HuSwitchPlayer: switches the controlled player in the game state. |
confirmed (code) |
0x00239e30 |
Human_MarkReachable |
HuMarkReachable: byte +0x11e. |
confirmed (code) |
0x00239e78 |
Human_SetMugCallback |
HuSetMugCallback: interns the mug Lua callback into +0x5a4 (Crimes). |
confirmed (code) |
0x00239ee0 |
Human_SetMug |
HuSetMug: the mug settings +0x5b0. |
confirmed (code) |
0x0023a058 |
Human_TeleportFollowers |
HuTeleportFollowers: moves the human's gang followers (brain +0x20c) to it. |
confirmed (code) |
0x0023a210 |
Human_SetNormalMode |
HuSetNormalMode: clears the special modes; AI goals use it. |
confirmed (code) |
0x0023a258 |
Human_SetRubberNeck |
HuRubberNeck: human flag 0x1000000 on or off. |
confirmed (code) |
0x0023a2c0 |
Human_SetBlockLook |
HuBlockLook: human flag 0x1000 on or off. |
confirmed (code) |
0x0023a328 |
Human_SetForceLook |
HuForceLook: human flag 0x10000 on or off. |
confirmed (code) |
0x0023a390 |
Human_SetLookPos |
HuSetLookPos: a point for the head to look at. |
confirmed (code) |
0x0023a3b8 |
Human_SetLookTarget |
HuSetLookTarget: an object for the head to look at. |
confirmed (code) |
0x0023a460 |
Human_ClearLook_Stub |
HuClearLook: empty in this build. |
confirmed (code) |
0x0023a468 |
Human_IsAimingAt |
HuIsAimingAt: a player's aim target +0x634 is the given object. |
confirmed (code) |
0x0023a4e0 |
SetDeathTimer |
SetDeathTimer: three death-timer values at 0x0051078c-0x00510794. |
confirmed (code) |
0x0023a540 |
Cfg_SetCombo |
CfgCombo: sets 0x005102b0. |
confirmed (code) |
0x0023a550 |
Cfg_SetSnap |
CfgSnap: sets 0x005102b4. |
confirmed (code) |
0x0023a560 |
Cfg_SetButtonMash |
CfgButtonMash: the mash target, decay and gain (Crimes). |
confirmed (code) |
0x0023a588 |
Cfg_SetStrafe |
CfgStrafe: sets 0x005104a8. |
confirmed (code) |
0x0023a598 |
Cfg_SetAutoLock |
CfgAutoLock: sets 0x005104b4. |
confirmed (code) |
0x0023a5a8 |
Cfg_SetAutoLockAndCombat |
CfgAutoLockAndCombat: sets 0x005104b8. |
confirmed (code) |
0x0023a5b8 |
Cfg_SetAutoCombat |
CfgAutoCombat: sets 0x005104b0. |
confirmed (code) |
0x0023a5c8 |
Cfg_SetLockOn |
CfgLockOn: sets 0x005104ac. |
confirmed (code) |
0x0023a5d8 |
Cfg_SetCombatTimeout |
CfgCombatTimeout: sets 0x00510994. |
confirmed (code) |
0x0023a5e8 |
Cfg_SetStrafeRStick |
CfgStrafeRStick: sets 0x005104d0. |
confirmed (code) |
0x0023a5f8 |
Cfg_SetTurnRate |
CfgTurnRate: sets 0x00510304, 0x00510308, 0x0051030c, 0x00510310, 0x00510314, 0x00510318. |
confirmed (code) |
0x0023a630 |
Cfg_SetCombatDistances |
CfgDistances: sets 0x005104bc, 0x005104c0, 0x005104c4. |
confirmed (code) |
0x0023a658 |
Cfg_SetBurnRates |
CfgBurnRates: sets 0x005101e4, 0x005101e8, 0x005101ec, 0x005101f0, 0x005101f4. |
confirmed (code) |
0x0023a7a0 |
Cfg_SetTurnRates |
CfgSetTurnRates: sets 0x005101b0, 0x005101b8, 0x005101c0, 0x005101c8, 0x005101d0, 0x005101d8. |
confirmed (code) |
0x0023a970 |
Cfg_SetJumpIsAction |
CfgJumpIsAction: sets 0x0051099c. |
confirmed (code) |
0x0023a980 |
Cfg_SetPickupIsAction |
CfgPickupIsAction: sets 0x005109a0. |
confirmed (code) |
0x0023a990 |
Cfg_SetPickupIsGrab |
CfgPickupIsGrab: sets 0x005109a4. |
confirmed (code) |
0x0023a9a0 |
Cfg_SetBurnTime |
CfgBurnTime: sets 0x00510220. |
confirmed (code) |
0x0023ad88 |
Human_SetVoiceIndex |
HuSetVoiceIndex: the voice +0x3b0. |
confirmed (code) |
0x0023adc8 |
Human_GetVoiceIndex |
HuGetVoiceIndex: the voice +0x3b0. |
confirmed (code) |
0x0023ae00 |
Human_SetStateRespVoiceIndex |
HuSetStateRespVoiceIndex: the state-response voice +0x3b4. |
confirmed (code) |
0x0023ae40 |
Human_LockPad |
HuLockPad: the player record's pad lock +0x1e. |
confirmed (code) |
0x0023ae90 |
Human_LockPadMovement |
HuLockPadMovement: the player record's movement lock +0x1f. |
confirmed (code) |
0x0023aee0 |
Human_StartButtonMiniGame |
HuButtonMiniGame: starts mode 5 of the mini-game record with its buttons, times and Lua callback. |
confirmed (code) |
0x0023b0e0 |
Human_SetScale |
HuSetScale: the human's model scale. |
confirmed (code) |
0x0023b128 |
CNS_SetMissionInfoEnabled |
CNSEnableMissionInfo: sets 0x005109ac. |
confirmed (code) |
0x0023b138 |
Human_TeleportNear |
HuTeleportNearHuman: puts the human at a free point near another human, facing it. |
confirmed (code) |
0x0023b778 |
Human_SetSlowMo |
HuSetSlowMo: a player's slow motion (+0x3bb, the time factor 0x005102cc). |
confirmed (code) |
The update, controls and carried props¶
0x0023b838-0x0024e1f8 (inferred from where the rows lie; each row is graded): anim handles, the state update and its
control functions (the member-function pointer at +0x1bc, An AI's locomotion), falling and landing,
the look-at, the reticules and carried props.
| Address | Name | What it does | Evidence |
|---|---|---|---|
0x0023b838 |
Humans_InitAnimHandles |
Static initialiser: fills the handle records at 0x006b6710 with null handles and clip 0x2d2. | confirmed (code) |
0x0023bc80 |
Humans_StaticInitAnimHandles |
Humans_InitAnimHandles(1, 0xffff). | confirmed (code) |
0x0023bcf0 |
Human_ClearVelocity |
Zeroes the task velocity and +0x3a0, clears the bone cache's +0x460 and marks the move dirty (+0x255). |
confirmed (code) |
0x0023be98 |
Task_SendHandleMessage0 |
Sends message 0 with a handle to a task (Human_PickUpObject). | confirmed (code) |
0x0023cd30 |
Human_UpdateLedSlots |
When the move is dirty (+0x255), recomputes four aim points in world space: +0x1e0 = the world transform composed with +0x500 (bone 6, the head), +0x1f0 with +0x540 (bone 3), +0x200 with +0x4e0 (bone 2, the hips), +0x210 = the position + 0.3 m up (Combat: the led steer). |
confirmed (code) |
0x0023ceb8 |
Task_SetVelocityXYZ |
Sets a task's velocity from three floats (w = 1). | confirmed (code) |
0x0023cf00 |
Human_SetMoveVelocity |
Sets the velocity +0x30, its speed +0x1ac and the gait for that speed +0x1a8, and marks the move dirty. |
confirmed (code) |
0x0023cf70 |
Human_ClearTurn |
Clears the turn state +0x300, +0x304, +0x331, +0x332; attacks and climbs call it before steering. |
confirmed (code) |
0x0023d630 |
Human_SetRotationThunk |
Calls Human_SetRotation. | confirmed (code) |
0x0023d660 |
Human_UpdateLod |
Distance to the nearest camera (+0x334), told to the resource manager; the detail level +0x333 (0-4) from the thresholds at 0x005102f0, raised by time since the last update. |
confirmed (code) |
0x0023dc50 |
Human_EmptyStub4 |
Empty function (Player_TryJump calls it). | confirmed (code) |
0x0023e7e0 |
Player_UpdateHideOrder |
Orders the player's Warriors to hide when the player hides (brain +0x2d5), from Human_SnapToGround. |
confirmed (code) |
0x0023f9d0 |
Brain_CheckMoveArrival |
Whether an AI's move reached its target point (from Human_StateUpdate). | confirmed (code) |
0x0023fb00 |
Human_ControlHandlerName |
The name of the control function +0x1bc for HuGetControlName. |
confirmed (code) |
0x00240800 |
Human_RegisterContextAction |
Human vtable +0x124: registers a context action with the actionable manager (+0x664). |
confirmed (code) |
0x00240850 |
Human_UnregisterContextAction |
Human vtable +0x12c: unregisters it. |
confirmed (code) |
0x00240890 |
Human_SetContextRecord |
Sets the context record +0x660. |
confirmed (code) |
0x00242ce0 |
Human_ButtonTapMove |
The button-tap control function: steps the human forward by tapped buttons. | confirmed (code) |
0x002432b0 |
Human_EnterStepControl |
Saves the control (+0x1bc to +0x1c4) and installs the step control (0x00243420) unless state 0x180f3ff0 or already installed; from TakeStepAction_Start (Step control). | confirmed (code) |
0x00243360 |
Human_LeaveStepControl |
Leaves the step control: Human_UpdateControl, or Human_MoveAttached while grabbed or mugged. | confirmed (code) |
0x00243420 |
Human_StepControl |
The step control function (+0x1bc while a TakeStep runs): for an AI, one step clip by the angle between brain +0x110 and the facing (step, dash or fight shuffle), turned over half the clip, then leaves at once; waits while busy (Step control). | confirmed (code) |
0x002445f8 |
Human_SetButtonTapMoveControl |
Control function = Human_ButtonTapMove and clears its counters +0x670, +0x674. |
confirmed (code) |
0x00244620 |
Human_SetWheelchairMoveControl |
Control function = Human_MoveWheelchair. | confirmed (code) |
0x00244640 |
Human_SetButtonTapSequence |
Copies up to 16 button ids (null-terminated) into +0x67c and resets the index +0x678. |
confirmed (code) |
0x002446a8 |
Human_SetButtonTapParam |
Sets +0x6bc for the button-tap control. |
confirmed (code) |
0x002446b0 |
Human_SetThrowAimState |
Enters throw aiming: the control function and the aim point. | confirmed (code) |
0x00247a08 |
Human_SetFallStartPoint |
+0x2c0 = the given point when it is on the nav mesh, else +0x2b0. |
confirmed (code) |
0x00247a50 |
Nav_WalkDistanceTo |
The straight distance to a point a walkable line reaches, else the length of a route planned to it (AI); 0 with no area or no route. | confirmed (code) |
0x00247fc0 |
Human_StrikeShapeOn |
Turns a strike shape on (message 0x8f). | confirmed (code) |
0x00248110 |
Human_StrikeShapeOff |
Turns a strike shape off (message 0x90). | confirmed (code) |
0x002482e0 |
Human_CanHeadLook |
Whether the head may turn to look. | confirmed (code) |
0x00248428 |
Human_UpdateHeadLook |
Turns the head toward the look target each update. | confirmed (code) |
0x00248980 |
Human_GetLookPoint |
The point the head looks at: for AI the brain's target point (+0x90) when within 90 degrees of its heading +0x110, else ahead; for a player from the target. |
confirmed (code) |
0x00249050 |
Ground_ProbeBelow |
A ray straight down from 0.1 m above a point; true when it hits the level. | confirmed (code) |
0x00249bf8 |
Brain_InstallTypeHandlers |
Installs the handlers for a brain's type (17 types, table 0x0055ce40), using the owner's type for type 5. | confirmed (code) |
0x0024b190 |
Reticule_FindRecentFadeIn |
A reticule that started fading in recently. | confirmed (code) |
0x0024b3d8 |
Reticules_UpdateTarget |
Updates the target reticule over the player's target and the timed hints (0x0051054c). | confirmed (code) |
0x0024bf78 |
Reticules_ResetStatic |
Static initialiser: 0x006c7310 = -1. | confirmed (code) |
0x0024bfa0 |
Reticules_StaticInit |
Reticules_ResetStatic(1, 0xffff). | confirmed (code) |
0x0024bfc0 |
Human_SpawnHat |
Creates the hat object by name at the human, pins it and stores its handle +0x364. |
confirmed (code) |
0x0024c1a0 |
Human_SendHatScene |
Sends the hat message 0x32 (attach 6, the scene state) at a scene's start and end. | confirmed (code) |
0x0024c560 |
Human_PlaceObjectInHand |
Puts a world object into the hand by its type record, unless picking up already (held 0x4000). | confirmed (code) |
0x0024c7b0 |
Human_StickThrownKnife |
Sticks a thrown knife into the human. | confirmed (code) |
0x0024cac8 |
Human_DetachProp |
Detaches the object +0x358, marks its body dirty and drops it (message 0x3e). |
confirmed (code) |
0x0024cbb0 |
Human_CreateCarriedProp |
Destroys the prop at +0x348, creates the named object at the human, keeps it in +0x34c and attaches it at point 0x13 (Human_AttachCarriedProp); clip event 0x30 (Combat: props of a power move). |
confirmed (code) |
0x0024cd30 |
Human_AttachCarriedProp |
Attaches the prop +0x34c to attach point 0x13 with its type's offset. |
confirmed (code) |
0x0024ce40 |
Human_ReleaseCarriedProps |
Drops a held kind-0x27 object and clears the carried props +0x348, +0x34c. |
confirmed (code) |
0x0024cef0 |
Player_AttachGear |
For a player with the unlocked gear (unlockables 6/5, 6/6): spawns the gear pieces on its bones from the transforms at 0x00715480. | confirmed (code) |
0x0024d340 |
Player_RemoveGear |
Destroys a player's gear objects. | confirmed (code) |
0x0024d420 |
Player_MessageGear |
Sends message 10 with a value to each gear object. | confirmed (code) |
Navigation¶
0x0024e1f8-0x00250e00 (inferred from where the rows lie; each row is graded): the human's side of the navigation
mesh (on-mesh tests, path polygons, links); the planner is on AI.
| Address | Name | What it does | Evidence |
|---|---|---|---|
0x0024e1f8 |
Nav_IsOnMesh |
True when a point lies in a nav area. | confirmed (code) |
0x0024e478 |
Nav_GetWalkingDistance |
WalkingDistance: the length of the path between two points. |
confirmed (code) |
0x0024e608 |
Path_LengthFrom |
The length of a path from a point through its corners. | confirmed (code) |
0x00250398 |
Seg_ClosestPoint |
The closest point of a segment to a point, and its distance. | confirmed (code) |
0x00250b10 |
NavLink_ClearAvoidByKind |
Clears the avoid mark of links of a kind (glass broken, doors opened). | confirmed (code) |
0x00250bb8 |
NavLink_SetKindNearest |
Sets the kind of the nearest link pair to a point. | confirmed (code) |
0x00250db0 |
NavLink_ConvertJumpToDoor |
ConvertJumpToDoor: turns a jump link into a door link. |
confirmed (code) |
Movement styles, clips and actions¶
From 0x00250e00 (inferred from where the rows lie; each row is graded): movement styles and anim slots, the clip
builders (Clip selection), the turn and arrival clips of an AI, and the per-human actions.
| Address | Name | What it does | Evidence |
|---|---|---|---|
0x00250e00 |
NavLink_DisableDoor |
Marks the door links near a point to be avoided (kind 0x10), as when a door shuts. | confirmed (code) |
0x00250e48 |
NavLink_EnableDoor |
Clears the avoid mark of the door links near a point (kind 0x10). | confirmed (code) |
0x002510a8 |
NavNode_IsLinkBlocked |
Whether a node's link list holds a link to the given node with a negative cost (a blocked edge). | confirmed (code) |
0x00251188 |
PathRequests_Clear |
Clears the 32 path-request slots (0x110 bytes each, at 0x006cc340 downward). | confirmed (code) |
0x002516d0 |
PathNode_SetIndex |
Stores a node pointer as its index (offset from the node array 0x006ca21c / 32) at slot i of a list. | confirmed (code) |
0x002516f0 |
PathNode_GetAt |
The node pointer at slot i of a list (the node array 0x006ca21c + index x 32). | confirmed (code) |
0x00251710 |
Ptr_Set |
Writes a word: a one-line setter. | confirmed (code) |
0x00251718 |
PathNode_IsEdgeBlocked |
Whether the edge between list slots i and i+1 is blocked (NavNode_IsLinkBlocked). | confirmed (code) |
0x00251768 |
PathNodes_ResetParents |
Sets every path node's parent (+0x1c) to none (0xffff) before a search. | confirmed (code) |
0x00251820 |
Path_DistanceCost |
The A* cost of a distance: length x 16, as a u16. | confirmed (code) |
0x00251a00 |
PathNodes_ClearOpenMarks |
Clears the +0x18 mark of every node on the open list after a search. | confirmed (code) |
0x00252108 |
Nav_NearestEdgePoint |
The nearest point on a nav polygon's edges (within 1.5 m of its box, at or above the point's height less 0.1) to a point; only when closer than 1.5 m, then dropped to the ground. | confirmed (code) |
0x00252ae0 |
Nav_FindPolygonUnderThunk |
Nav_FindPolygonUnder with a height of 1.0. | confirmed (code) |
0x00252b08 |
Nav_ClampMoveToRange |
Limits a move to a range: beyond it the point is kept only when the nav mesh connects the two (Nav_IsConnected), else the start. | confirmed (code) |
0x00252c28 |
NavLink_SetBlocked |
Sets or clears a path polygon's blocked flag 8 (+0x48) at a point. | confirmed (code) |
0x00253068 |
PathSearch_ResetCount |
Global 0x005105b0 = 0. | confirmed (code) |
0x00253118 |
PathSearch_InitLists |
Static initialiser: the open heap, the closed list and two small lists of the path search. | confirmed (code) |
0x00253170 |
PathSearch_StaticInit |
Calls PathSearch_InitLists(1, 0xffff). | confirmed (code) |
0x00253190 |
PathHeap_Construct |
Constructs the open heap (count 0). | confirmed (code) |
0x002531b8 |
PathHeap_Clear |
Count = 0. | confirmed (code) |
0x002531c0 |
PathHeap_Push |
Pushes a node on the binary min-heap keyed by the node's cost (+0x16), at most 1000. | confirmed (code) |
0x00253268 |
PathHeap_PopMin |
Removes and returns the cheapest node of the heap. | confirmed (code) |
0x00253350 |
PathHeap_Find |
The heap position of a node, or 0xffff. | confirmed (code) |
0x00253398 |
PathHeap_IsEmpty |
Count is 0. | confirmed (code) |
0x002533b8 |
PathHeap_SiftUp |
Moves a node whose cost dropped up the heap. | confirmed (code) |
0x00253480 |
PathList_Construct |
Constructs the closed list. | confirmed (code) |
0x002534a8 |
PathList_Get |
Entry i of the closed list. | confirmed (code) |
0x002534c0 |
PathList_Clear |
Count (+6000) = 0, last (+0x1772) = -1. | confirmed (code) |
0x002534d0 |
PathList_Add |
Appends a node to the closed list, at most 3000. | confirmed (code) |
0x00253528 |
PathList_Count |
The closed list's count. | confirmed (code) |
0x00253538 |
PathPair_Construct |
Constructs a three-short record (zeroed). | confirmed (code) |
0x00253560 |
PathPair_Clear |
Zeroes the three shorts. | confirmed (code) |
0x00253598 |
PathCursor_Construct |
Constructs a two-short record (zeroed). | confirmed (code) |
0x002535c0 |
PathCursor_Clear |
Zeroes the two shorts. | confirmed (code) |
0x00253e68 |
Human_SetMoveStyle |
Replaces the top movement style (pop then push), or applies style 0 when none is stacked. | confirmed (code) |
0x00253f78 |
Human_RemoveMoveStyle |
Removes a movement style from the human's stack and re-applies the top one (Anim slots). | confirmed (code) |
0x00254060 |
Human_GetMoveStyle |
The movement style byte of the human's record (styles 4 and 6 use the slow turns). | confirmed (code) |
0x002541c8 |
Human_CheckMoveStyles |
Drops a held-object movement style (1-6) that no longer matches what is held, style 14 without state 0x100000, and an object held with no style. | confirmed (code) |
0x002544b8 |
Human_GetAttackerReach |
The reach (attack table +0x4) of the move the human's attacker is playing on it. | confirmed (code) |
0x002545a8 |
AnimRange_SetFarRange |
Writes an anim id's far range (seconds x 1000, mm) into the human's Anim Range List (+0x160). | confirmed (code) |
0x002548c8 |
AnimRange_SetDamage |
Writes an anim id's damage (+0x0a) into the human's Anim Range List. | confirmed (code) |
0x00254e20 |
AnimRange_GetHitFlags |
The Anim Range List's u16 +0x0e for an anim (-1: the current one); Hit_ResolveBlock tests 0x600. | confirmed (code) |
0x00255540 |
Human_UpdateWorkout |
State code 0x1b: the workout on gym equipment (start, repetitions with the pad's pumps, end), its clips and rate. | confirmed (code) |
0x00255cf8 |
Human_UpdateSpecialIdle |
State code 0x1d: the special idle (672 start, then 671 looping) from the script's clips; ends the special state. | confirmed (code) |
0x002570e8 |
Human_ComputeThrowVelocity |
The launch velocity of a thrown held object: aimed at the target (led by its speed) or along the facing, scaled by the object's weight and the human's scale. | confirmed (code) |
0x00257ce8 |
Human_EndThrowAim |
Cancels the throw aim and drops what cannot stay in hand (kinds 4 and 6; any but sets 1 and 2 without human flag 0x2000). | confirmed (code) |
0x00257d70 |
Human_EndThrowAimDropHeavy |
Ends the throw aim: drops a held object of kind 4 or 6, then (no human flag 0x2000) drops any other when the human is hurt or holds set 3, unless a cop (brain type 1) holds set 1 or 2 (Combat: dropping what is held). | confirmed (code) |
0x00257e38 |
Human_EndThrowAimIfHurt |
Ends the throw aim and drops a held object of kind 4 or 6; a hurt human (no human flag 0x2000) also drops any other, unless a cop (brain type 1) holds set 1 or 2 (Combat: dropping what is held). | confirmed (code) |
0x00257ef0 |
Human_KnockOffHatIfDown |
Class 0x80 down or dead: knocks the hat off (Hats). | confirmed (code) |
0x00258330 |
Human_KnockOffHat |
Throws the worn hat off (unless HuSetKeepHat or kind 27): spin 8 rad/s, drop at 2 m/s, +0x364 cleared (Hats). | confirmed (code) |
0x00258968 |
Human_ReleaseAll |
Lets go of everything: breaks a pair, ends a mini-game, drops the held object, clears the object target, ends rage, clears state 0x18800f, empties the move-style stack and sets style 0x12. | confirmed (code) |
0x00259368 |
Human_UpdateFaceClip |
Queues the face clip 698 + 3 x the record's +0x1c (and its pair 700 + 3n), fading out the old one over at most 1/30 s; record +0x24 holds the current face clip. | confirmed (code) |
0x0025a178 |
Human_BuildCombatIdle |
Anim state 11: the fight-stance idle (slot 11) looping after a fade; from state 2 the fade holds 0x80000. | confirmed (code) |
0x0025a310 |
Human_PushRecordOverlay |
Sets the anim state and a held flag and pushes the record's +0x20 clip as a 0.05 s overlay holding that flag. | confirmed (code) |
0x0025aeb8 |
Human_TryIdleFidget |
With nothing held and record +0x3bc set, a 30% chance to play the fidget 601 as an overlay holding 0x200000. | confirmed (code) |
0x0025af90 |
Human_BuildPairedFourMix |
Builds the four-way gait mix (slot 4, anim state 4, action 3) for the human and for its pair partner (+0xc4), after a fade (0.1 s longer with held flag 0x40000000). | confirmed (code) |
0x0025c240 |
Human_StartTurnOnSpot |
An AI's turn on the spot: a turn clip (395-398, slot 8 or 15 variants) and the rest of the angle over the clip (An AI's locomotion). | confirmed (code) |
0x0025c5b8 |
Human_TryArriveClip |
An AI covering the last metres to its point with one clip: a step or dash from rest, the run stop at a run (An AI's locomotion). | confirmed (code) |
0x0025cb90 |
Human_PlayOneShotClip |
Plays a clip once over a 0.1 s fade, then the idle (or combat idle) looping, holding the given flag; state code 0. | confirmed (code) |
0x0025cd58 |
Human_BuildStanceEightDir |
Anim state 14: the fight-stance eight-direction blend (slot 14) driven by record +0xdc x 4/pi, after a fade of 0.15 s (none from state 2). | confirmed (code) |
0x0025cf10 |
Human_LaunchJumpThunk |
Calls Human_LaunchJump. | confirmed (code) |
0x0025cf30 |
Human_BuildJumpLoop |
Anim state 25: the jump loop 434 after a 0.1 s fade, then the launch (Anim states in the air). | confirmed (code) |
0x0025d020 |
Human_BuildFallClip |
Anim state 26: the drop cycle 428, or the long-fall cycle 422 / 425; a long fall from a grab gives the grabber style points and rage. | confirmed (code) |
0x0025d2d0 |
Human_ResetToIdleState |
Anim state 0 unless state 0x1c00000000; clears landing 0x2000000000 and the down and stun timers. | confirmed (code) |
0x0025d348 |
Human_ResetToMoveState |
Anim state 4 unless state 0x1c00000000; clears landing 0x2000000000. | confirmed (code) |
0x0025d390 |
Human_BuildLandingClip |
Anim state 27: the jump end 435 / 436, the drop land 429 or a long-fall clip, then the idle or a gait blend (Anim states in the air). | confirmed (code) |
0x0025e0e0 |
Human_BuildSlotLoopWithIntro |
Loops the record's +0x88 clip, after the +0x7c clip (holding 0x20000000) unless asked not to. | confirmed (code) |
0x0025e250 |
Human_EndDownState |
Clears record +0xbe and gets the human back up: wakes it (0x40000), unarrests it (0x20000), gets it up (0x80000), else back to the fight stance. | confirmed (code) |
0x0025e900 |
Human_EndObjectSteer |
End hook of the steer clip: clears the object target and sets push weight back to 1.0. | confirmed (code) |
0x0025e948 |
Human_StartObjectSteer |
Plays clip 666 then the idle, holding 0x4000, push weight 1e9, steering onto the object target over 0.1 s. | confirmed (code) |
0x0025eee0 |
Human_UpdateGaitBlendTarget |
Sets the top gait blend's target from the speed (Clip selection). | confirmed (code) |
0x0025ef58 |
Human_PushScriptOverlay |
Pushes a script clip as a 0.1 s overlay holding 0x100, unless the human is busy. | confirmed (code) |
0x0025f028 |
Human_BuildAnimState |
Calls the builder for anim state 0-7 from the table at 0x0055db50. | confirmed (code) |
0x0025f1b8 |
Human_BuildIdleClip |
Builds the idle (or a given loop) with stealth and stalking variants (Clip selection). | confirmed (code) |
0x0025f450 |
Human_RebuildAnimOnMove |
Forces the anim state to be rebuilt (flag 0x20000000) after state code 18 or while holding 0x20000 (clearing it). | confirmed (code) |
0x0025f4d0 |
Human_BuildMoveState |
Anim state 4: the gait blend via Human_BuildMoveTasks, or the stance eight-direction blend (Clip selection). | confirmed (code) |
0x0025fba0 |
Human_BuildState25 |
Builds the jump loop and sets anim state 0x1a. | confirmed (code) |
0x0025fbd0 |
Human_BuildState26 |
Builds the fall clip and sets anim state 0x1a. | confirmed (code) |
0x0025fc00 |
Human_BuildState27 |
Builds the landing clip and sets anim state 0x1b. | confirmed (code) |
0x0025fcb0 |
Human_UpdateDealerPair |
The paired clip 668 between two humans: within 1.875 m, a clear way and grabbable, aligns both and plays it on both; otherwise idles both. | confirmed (code) |
0x0025ff18 |
Human_StartObjectSteerIdle |
Starts the object steer, then anim state 0 and action 0. | confirmed (code) |
0x0025ff50 |
Human_BuildGaitBlendFromValue |
Builds a gait blend from a gait value, rounded down to 0-3 (Clip selection). | confirmed (code) |
0x002726d8 |
Human_SetStrafeDirection |
In a fight stance with a brain: record +0xdc = the movement direction relative to the facing (0-2π), for the eight-direction blend. | confirmed (code) |
Game-state functions¶
Functions of the game-state module (0x00417af0-0x00424e50, a range inferred from where its functions lie: inventory,
statistics, flags, configuration workers) that belong to this page, by address.
| Address | Name | What it does | Evidence |
|---|---|---|---|
0x004197a8 |
GameState_CheckGameOver |
each update, when enabled (+0x155), outside Rumble (+0x158) and not already over (+0x14c): over when a player has state flag 0x100000000; when every player is knocked out or cuffed, not over while player 1 is cuffed, awake and can uncuff himself (upgrade (6, 15) and item 6); over when no gang member can help (Gang_NoneAbleToHelp); over at once when player 1 is out with no flash (item 1) or byte +0x414 + player is set; otherwise every 37th update asks Gang_CanReachToHelp and is over after 4 failures in a row (+0x56e6). The mission-failed reason is GSTRING 21 when cuffed (+0x118 = 1), else 20 |
confirmed (code) |
0x00419a68 |
GameState_SetWarChiefPlayer |
+0x222 = the war chief's player number (0 or 1; larger values ignored); from GameState_SetWarChiefHUD |
confirmed (code) |
0x00419e10 |
GameState_SwapPlayerSlots(a, b) |
from Human_SetWarChief: swaps two players' entries in the player list (+0x228), their player numbers (human +0x1b0) and the pads' player numbers (pad record +0x42) |
confirmed (code) |
0x0041a9c0 |
GameState_SetLeadPlayer |
from Human_SetWarChief: puts player 1's camera target first (CameraTargets_Swap) and moves the old leader's pad bit in +0x456 to the new one |
confirmed (code) |
0x0041cf78 |
GameState_AnimEventHook |
empty; called by Anim_FireFrameEvents |
confirmed (code) |
0x0041d428 |
Cfg_SetCharClassAttribs |
CfgCharClassAttribs's worker: a 16-byte record per class at W_GameState + 8 + class × 16 (two floats at +0x0c, +0x14 and four bytes) |
confirmed (code) |
0x0041d890 |
GameState_EnableGameOverCheck |
EnableGameOverCheck: byte +0x155 (1 at set-up) |
confirmed (code) |
0x0041ddb8 |
GameState_IsLevelEnding |
whether +0x14c (game over or complete) is set |
confirmed (code) |
Coney's implementation¶
Coney loads a character's three resources, plays its clips, and plays Rembrandt as player 1 with the human's locomotion and the follow camera (Camera):
src/characters/character_list.*reads the Character List fromwarriors.glrand finds a record by model name;character_assets.*loads the three resources by the records' hashes ("%u"file names).src/characters/clump_reader.*andcharacter_model.*decode the clump (frames, HAnim, skin, material) and the native geometry, with the shared PS2 decoder insrc/graphics/ps2_world_mesh.*, into merged skinned vertices and triangles; no librw outsidesrc/platform/.src/characters/character_data.*resolves the 722 anim ids to clips (Animation): the distinct slot values, smallest first, each take the next clip popped off the object stack, which gives Rembrandt's 408 walk and 413 walk-start as above.src/characters/character_rig.*builds the skeleton and the skinning matrices (pose bonen + 2· the pose turn · the inverse bind matrix) and skins on the CPU.src/platform/character_mesh.*holds the skinned model as two librw atomics: the first texture-coordinate set with the character's texture, and the second set for the material's dual layer. In play (src/platform/play_lighting.*) every human is drawn in the two passes of Drawing a human: the second pass blends the blood texture (src/graphics/human_blood.*, the threecharbloodtextures ofglobal.pak) by its alpha below 90% health.--view-character [NAME] [--anim CLIP](Running Coney) shows a skinned character playing a clip in place on the fixed 30 Hz step, with a pad-driven orbit camera.--render-references DIR(Running Coney) writes a 256x256 transparent PNG of every character, posed at its default clip's first frame (and of every object, Level loading), from a fixed three-quarter camera (src/characters/reference_render.*frames and reduces;src/platform/reference_renderer.*draws offscreen).src/characters/anim_set.*answers an anim id from the character's own data, then from the generic character data0x9da2e531(594gen_*clips) in place of the default table, with the clip's rate from the Anim Range List flags (CfgAnimSpeeds) and its speed (root displacement × rate / duration).src/scripting/anim_callbacks.*is the animation callbacks' 16-slot table and its three bindings; a human's task stack reports each anim id it starts (AnimTaskStack::setStartHook: a change, a clip handing over to its next, a gait blend moving to another clip), and the scripts' brains run the first matching callback with (human, anim id). Coney choice: the callbacks run after the characters' step, in order, not inside the animation code.src/human/locomotion.*is the pure maths: the camera-relative stick, dead zone and run threshold (PlayerRecord_Update), the target speed and gait, the per-gait turn limit with its ease and carry, acceleration, the skid rule, the slope factor and the gait blend's target (0x0025ec28); the sprint's target speed; and the body's lean (Human_Lean,0x00248df0). The turn limits and ease are the values in play (20°, 18°, 18°, 16°, 24° in a combat stance, full at 2.0 rad): the defaults, and for a level--play-levelruns, what the preload'sCfgSetTurnRatesandCfgTurnRatecalls recorded.src/human/stamina.*is the stamina meter (Sprint and stamina): the drain at the sprint gait, the refill (the maximum and the refill each human's power class's, Stats) and the gait, airborne and "run with L2" blocks, andPlayer_UpdateSprint's rule (L2 held and stamina not 0, asked again every update).src/human/body.*holds the walking body's sizes for Walls and steps: the sphere of 0.35 × scale² (× 1.4286 for the player: 0.4705 m) centred 0.05 above its radius, the 0.25 m wall height, the fence materials passed through while climbing over, and the airborne push-out sphere (0.5 × scale for a player). An AI human's sphere is 0.35 × scale × scale (human::aiWalkingRadius, its body+0x60being 1.0). The walls themselves are the physics world's sweep (src/physics/, Physics), run byHuman::sweep: wall triangles|n.z|≤ 0.65 that the move goes into, the 0.25 m low-and-thin rule, the sphere swept from where the move starts and met where it first touches (face, edge or corner), advanced to 0.01 m short of it along the normal, then the slid move swept again, up to three passes. So a face lower than the sphere's centre stops it where it meets the face's top edge, and a face under 0.25 m is walked onto in one update with no change of speed or clip. Only a player's slid velocity is written back (v −= (v · n) n, the slide response), so a wall met at a steep angle brakes him; an AI's velocity is never braked by a slide. Nothing pushes a walking sphere out of a face it overlaps, so a move into a face leaning back rises along it (Slopes). The corner slide (flag bits 14-15) is added to the move on the ground unless the human is in a pair or blocking. While airborne the same sweep takes the move across the ground, and the air sphere's push out of the nearest wall waits for the next move.src/human/jump.*is the jump's checks and launch (Jumping): faster than 3.3 m/s and the stored gait at jog or above, refused when a climbable face lies within 5.5 m; the run or sprint speed forward and 5.5 m/s up. A start clip does not stop it.src/human/climb.*is the climb choice (Climbing): the two forward rays, the climbable test, the probe 0.4 m behind the face with its windows, the 2.5 m fence ray, the clip ids, each clip's reach (its type-8 event vector) and the reach window.src/human/human_animator.*is the anim state and its builders: idle, move, the jump loop (25), the drop cycle (26), the landing (27: 436 handing over to a gait blend, or 435 and the idle), the run stop and the climb's three clips (Coney's states 101 and 102); the walk or run start played at once and handed to the five-clip gait blend, the 0.1333 s fade between moves, the idle's fade by how much of a start clip has played, and the walk start swapped for the run start in its first half (Clip selection). Its clips hold their record+0x08bits while they play (Tasks): a start clip0x10000000, the landing0x1000000, the run stop and the climb's first and last clips0x80000, the combat moves theirs (Combat).- Dynamic animation slots (Animation):
human::ScriptStateholds the seven slots thatHuUseAnimandHuUseAnyAnimboth fill. An id already in a slot reuses that slot, a new id takes the first free one, and an eighth id is refused. The play mode hands the idle's clip (0x184) toHuman::setIdleClip()and every other one toHumanAnimator::setOverride(), which all of the human's own clip lookups go through (a paired clip taken from another human's set does not). Stand-ins: a clip already playing is not restarted when its slot changes (the original's state flag0x20000000), and a dynamic clip plays at the rate of the id it replaces. - Hats (Hats;
src/world_objects/hats.*,src/platform/play_level_hats.cpp): the class's hat when the human is made, andHuPlaceHatOnHead's, each a pinned world object on bone 6. A Warrior wears it at his set's fitting; anyone else at the hat type's own pose (CfgObjarguments 21 and 22). The offset is multiplied by the wearer's scale. A hat comes off when a knockdown grounds its wearer (not a cop's, nor class0x80's or 13's unless he is out of health), when he is mounted, or when a new hat replaces it, unless he hasHuSetKeepHator the hat is kind 27. It is thrown at 2 m/s spinning at 8 rad/s, then falls until it rests on the ground.HuHasHatgives the handle of the hat worn. Stand-ins: the side of the blow is not modelled, so every hat leaves behind its wearer (side 2); the fall is Coney's own (gravity 9.81 m/s², stopping where it meets the ground); a deleted human's hat goes with him; the respawn timers are not applied. src/human/locomotion_gate.*is the locomotion gate (Tasks):stickBusy()(Human_IsBusy's mask0xaeebf7ff, in the air or held: no stick step) andstickVelocityGated()(0x110c0880, or state code 5 or 6: the stick turns the human but sets no velocity).src/human/human.*is the human: spawn (a 2.5 m ray from 1 m above, feet 0.01 above the hit), its per-player record (the stick, the camera's forward, the command and the buttons, written by the pad or a brain) and its update in three passes (Tasks):animate()(the animation),updateState()(root motion of the clips that move the body, locomotion through the gate, air control or a climb's move, gravity, move or fall, the meters, the lean) andupdateActions()(the dispatcher from the record, triangle's actions, then the anim state); the ground snap, the fall and landing, the walking body, the airborne push-out, and the climb from its probe to its last clip (the move to the start point, the re-probe at the first clip's end, the rise onto a wall). A pad-driven human with the wheelchair flag runs wheelchair control in place of the locomotion, its constants per 1/30 s step; stand-ins: its sticks are ignored so no locomotion clip starts, and no start or wheelchair clip plays.src/human/humans.*is the characters' step (Humans_Update): every human's record (the commands of those no pad drives cleared), the brains' hook (empty until the AI lands), then the animation of all, the locomotion of all and the actions of all, walked forward and backward on alternate steps. It runs on Coney's fixed 1/30 s step.src/human/player.*is player 1 (the character at scale 0.97, the human, the follow camera; L2 held sprints, triangle pressed acts): it writes the pad into its human's record and steps the characters, and keeps the snapshot drawing reads: the previous and current feet, heading, lean, pose (each bone slerped) and camera, interpolated for a renderer that draws between steps.- The debug menu's tunables (Debug menu) expose the body, sprint, jump, climb and lean values above, each defaulting to the original's.
--play-level NAME(Running Coney) plays it:src/platform/play_level_mode.*steps the player, streams the level and runs the visibility pass in its update, and draws from the snapshots of the last two steps, blended by the frame's alpha, in its render (Update and render). A disc test ([frame_rate]) plays both scripts at 30, 60, 144, 240 and 1000 frames a second and with irregular frames and checks the player, the camera and the streaming come out bit for bit as in lockstep.
Disc test ([characters], counts only): all 543 records load (128 models, 52 character data resources, 507
dictionaries); 150,509 vertices and 155,493 triangles; 1,692 clips and 2,843 resolved ids; the joint mismatch of a
clip's first pose over the reference pose averages 0.045 m (worst 0.098 m); the
skinned heights run from -0.07 to 4.06 m (2.29 m at most over the bind rotations; which first clips reach higher is
not looked at yet); every model's bind rotation for
bone 2 is 120° from the reference pose's, and 292 of the bones 2-33 over the models are more than 10° from it.
Disc test ([player], counts only): Rembrandt's speeds from his clips are the runtime values (walk 1.629, jog
4.857, run 7.801, sprint 10.245 m/s); at level99's start he lands at z 0.25, idles in 388, takes the walk start 413
at a 30 % stick (not moving on its first update, then 0.762 m/s: the clip's root motion × 0.97), walks in 408 at
1.629 m/s, gains 0.8 m/s per update to the run (410), skids into the run stop (417) on release and then idles, and
never leaves the ground or passes through the scenery he is run into; the same script gives the same path twice.
On the default sandbox's ledges he walks onto the 10 and 25 cm blocks (the 25 cm block's 3 m wide face is two
slivers the 0.25 m rule skips) and stops at the 50 cm one, 0.484 m short of its face, where the sphere meets its
top edge.
Disc test ([traversal], positions and hashes only; Rembrandt on the sandbox's
parkour course), each run against the runtime values above:
- Sprint (L2 held, stick 0.8 then 0.96): a walk at 1.629 m/s and no drain at 0.8; 10.245 m/s at 0.96 for about 6.75 s until stamina is empty, then 7.801 m/s in one update; stamina held at 0 while L2 stays down and refilled at 40 per second once it is let go; letting go of the stick at the run plays the run stop (417) for about 24 updates.
- Jumps: a run jump at 7.801 m/s and a sprint jump at 10.245 m/s, both 5.5 m/s up, rising 1.06 m and landing in 436; the sprint jump carries about 8.5 m (24 updates in the air and the landing update); letting go of the stick in the sprint plays the run stop (417). A run jump clears a 5 m gap between two 2 m platforms, the feet ending one update below the far platform's top before they land on it.
- Fences of material 30, from a run: the 1 m short fence (446) and the 2 m fence (440) are climbed with the feet at ground height, through the fence; the 2.6 m fence is neither climbed nor jumped, and stops the body at its face.
- Walls and blocks with flag
0x80, standing: the 0.75 and 1.0 m walls are short-wall climbs (455) onto their tops; the 2.0 m block is a wall climb (449) onto its top; the 3.0 m block is not climbed. - Kerbs and ledges: a 20 cm kerb is walked onto; 30, 50 and 65 cm ledges stop the body at their face.
Coney choices where the research is silent: the material takes its dictionary's only texture; bones 0 and 1 rest at the identity; weights are used as stored, not renormalised; the viewer's lights, camera and clip keys, and the reference images' pose, camera and lights, are Coney's own. For the human:
- The default anim table: slots set to the default (
0xffffffff) are answered by the generic character data0x9da2e531, whose clip speeds match the runtime-confirmed ones exactly (380, 407, 409, 410, 411); the table at resource manager+0x70is not decoded. - Standing: a human not asked to move counts as moving while faster than a quarter of the walk speed. The
original's getter
0x00221580is the sneak-walk speed (record+0x16c×+0x3a4, 1.585 for Rembrandt; confirmed (code)), so the original's threshold is 0.396 m/s against Coney's 0.407. Coney's reading: a start clip no longer asked for gives way to the idle at once, whatever its root motion's speed: the idle builder fades over a start clip still playing (0x0025f770, above), which the speed test alone would never reach. - Falling: state 26's builder loops the drop cycle (slot 26, 428) with the idle's 0.15 s fade; a drop lands straight into the idle or the move, without the drop land (429), the long-fall cycles or the ground roll.
- Locomotion while a start clip plays keeps turning (only the horizontal velocity waits for the clip), as the
locomotion gate gives it (confirmed (code)): the start clip holds
0x10000000, which zeroes the stick's velocity but is outsideHuman_IsBusy's mask, so the turn still runs (Tasks). In the air only a jump steers; a fall keeps its velocity, and no clip's root motion moves the body while airborne. Standing (no gait blend yet), the update the start clip begins sets no velocity, as at runtime. - The start clips hand over early: the walk and run starts give way to the gait blend on the update after which less than an update of them is left, so they last 13 updates as at runtime (playing to the end would take 14). From their first moving update they move at the clip's root speed × 0.97, as at runtime (0.762 m/s for the walk start, 2.773, 2.486, … for the run start), through the sampler's reading of the clip's keys (Animation).
- Root motion of every clip that moves the body on the ground is scaled by the body scale (0.97 for Rembrandt); a wall or short wall climb's rise at its second clip is not (it matched the runtime unscaled).
- The jump's gait test reads the stored gait (
+0x1a8, the nearest) where the code reading names the "reached" gait: the runtime jump 7 updates into the run start, at 3.35 m/s, passes only with the stored gait. - The body is the original's walking sphere (Walls and steps), but pushed out of the nearest wall triangle by the distance to the triangle's closest point rather than swept along the move; the push tries 3 passes, then stops. The low-and-thin rule takes the triangle as given, so a 0.25 m face exactly (a sliver) does not stop the body, and a face from 0.26 m does.
- Landing probes from 1.0 m above the feet, as the ground snap does. A floor found on that segment is landed on by the first update that starts with the feet 0.17 m or more below it (When the landing happens); until then the body falls on through it (0.19 m after a run jump, as at runtime). The landing is at that update's position across the ground, so the landing update keeps the full horizontal speed.
- Stamina carries one fraction of a point between updates, started again when the meter changes direction.
- The run stop (417) plays after a skid, after the 0.1333 s move fade, which blends only the pose: the gait blend
it fades from has no root velocity, so the body moves at the clip's 2.244, 4.996, 5.101, … m/s from its first
update, as at runtime. The skid's own update still turns one step and the run stop then holds the facing. The skid
needs the run speed ≤ the speed, compared exactly (The run stop at a run): a sprint always skids, a
steady run only when its speed lands on or above the run speed. Coney's reading of the speed: the velocity's
length with each multiply, add and the square root rounded toward zero, as the PS2's floating-point unit rounds
(
core/ps2_float.h); the velocity itself is Coney's, so a steady run reaches the run speed on about 6 % of updates against the original's 10 of 93. What builds the run stop is not traced. It holds0x80000, which the locomotion gate reads (busy and no stick velocity). The skid sets no state code (the original's 9): the run stop's own bits gate the same updates. - The idle's fade (0.15 s, 5 updates) holds
0x10000000, so after a stop, a release or a block the stick turns the human but the walk start waits for the fade to end (Tasks). Over those updates the last stick held turns him on, its magnitude × 0.8 each update, eased from rest at the standing turn's 20° limit; Coney's reading of the ease (afterrun_circle's release it turns 0.666°, 1.136°, 1.409°, 1.515°, within 0.004° of the original's). Coney never sets state code 5 (turning in place): nothing reads it apart from the gate, and the fade holds the same updates. - The landing has no fade, so 436 moves the body at its 4.23 m/s from its first update as at runtime, and hands over to a gait blend at the jog (1.0).
- The air turn is limited to 4° an update, what a sprint jump turned at runtime; which gait's limit the original uses is not traced.
- Climb reach:
r1andr2are read as the reaches of the standing and the running first clips (for the fence, 437 and 440). Rembrandt's type-8 vectors are 0.77 / 2.31 m (fence), 0.64 / 1.39 m (short fence), 0.65 / 2.30 m (wall) and 0.78 / 1.55 m (short wall), which give a running fence climb from 1.23 to 5.09 m: the runtime tap at about 4.4 m started one and the one at 4.9 m did not (the 4.5 m ray). The chain's first two clips would give about 1.2 m and no running climb from 4.4 m. - The climb's move to its start point takes 2 updates from a run and 1 standing; the first clip's clock runs through it, as the original installs the clips with the move, but its root motion moves the body only once the move is done. On the sandbox's 2 m fence 440 lasts 11 updates from the tap's, as at runtime.
- The rise: walls and short walls move at once at the second clip's start, by its root displacement (450 and 456 carry none in section A; their whole displacement, 0.98 and 1.06 m, as at runtime) and up to the top the probe found; fences go by the second clip's own root motion, as the runtime's 441 carried the body through.
- Climbing over lasts the second clip: from the re-probe at the first clip's end to the third clip's start (at
runtime 441 had record
+0x080x40and 442 did not). Meanwhile the fence materials (30, 31, 122) are not walls, and not ground for the snap and the landing, so a low fence's top does not lift the feet. This differs from the original and breaks the hangout railing (checklist item 46): the original runs no wall sweep and no ground snap while0x40is set, so a climbed face of any material lets the body through, but Coney still sweeps the walking sphere with only 30, 31 and 122 left out. The hangout's railings are material 7 with flag0x80(Collision), so 443 and 444 play and the sweep holds the body 0.48 m in front of the face: the vault ends on the same side. A climbed face that is any other material with flag0x80or0x4(car shells are material 0) does the same. And had the body passed, Coney's snap would find no ground over the 3.2 m drop part-way through 444, so the climb would end in the idle at once (followClimb's airborne end) instead of the fall at 444's end. Fix: while climbing over, skipsweepandsnapToGroundand move the feet by the velocity and the clip's root motion, height included (Human_Move's0x40branch); from the third clip on, move as now, and when the snap then finds no ground start the fall (anim state 26, the fall clip, 428 at runtime) in place of the idle and the third clip. Test: a synthetic railing of material 7 with flag0x80, 0.9 m tall and one-sided toward the player, on the edge of a floor with another 3.2 m lower behind it: the player standing 0.6 m from it, the stick at 50 % toward it and triangle, plays 443 and 444, is past the railing's back at 444's end with the feet still at the upper floor's height, never plays 445, then falls and lands on the lower floor beyond the railing; the same railing over level ground ends on the far side with 445 played. - The re-probe at the first clip's end accepts any triangle, not only a climbable one.
- The climb's fade: none; the first clip moves the body at its full root speed from its first update after the move (5.79 m/s on the 2 m fence, as at runtime). The update the chain goes on to its second or third clip moves nothing, as the first update of 441 and of 442 did at runtime; why is not traced.
- The lean: the four limits go with walk (and standing), jog, run and sprint; the drawing rolls the body by the lean about its forward axis through the feet.
- Context and object actions (triangle's second and fourth tries) are hooks that never succeed yet, so triangle goes on from a refused climb to the jump.
- Camera through a fence: the follow camera's ray passes through material 30 (Camera), so it keeps its distance through a fence climb as the original's does.
- Out of the world (20 m below the mesh's lowest point): the human is put back at the start instead of failing the mission.
- The gait blend's leading clip uses a tolerance of 0.001 when it compares the value with its target.
- Level starts: the start comes from the level's own script at run time: its
HuCreatefor player 1 at the checkpoint (GetCheckPoint), kept by Coney'sHuCreatebinding (Level loading); the story's way in and--play-level NAME [--checkpoint N]both run it. A level that places player 1 at a flag (the hub, the Rumble arenas) starts him on the flag (World flags). The character's lights (ambient 0.45, one directional 0.7) stand in for the LightManager, and he is drawn between the level's two worlds. - Handing the player over (
ScriptedBrains::changePlayerGang,PlayLevelMode::takePlace, from Players): a laterHuCreatewith player argument 1 makes an AI human (the argument is kept as its priority), andHuChangePlayerGangpicks the gang's member asGang_PickNextPlayerdoes. Coney's stand-in: Coney has one player human, so instead of making the chosen human the player and leaving the old one in the world until its gang is deleted, the player's human takes the chosen one's place (spawned where it stands, drawn as its model, named by its handle, in its gang) and the chosen human leaves the world; the old handle names no one. The scripts' record of player 1 (CreatedHumans::player(1), which the play mode's thefts, pick-ups and muggings, the crew's chief and the teleports read) names the chosen human from then on (CreatedHumans::setPlayer). The end state after level 99'sGangDelete(0)(the hand-over) is the original's;tests/platform/disc_level99_handover_test.cppplays checkpoint 1 into 2 and steals the first stereo as the new player 1 (before, the theft named the old handle,HuIsAPlayersaid no and the lesson stalled).GangDeletedeletes the gang's members at once (AI); player 1, with no other human to go to, is only taken out of the gang.tests/platform/disc_level99_street_test.cppplays the end of the combat course into the street with the disc. - The model (
src/characters/character_class.h, from From a type to a model):characterClassOfisHuman_Init's switch,modelRecordTypethe player's plain-alias rule (a player made as a plain alias is drawn as the class's own type; a variant keeps its own), andmodelNameFortakes the model name from that type's recordedCfgCharcall (its tenth argument), with_aadded in levels 60 to 64;HuCreatekeeps it and the play mode loads it from the Character List. Disc check:level2checkpoint 3 is drawn aswarr_cl(Cleon),level3checkpoint 4 aswarr_sn(Snow),level99checkpoint 1 aswarr_re_cv. Coney's choice: a type with noCfgCharcall or a model the Character List lacks is drawn as Rembrandt, with a log line. - The scripts' hold on a human (character bindings): the flag word
+0xe0ishuman::flag(src/human/human_flags.h), kept by the fighter and across a respawn; combat reads the bits Combat lists,0x2gates the running climb and0x4000000keeps stamina full. A player starts with0x2,0x4,0x400,0x2000000and (Coney choice, for scenes with no level script)0x20000000000. The rest of what the scripts set (arrest, pad lock, disabled commands, money, carried item, callbacks, icon, held object, head-look, animation overrides) ishuman::ScriptState(src/human/script_state.h); the bindings aresrc/scripting/human_bindings.*, acting throughai::ScriptedHumans. Stand-ins: an arrested human only stands still; the icon, the object in the hand, the head-look andHuUseAnim's clips other than the idle's (slot 0, which plays: Rumble) are kept, not drawn or played;HuSetPushableis kept (bodies do not push each other);HuDropWeaponlets go of any object;HuChangePlayerGangandWCIssueCommandonly note the gang and command.LoadBumAnimsrequests or releases the bum animations' table, in order, asSetDynamicAnimationdoes each: the file listed twice is one entry, so twelve files are listed. - The hub's hold on a human (
level95;src/scripting/hub_bindings.*, acting throughai::ScriptedHub): money, death, character class and voice set are read; the scale, name, cuffs, muggable, pedestrian reaction, unarrestable, combat mode, gear and tackle block are set. Workouts (HuWorkout): the enter clip into a loop blend of the three workout clips, the effort pumped with cross and decaying byCfgWorkoutParams' tables, a repetition each wrap of the loop, the end on triangle or when worn down, then the exit clip (Workouts,human::WorkoutEffort). Stand-ins: another human's equipment is reached within 1.5 m; no dynamic clip plays; combat mode and the gear are kept, their context actions not built. - Names:
@orignames for addresses the research describes but does not name (such asHuman_SnapToGround,GaitBlend_Advance,PhysicsBody_PushOutOfWalls) are Coney's.
Notes for implementers¶
- Make the player first. For
level99, checkpoint 1: one human namedRembrandt, modelwarr_re_cv(found through the Character List), at(-284.4, 120.4, 0.3)heading 0°, snapped to the ground with a 2.5 m ray from 1 m above; pad 0; the follow camera targeting it (Camera). Any other level: its entry for the checkpoint in Level starts (checkpoint 1 by default, the first mode's flag in a Rumble arena,fWchiefStart_1in the hub), with that entry's character type. - Step at 30 Hz. Movement, turning and the animation step all use dt = 1/30 and per-update limits; a PC build that runs faster should keep a fixed 30 Hz step (or scale every limit) so speeds and turn rates match.
- Speeds per human come from the clips: for each locomotion slot, the clip's horizontal root displacement over its duration (Speed classes). For Rembrandt walk 1.63, jog 4.86, run 7.80, sprint 10.25 m/s.
- Gaits: a walk up to 0.95 stick magnitude (any amount above 0.12 gives the same walk), a run above it; the start clips (413, 414) first, then the gait clip; idle at once on release.
- Ground: no vertical velocity on the ground; snap the feet with a ray from 1.0 m above, 1.5 m long, each update;
a miss starts a fall with gravity 15.68 m/s² (from the second airborne update), capped at 50 m/s; land on floors
with
n.z> 0.65; fall damage from 14.9 m/s, a kill from 20.5 m/s (above). A fall or jump lands on the first update that starts with the feet about 0.17 m or more below the floor: keep moving the full step until then (feet up to 0.38 m under), then land at that update's position with the horizontal speed kept (When the landing happens). - Clips that move the body move it at the clip's root speed × the body scale from their first moving update, with no fade-in of that speed: the walk start 0.762 m/s on every one of its 12 moving updates, the run start 2.77, 2.49, 2.56, … and the run stop 2.24, 5.00, 5.10, … (Per update). A first update slower by 0.75 (the start clips' playback rate) or a stop that ramps from 0.31 m/s is a difference to remove.
- Walls: a sphere of radius 0.35 × scale² × body
+0x60(1.4286 for the player: 0.4705 m), centred that radius plus 0.05 above the feet, swept along the move; the slid velocity is kept, so a steep wall brakes the player; wall triangles less than 0.25 m tall are not walls, which is all the step-up there is (Walls and steps). - Sprint is L2 held with the stick above 0.95: cleared every update and set again while L2 is down and stamina is not 0. Stamina drains 20 per second at the sprint gait and refills 40 per second (Rembrandt: 135), not while running with L2 held (Sprint).
- Triangle tries a climb (stick above 0.12), then a context action, then a jump (stick above 0.95, faster than 3.3 m/s): 5.5 m/s up, the run or sprint speed forward, steering in the air (Jumping).
- Climbs are chosen by two forward rays at 0.69 and 1.7 m and a downward probe 0.4 m behind the face; the feet rise in one move at the second clip's start (Climbing).
- The stick is camera-relative before the human sees it: turn the stick by the camera's heading first, then apply the dead zone (0.12) and the run threshold (0.95) to its length.
- Turn with a limit and an ease, not instantly: in play 20° per update walking, 18° jogging and running, 16°
sprinting, eased below 2.0 rad (the values
config_preload2.luasets, Locomotion). - Characters are skinned with 32 bones (34 pose entries, Animation); every character on the disc has the same skeleton layout, so one bone mapping serves all.
- A disc test: every Character List record resolves to three resources that load, and every model is the clump described above (counts only).
The fifth and sixth missions' switches (2026-10-06): HuBlockJump sets flag 0x10000000, which makes
Human::tryJump refuse (a climb or a context action still takes triangle); HuSetNoReact sets 0x800, the same bit
the AI's block goal sets (Fighter::hitReactionsOff() now reads the flag word); HuSetAutoCombat keeps
0x200000000000 with no reader. HuApplyDamageModifier rewrites a human's attack damages from its class table × the
factor, rounded, then a player's Warrior class percentage (Human::applyDamageModifier; an entry that rounds to 0
keeps its damage). Coney's stand-ins: HuEnableTagCheer, HuSetInterrogation and HuUseAnyAnim are kept on the
human's ScriptState (no crew comment on a slip, no interrogation shake-down, no clip played; HuUseAnyAnim keeps
up to seven overrides apart from HuUseAnim's four and answers whether the file was requested); HuHasHat answers
NilHandle, as Coney's humans wear no hat objects; HuClearLook and KillHumans do nothing, as in the original.
CfgSetMaxThrowError keeps its two errors in radians (CharacterRules::maxThrowError); Coney's AI throws do not
read them yet.
Open questions¶
- The workout's reach and timing (answered): the scripts make the player his own target, so the reach always passes, and a repetition is each wrap of the loop blend's leading clip (Workouts).
- Where god mode (
0x10) is tested: Coney drops the damage and still plays the reaction. -
What the arrest (state
0x20000) plays and allows, and which human statesHuIsAlive's mask0x180050000names. -
Clip selection (answered): Clip selection, with the task system on Animation; the playback rate is never scaled with the speed. Still open: the anim states other than idle and move (11, 21, 24 and the combat ones) and the special idle ids.
- Jog: when a pad-controlled human jogs other than when carrying (a movement style, a script).
- What slots 16-24 and 28-34 are used for (16-24 are the attacks and the block, Combat) (their
default ids are known, Anim slots), and the
movement styles of
0x00253688beyond the ids they write. - The
+0x65cscale's source: what the division inHuman_Inittakes. - How the texture reaches the material, which names none (Character geometry).
- The rest rotations of pose bones 0-2 and why bone 3's parent in the table differs from its frame's.
- The vertex colour slot (zeros in every character checked): what the renderer does with it. The second texture-coordinate set is the blood layer's (Graphics).
- The default anim table at resource manager
+0x70, which answers the slots set to0xffffffff; Coney uses the generic data0x9da2e531, which matches every speed checked, but whether the table is that resource is open. - The idle threshold's getter (answered): the sneak-walk speed (Coney's implementation).
- The body's shape (answered): a capsule shape whose walls are a swept sphere (Walls and steps). Still
open: what the capsule's 1.886 (shape
+0x44) and the human's+0x4e8are used for. - The airborne anim state (answered): states 25-27 (Falling and landing); air control (Jumping). Whether locomotion turns the human while a start clip plays (answered: it does, Tasks).
- Step height at runtime (answered): steps up to 0.245 m are walked onto in one update, from 0.255 m they stop the body (Walls and steps), measured on a test step made in the mesh. Still open: a real kerb in a later level, as corroboration.
- The context action (
0x002811f0) and the object action that triangle also starts (partly answered, Triangle with something in hand): the object action is the drop of what is in hand; the context action is the context records and the pick-up search (Crimes). Doors and which objects are still open. - The air turn's gait (partly answered): 4° per update from a run and a sprint alike, which is the non-player
column's run value at
0x005101c4(inferred:Human_AirControlreads the other column); not read in the code. - Climb reaches (partly answered): Coney reads Rembrandt's type-8 vectors (Coney's implementation);
still open: which two clips
r1andr2belong to (Coney's reading: the standing and running first clips), and which clip's reachClimb_Start's "too far by less than twice the reach" swallow uses (Triangle with something in hand). Answered:CfgClimbWithGhettokeeps the ghetto blaster (held set 6) in hand through a climb; without it the climb drops it, and always drops a set-4 object (Climb_Start,0x00281c20, confirmed (code)). - The landing's threshold: why the landing contact is taken only once the feet start an update about 0.17 m
below the floor (When the landing happens). The contact path is traced (the resolution
0x0033d9d8acts on the first contact by fraction) and does not explain it; a runtime log of the contacts is needed. - The landing's fade and the gait blend value 436 hands over to; the drop land (429) and the long falls are not built in Coney.
- The climb's details (the first clip's timing answered: it runs during the move to the start point, with no
fade, Climbing): when record
+0x080x40is cleared; whether the re-probe needs a climbable triangle; how a fence climb's second clip moves the body (Coney: its root motion). - The camera during a fence climb (answered: the camera's rays exclude materials 30, 122 and 107, Camera).
- Triangle flags
0x4and0x80: why two climbable flags (one for players only), and which surfaces carry them. - The sprint's other clear (answered: the block, Combat). Still open: what
0x00230140, called when the sprint is set, does, and the fight test0x00224f28. - The rest of the human: the 0x180 record (the 0x2f0 record is the brain, AI), the state flags
tested by
0x002265f0/0x00226660, andHuman_MakePlayer's steps. - Level starts at runtime: the list is read from the scripts; a runtime check would confirm a few. For each of
level2checkpoint 1,level95chapter 1 andlevel102brawl, read player 1's transform (the table at0x00714b00, index+0x92of the human whose+0x1b0is 1) on the first frame of play, and again after the checkpoint script's start function has run (the hub's door walk, the arena's teleport). - Start functions that move the player later: the list notes only teleports made directly by a checkpoint's start
function (or one function down). Scene callbacks and chapter steps teleport the player too (99
Teleport/TeleportToFlagcalls onplayerin the story scripts in all); which of them run before the first frame the player controls is not traced. - Four Rumble arenas without a known mode script: their
level<N>_<mode>_init.luanames are not recovered, so they have no entry (Levels lists the modes found per arena). - A steady run's velocity in floats: the original's run measures at or above the run speed on 10 of 93 updates, Coney's on about 6 %; how its velocity is built (the order of the multiplies) is not traced.
- The run start's speed on its 7th and 11th updates in
run_circle(3.35 and 5.40 m/s against Coney's 3.25 and 5.54): which key the original samples there. - The trace's clip id in the gait blend (record
+0x20): 410 where Coney reports the blend's leading clip (411, 409) inrun_circle's steps 54-56; which clip of the blend the original stores. - The circling run's turn: on
run_circle's step 75 Coney turns 16.92° against the original's 16.44°, which the camera's auto-follow then grows to 6.6°; whether the stick is turned by the camera of this update or the last.