Sound events¶
Verified against: SLUS_212.15 (NTSC-U, SHA1 e9cb2cc49aa046b9e494313dce2f5038ed17b2f4): Ghidra, and the disc's
scripts read with coney-tools (the MATERIAL, SA and CfgChar values).
Purpose¶
Every place the game itself starts a sound during play, with its trigger, how the sound is chosen, where it plays and how loud: animation sounds, footsteps, hits and contacts, cars, objects, pickups, the interface, the speech the game says by itself, and the ambience and music of a level. The sound engine these calls go into (tasks, voices, 3D volume, the music player, the speech-line rules) is on Sound; the data on Audio data.
Original structure¶
The game starts its sounds from the subsystems that raise them (humans, combat, objects, cars, the HUD, the AI's speech), each through one of a few players over the sound matrix, below; each section of Behaviour names the function that plays its sound. The sound engine's own classes are on Sound.
The players¶
Gameplay sounds go through a few players over the sound matrix, filled by the matrix
script's NewMaterialSound / NewAnimSound calls. The matrix's tables are indexed by the script globals MATERIAL
(material ids 1-190, the same ids as the collision triangles' materials: 1 NONE, 2
GLASS, 5 CONCRETE, 8 SHOE, 9 FIST, 10 TORSO, 13 CAR_HOOD, 17 HEAD, 26 HUMAN, ...) and SA
(animation sound ids 0-161, below). A matrix entry has up to three columns, each a sound and a volume;
its alternatives play in turn. Every player below plays positional, duckable sounds (the play call's last
argument 1) at volume × the column's volume and pitch 1 unless stated. Confirmed (code):
| Player | Address | What it plays | Evidence |
|---|---|---|---|
Sound_PlayMaterialPair(vol, m1, m2, pos, default) |
0x00110830 |
entry [m1][m2]: column 2, then column 1 (column 1 at pitch Random_Unit() × 0.2 when m1 is 13 CAR_HOOD); no owner |
confirmed (code) |
Sound_PlayMaterialPairPlain |
0x00110940 |
the same without the CAR_HOOD pitch |
confirmed (code) |
Sound_PlayMaterialHit |
0x00110790 |
column 1 of [m1][m2] only |
confirmed (code) |
Sound_PlayAnimSound(vol, id, pos) |
0x00110a08 |
column 1 of animation entry id; no owner |
confirmed (code) |
Human_PlayAnimSound(h, id, pos) |
0x0021f548 |
animation entry id at the human, owned by him: not a player: column 1 at its volume; a player: column 1 and column 2 at 2 × volume × the combat factor (column 2 unowned), and column 3 the same while the camera is in combat framing |
confirmed (code) |
Human_PlayImpactSound(vol, h, owner, victim, m1, m2, default) |
0x00220ac8 |
entry [m1][m2] at h, owned by owner; with a victim knocked down, TORSO (10) and HEAD (17) on either side become 159 TORSO_PRONE; owner not a player: column 1; a player: column 1 and column 2 at 2 × vol × the owner's combat factor, and under combat framing also column 3 at a random pitch 1 ± n / 100, n in 0-19 (the audio generator 0x006eb8b0) |
confirmed (code) |
Human_PlayFootstep(vol, pitch, h, bodyMaterial) |
0x0021f290 |
footsteps | confirmed (code) |
Human_SayAnimLine(h, id, overLine, cut) |
0x0021f410 |
animation entry id's column 1 as the human's speech line (vocal ids) |
confirmed (code) |
A material lookup whose second material is 0 or 1 uses the caller's default (5, CONCRETE, at every call site on
this page), and an empty entry falls back to [m1][default] (Sound).
AudioManager_VolumeHook (0x00111650) is empty: its factor stays 1.
The combat factor (Human_GetSoundVolumeScale, 0x0021e3a8): game state +0x24c for a player while
Camera_IsCombatFraming (0x00233c50) holds, else 1. +0x24c is the first argument of CfgBreathingSound
(Cfg_SetBreathingSound, 0x0041d480); config_preload2.lua passes 1, so the factor is 1 in the shipped game.
Combat framing: one player, and player 1 either locked on with a target or in the fight stance
(Human_FightStanceMove) with a target while the camera mode 0x00510228 is above 1, and not in a grab
(+0xc4 empty). Confirmed (code).
Female voices. Human +0x3b8 is copied at creation (Human_Init, 0x002184f0) from the character class's
+0x14b, which is CfgChar's 13th argument (Cfg_SetCharacterClass, 0x00228af8). It is 1 for 61 of the 449
types, all of them women (char_lcgf, the Lizzies, hook_01 ...): the code reads it only to pick the _female
animation sound for a vocal id. Confirmed (code); "female" is inferred from the types and the ids' names.
Data¶
The tables an event reads (the sound matrix's material pairs, the vocal and speech-command ids, the sound classes of the disc's sound list) are described where an event uses them, in Behaviour below; their formats are on Audio data.
Behaviour¶
What starts each sound, kind by kind.
The ground under a human¶
Human +0x1d8 is the material of the level triangle under him: Human_SnapToGround (0x0023eab8, each update's
1.5 m ray down from 1 m above the feet, Characters) and the landing test (Human_LandingTest,
0x0023e408, a floor contact with n.z > 0.65) store the hit's material (WorldManager_RayCast result +0x28,
Collision). A cast that hits nothing leaves it unchanged; it starts at 0 (Human_Init), which the
lookup treats as the default, CONCRETE. Confirmed (code).
Animation sounds¶
A clip event of type 11 sends the human message 0x8b with the event's value (Sound);
Human_OnAnimSoundEvent (0x0021f700) acts on that SA id. "At the human" is the human's position in the transform
table (0x00714b00 + index × 0x20). Confirmed (code) for every row.
Footsteps and body falls¶
Everything in this section is confirmed (code) at the cited addresses unless a line says otherwise.
Human_PlayFootstep(vol, pitch, h, body) plays Sound_PlayMaterialPair of [body][ground], with ground the
ground material remapped first (Sound_RemapFootMaterial, 0x00110aa8: 18 TRAINRAIL → 106
GRAVEL, 6 ASHPHALT → 5 CONCRETE, 121 CARPET → 35 GRASS at half volume). The volume is vol × the remap's
factor × the human's combat factor × 2 for a player. For body material 8 (SHOE), a human hidden in shadow
(state 0x200000, Human_IsHiddenInShadow 0x00228168, the sneak) plays at half volume and without the
player's doubling; classes 0x77 and 0x78 use the plain pair (no difference for body 8).
SA id |
Name | Body material | Volume | Evidence |
|---|---|---|---|---|
| 1 | footstep |
8 SHOE |
1 | confirmed (code) |
| 3 | footstep_run |
8 SHOE |
1.25 | confirmed (code) |
| 12 | truekneedrop |
34 KNEE |
1 | confirmed (code) |
| 13 | hit_ground |
26 HUMAN |
1 | confirmed (code) |
| 18 | xhit_ground |
10 TORSO |
1 | confirmed (code) |
| 37 | jump_land |
164 FEET_LAND |
1 | confirmed (code) |
| 40 | light_hit_ground |
161 HUMAN_LIGHT |
1 | confirmed (code) |
| 43 | thrown_hit_ground |
162 HUMAN_HEAVY |
1 | confirmed (code) |
| 67 | light_land |
163 FEET_LIGHT |
1 | confirmed (code) |
| 69 | land |
101 FEET |
1 | confirmed (code) |
Fixed material pairs¶
SA id |
Name | Plays | Evidence |
|---|---|---|---|
| 46 | kick_to_body |
Sound_PlayMaterialPair([8 SHOE][159 TORSO_PRONE]) at the human, volume 1, a player 2 |
confirmed (code) |
| 65 | kick_to_head |
Sound_PlayMaterialPair([8 SHOE][17 HEAD]), volume 1, a player 2 |
confirmed (code) |
| 52 | bottle_smash |
by the held object's material (its type's +100): 102 BRICK → SA 141 brick_smash, 60 POOLBALL → 142 cueball_smash, 37 BOTTLE → 52 itself, through Human_PlayAnimSound; when that plays nothing (no entry), the object's material against itself: not a player Sound_PlayMaterialHit([m][m]) volume 1, a player Sound_PlayMaterialPair([m][m]) at 2 × the combat factor. Nothing without a held world object |
confirmed (code) |
| 86 | head_wtand |
the held object's material m against 17 HEAD: not a player Sound_PlayMaterialHit([m][17]), a player Sound_PlayMaterialPair([m][17]) at 2 × the combat factor |
confirmed (code) |
| 62 | stab |
Human_PlayAnimSound(62), or SA 140 bottle_stab when the held object's +0xc4 is hash 0xc68a017c (the broken bottle, inferred) |
confirmed (code); the bottle inferred |
Sounds without a speaker¶
SA ids |
Plays | Evidence |
|---|---|---|
4 short_fabric, 6 long_fabric, 15 fabric_friction |
Sound_PlayAnimSound at the human: a player at volume 2; anyone else at 1 only when his class +0x11b is 13 (Human_IsClass13 0x00223e20: the bosses and big fighters, CfgChar's third argument), else nothing |
confirmed (code) |
5 swoosh_big, 8 swoosh_sml, 16 boss_swoosh1, 23 swoosh_punch3, 44 weapon_swoosh, 90 boss_swoosh2, 91 boss_swoosh3, 92 boss_swoosh |
Sound_PlayAnimSound: volume 1, a player 2 |
confirmed (code) |
50 zoom_01 |
column 1 by PlaySound3DByHash at its column volume, no owner, no doubling |
confirmed (code) |
66 fence_rattle_big, 68 fence_rattle_small |
column 1 owned by the human at its column volume (AudioManager_PlayOwned), no doubling: the fence climb |
confirmed (code) |
83 sprayloop |
stops the human's sound at +0x168, plays column 1 owned by him and keeps it there (Human_SetSpeechHandle) |
confirmed (code) |
60 uncuff |
Human_StartGrabSound (0x0021b228): starts the sound prepared in +0x180 (Human_PrepareGrabSound 0x0021b148 prepares SA 60's column 1 at the grab partner, from Human_StateUpdate and ContextActions_Pick), or, when none was prepared, Human_PlayAnimSound(60) |
confirmed (code) |
87 stealth_hands, 88 stealth_baton, 89 stealth_knife |
while the human's sound at +0x17c is not alive: stop his line and play column 1 as a cutting speech line (Human_PlaySpeechCutting, volume 1), then Player_UpdateHiddenLoopSound (0x002301f0); otherwise Player_StartPreparedLine (0x0021f208) |
confirmed (code) |
0 none |
nothing | confirmed (code) |
| every other id | Human_PlayAnimSound (the default; listed explicitly for 2, 11, 26, 33, 74, 94, 118-128) |
confirmed (code) |
The spray loop's end¶
SA 83 sprayloop keeps its sound in the human's speech handle +0x168 (above), and nothing about the sound
itself ends it: the clip that starts it also stops it. Clip event 0x45 sends the human message 0xc5
(Anim_FireFrameEvents 0x00101dd8), whose handler stops the sound in +0x168 when it is still alive
(Sound_Stop) and resets the handle to the null handle (0x00598690, Human_SetSpeechHandle;
Human_HandleMessage 0x00245920). Confirmed (code). The other ways that sound ends: the next SA 83, 14 / 71
(0x8e / 0xc7) on the same human stop it before starting their own; a cinematic's start stops every human's held
sound (Scenes); Human_StopVoice (0x0021f058).
In Rembrandt's power move 59 (gen_grab_front_power10_p2, rate 0.75, Combat) the loop
starts at frame 14 and event 0x45 stops it at frame 27: 17 updates (0.57 s) of spray. Confirmed (runtime),
PCSX2 2.9.94, level99 lesson 6: +0x168 held a live handle from 59's update 19 to its update 36 and was empty from
then on.
Coney (#224, implemented): HumanSoundEvents keeps the loop as the human's held sound (m_held,
repo:src/audio/human_sound_events.cpp) and stops it on clip event 0x45 (HumanSound::Kind::StopHeld, from
Human::takeSounds), on the next held sound, on a cinematic's start (GameSound), when gameplay is left, and when the
human is removed (SoundHost::humanRemoved). Disc test (tests/platform/disc_level99_power_lesson_test.cpp): the
spray is held for 17 updates of 59 and no human holds a sound after it, and a one-shot interface cue (the flash's use,
cue 23) played during the spray, after it and in l99_c7 starts each time. The same test with the stops switched off
(a loop that runs on, as before) also let the cue start: one stuck loop takes a task and a voice, which the engine's
admission does not refuse a cue for, so it does not by itself explain the silence a user reported for the flash's use
sound or for a cutscene's audio.
Vocal ids¶
Human_SayAnimLine(h, id, overLine, cut) (0x0021f410) plays the entry's column 1 as the human's speech line
(Human_PlaySpeech 0x0021e400, or Human_PlaySpeechCutting 0x0021e698 when cut), at the column's volume, ×
2 for a player. Nothing when the human cannot speak (+0x199 clear) or the entry has no sound. While a line plays
(+0x178 alive) it plays only when overLine, and then stops that line first without force: a line that may
not be cut (+0x194 clear: every HuSpeakNI line and every Warrior order) is neither stopped nor talked over, so
the grunt is not heard and that line's script callback still runs when it ends; a cut-able line (HuSpeak's) is
replaced by the grunt and its callback is lost. Confirmed (code) and (runtime):
Cutting in on a line. As a speech line it follows Speech lines: nothing
during a cinematic. Where a female id is given, a human with +0x3b8 = 1 uses it instead.
SA id (female id) |
overLine | cut | Notes | Evidence |
|---|---|---|---|---|
7 whistle, 149 sleep_snore, 150 sleep_grunt, 156 puke_big, 161 puke |
yes | no | confirmed (code) | |
9 grunt_pch (109) |
yes | no | confirmed (code) | |
22 grunt (108) |
yes | no | confirmed (code) | |
59 grunt_pain → plays 22 grunt (108) |
yes | no | the pain grunt is the grunt entry |
confirmed (code) |
30 grunt_land, 54 grunt_strain, 72 grunt_hit_face |
yes | no | no female variant | confirmed (code) |
39 winded (111) |
yes | no | confirmed (code) | |
71 grab_back_grunt (112) |
yes | no | confirmed (code) | |
101 grab_grunt (110) |
yes | no | confirmed (code) | |
58 die (114) |
yes | yes | only while no scene plays (game state +0x410 clear) |
confirmed (code) |
115 expire (116) |
no | yes | only when no line is playing | confirmed (code) |
28 grunt_x (113) |
yes | no | only after command 12 pain said nothing (below) |
confirmed (code) |
32 grunt_lightlift (145) |
no | no | only after command 152 lightlift said nothing |
confirmed (code) |
57 grunt_heavylift (144) |
no | no | only after command 151 heavylift said nothing |
confirmed (code) |
Speech-command ids¶
Everything in this section is confirmed (code) at the cited addresses unless a line says otherwise.
These say a speech command through Human_SayCommand(vol, h, command, callback, interrupt,
uncut, target, duckable) (0x002205e0; uncut 0 plays it as a cutting line; it returns the line's handle). The
target for a look is the null handle (0x006ebd30) in every row. "Gesture" means only when Ambient_MayGesture
allows; "1.5 at a player" means volume 1.5 when the human's target is a player, else 1.
SA id |
Command (number) | Volume | Interrupt | Duckable | Condition | Evidence |
|---|---|---|---|---|---|---|
28 grunt_x |
pain (12) |
1.5 at a player | yes | yes | then the vocal line above when no line came | confirmed (code) |
29 cmd_mount |
mount (14) |
1.5 at a player | yes | yes | gesture; never for a player while game state +0x268 is positive |
confirmed (code) |
32 grunt_lightlift |
lightlift (152) |
1 | no | yes | then the vocal line | confirmed (code) |
35 cmd_wave, 97 cmd_point |
point (15) |
1.5 at a player | yes | yes | gesture | confirmed (code) |
41 agony |
agony (34) |
1 | no | no while the human has interrogation lines (+0x590-+0x59c, Crimes), else yes |
confirmed (code) | |
56 cmd_throw |
throw (10), or throw2 (38) when the held object's type (+0x86) is 8 TYPE_MOLOTOV |
1 (3, below) | yes | yes | only with a target (a human or a flag) and brain +0x2d4 clear |
confirmed (code) |
57 grunt_heavylift |
heavylift (151) |
1 | yes | yes | then the vocal line | confirmed (code) |
82 cmd_rage |
rage (160) |
1 | yes | yes | confirmed (code) | |
95 cmd_boss |
roar (167) |
1 | yes | yes | confirmed (code) | |
103 cmd_kiyap |
kiyap (176) |
1.5 at a player | yes | yes | gesture | confirmed (code) |
139 cmd_fire |
onfire (107) |
1 | yes | yes | only while his on-fire shout is on (human byte +0x19b, 1 at spawn, HuEnableOnFire); the byte is the shout's switch, not a burning state: its only readers are this event (0x00220044) and Human_StartBurning (0x002192d8), both gating command 107 |
confirmed (code) |
146 sprayface |
sprayface (203) |
1 | yes | yes | confirmed (code) |
cmd_throw in the level whose record +0x04 is 31 at checkpoint 6, for a gang member (brain +0x04 = 3): volume 3,
and with a target he cannot see (Human_HasLineOfSight 0x00222288), instead the line
vags/character/voices/<set>/warn/warn_o_01 of his voice set (+0x3b0) through Human_PlaySpeech at volume 4.
Ambient_MayGesture (0x00291ed0): true when a camera is within 30 m of the human and one of the first
n slots of his brain's reaction kind (two handles per kind at 0x006cddf8; n is the brain's help level) is free:
empty, or its human not speaking (+0x178 dead). The human then takes that slot. So at most two humans of a
reaction kind make these gesture lines at a time. Confirmed (code).
Crew lines while tagging¶
Everything in this section is confirmed (code) at the cited addresses unless a line says otherwise.
Two tag lines come from a crew member, not the tagger, through Tag_SayNearbyLine(tagger, command) (0x00273a68).
Confirmed (code):
| When | Command | Caller | Gate on the tagger |
|---|---|---|---|
| the cursor snaps back after more than 4 off-track updates (Crimes step 3) | 80 tagcheer |
TagGame_Update (0x002748a8) |
his +0x19c set; no random chance |
the stick game is finished (Tag_IsComplete) and was not stopped by the 300-cell cap (game +0x4fc = 0) |
83 tagdone |
Tag_UpdatePlayer (0x00273c60), not Tag_End |
none (+0x19c is not read) |
- Gate. Nothing unless the tagger is a war chief (
+0x3ac= 1) and his player's last Warrior command (game state0x0051489c+0x41a+ his+0x1b0) is 2, defend. - Candidates: the 16 member slots (gang
+0x48) of the tagger's gang (his brain+0x20c), in slot order. A slot counts when it holds a human who is not the gang's leader (Gang_GetLeader0x00165678, normally the tagger himself), not a player (+0x1b0= −1), not busy (Human_IsBusy0x00228258: busy means he has a record and either a state flag of0x1f80974000or record+0x108ahead of the game time), whose actions are not blocked (0x00228228: no state flag of0x7bf9e9f7ff0), whose brain has no queued actions (+0x2e≤ 0), and who is within the tagger's far melee range (his brain+0x140, 5.0 m unlessBrSetMeleeRangechanged it; AI) of the tagger, by the squared distance between their positions. - Pick: not the nearest. The first counting slot is taken; every later counting slot replaces it when
Random_Int(100)(stream0x006eb8a0) is 50 or more. So the last counting slot wins at 50 %, the one before at 25 %, and the first at 1/2^(n−1) of n. - Speak: only the picked member is then tested:
Ambient_MayGesture(above) and his own+0x19a(HuEnableTagDone) must both pass, or nobody speaks (no second choice). He says the command throughHuman_SayCommand(1.0, member, command, no callback, no interrupt, uncut 1, null target, duckable 1).
So +0x19c (HuEnableTagCheer, read on the tagger) gates only the cheer, while +0x19a (HuEnableTagDone, read on
the speaker) gates both lines. Human_Init (0x00218008) sets +0x198 to +0x19c all to 1 (one register holding 1,
stores at 0x0021810c-0x0021811c), so both are on for every human until a script clears them.
Hits and contacts¶
A strike on a human¶
Everything in this section is confirmed (code) at the cited addresses unless a line says otherwise.
A landed strike (Strike_Contact, 0x0021b290, Combat) on a human with health plays its hit
sound in Hit_ResolveBlock (0x00220df0): Human_PlayImpactSound(1, attacker, attacker, victim, strike, struck, 5),
nothing when the attacker's body +0x1a0 has +0x38 set. Confirmed (code):
strikeby the attacker's striking shape (its bone id+0x31, Combat) and the hit's strengths(0-3: the reaction's bits 4-5 fromHit_PickReaction0x00266d00, plus 1 when the attack's Anim Range flags (+0x0e,0x00254e20) have0x600or the attacker's anim id is 11, at most 3):- head (bones 5-16): 17
HEAD; - forearms and hands (17-27):
[111 JAB, 9 FIST, 112 BIGPUNCH, 112 BIGPUNCH][s](0x00510190); - shins and feet (28-33):
[8 SHOE, 8 SHOE, 113 BIGKICK, 113 BIGKICK][s](0x005101a0); - anything else: 0 (the default row).
A boss-class attacker (class
+0x11b13) striking withJAB,FISTorBIGPUNCHuses 183BOSS_FIST.
- head (bones 5-16): 17
struck: 108BAGwhen the victim's class is 460 (punchbag); else 184DEADwhen he is down or dead (Human_IsDownOrDead0x00227e60); else 127BLOCKwhen he blocks or ducks (or is a boss-class human with flag0x10of+0xe0); else 17HEADfor a boss-class victim; else by the shape struck: 17HEADfor the head (bones 5-16), 10TORSOotherwise (the spine, or no shape).
Before that, Strike_Contact adds Human_PlayImpactSound(1, attacker, attacker, victim, 26 HUMAN, 26 HUMAN, 5) when
the attacker holds flag 0x1400000 (a charge or a thrown human, Combat) or the attacker's body shape
has flag 0x2. A friendly hit by a player on a gang member (brain +0x04 = 3) who is idle and may gesture makes
him say fuck_you (88; interrupt, duckable) when his health is above 10 %.
A strike on the level, an object or a car¶
Strike_Contact, confirmed (code):
- The level mesh (no object):
Human_PlayImpactSound(116, h, h, 0, 9 FIST, triangle material, 5)(the volume is clamped to 1 by the engine), unless the attacker holds0x400800, is turn-limited (Human_IsTurnLimited40x00227f68,Human_IsTurnLimited60x00227f40), has state0x4000000, or his anim id is 2, 4 or0x1b2. Plus a particle burst. - A world object:
Human_PlayImpactSound(1, h, h, 0, 9 FIST, or 26 HUMAN when holding 0x1400000, the object type's material (+100), 5), then the object takes the hit (a glass pane breaks: World objects). - A car:
Human_PlayImpactSound(v, h, h, 0, 9 FIST, or 26 HUMAN when holding 0x1400000, 13 CAR_HOOD, 5)when the strike reached any part (the car's vtable+0x104mask is not 0), withv1, or 0.5 when the attacker is a player (whose doubling in the player makes it 1 again). A window it breaks adds its glass sound (Cars).
The body's contacts¶
Human_OnContact (0x00219d50), confirmed (code):
- A thrown human against the level: the player's throw (
Player_Throw0x0026dd08) arms the victim's+0x3bd; his first level contact that comes while he holds0x400800, is turn-limited 4, has state0x4000000or plays anim 2 or 4 (and+0x5b9is clear and he is not turn-limited 6) playsHuman_PlayImpactSound(v, h, h, 0, 26 HUMAN, triangle material, 5)withv1 when he holds0x400000, else 0.5, and disarms it. - A world object of type 29 (
TYPE_MOVINGVEHICLE): kills the human and playsSound_PlayMaterialPair(1, object material, 26 HUMAN)at him. - Type 34 (
TYPE_CHATTERBOXTRAIN): a third of his maximum health, once per object, and the same pair.
Cars¶
Confirmed (code):
- Part damage (
Car_OnHit,0x0038bea0): a part among 4, 5, 10-14, 16, 18 and 20 whose damage crosses 0.5 in this hit playsSound_PlayMaterialPair(1, 129 CAR_DAMAGE, 129 CAR_DAMAGE)at the hit point. - Windows: a part's first break (
Car_BreakWindow,0x0038a830) sends message0x3fto the shared car-damage particle system (Cars): kind 4 for windows 15, 17, 19 and 21, kind 6 for parts 6 and 7. Its glass shatter (SubGlass_Update,0x003e4cb8) playsSound_PlayMaterialPairAt(0x00117280, volume 1) at the burst, before any camera culling of the effect:[88 GLASS_SMALL][88 GLASS_SMALL]for a small burst or one whose record+0x0cis0x10001(kind 4, the windows),[2 GLASS][2 GLASS]otherwise (kind 6). - Explosion: a molotov's hit prepares
vags/fire/car_explodes<n>,nrandom 1-3, at the car (Sound_PrepareAtByName, kept at car+0x12fc), andCar_DoExplode(0x0038ab50) starts it. - The rock (shocks): a hit by a human or by an object a human holds (not a thrown one) starts the car rocking away
from the hit (
Car_StartHitRock,0x0038b1f8, the only caller isCar_OnHit; Cars: the rock; the rock itself is inferred from the fields it sets), strength 2 bare-handed, 4 for a human with flag0x400000and 3 for a held object, which playsvags/vehicles/shocks_squeak_0<n>(nrandom 1-3) at the car. It is skipped while the hitter's+0x568is set (for a held object, the parts this swing has hit; inferred: once per swing). So a window smash plays a squeak with the glass;0xe77006e7and0x90773671are_02and_03(the hashes). (An earlier reading put this in the explosion; the explosion does not call it.) - Loose parts (
Car_UpdateLoosePart,0x00387f18): a part's impact above 4 m/s playsSound_PlayMaterialPair(1 / (n + 1), 119 CAR_PART, triangle material)(nthe part's byte+0x97) and(…, 120 CAR_PART_STREAM, …); each that plays nothing is replaced by the same material against 5CONCRETE. Car_PlaySound(0x0038d6d8) is only scene event 71 on a car (Sound).
Objects¶
Everything in this section is confirmed (code) at the cited addresses unless a line says otherwise.
World objects play material pairs of their type's material (CfgObj, type record +100) through
Sound_PlayMaterialPair or Sound_PlayMaterialPairAt (0x00117280: volume 1, default 5), and fixed sounds by
hash through Sound_PlayAtDefault / Sound_PlayAtDefault2 (0x00117190, 0x00117238: positional, volume and
pitch 1, duckable). Glass panes, doors and barriers are on World objects. Confirmed (code):
- A dropped or thrown object hitting the level (
WorldObject_OnImpact,0x003939a8): the triangle's material is kept in the object's+0xf4;ncounts its impacts (+0x13c). A plain bounce plays only for the first three and above 2 m/s:[object][surface]at volume1 / (n + 1). A hit that breaks the object (its+0x10dor+0x10eis 0) plays[object][object]at 1 (0.1 for material 86CHAIRmoving straight down), then[object][surface]at0.5 / (n + 1)with default 116BRICKWALL, or, when that plays nothing, with default 5. A held or struck object that does not break plays[object][surface]with default 116, falling back to 5. The first sound'sfar(30 m without one) is the radius of the AI noise it makes. - An object touching another or a car (
WorldObject_OnContact,0x00394050):[other][object]at 1 and the object against 13CAR_HOODand 14CAR_HOOD_STREAMat 0.5 (at 1 when the contact has no other object). - A thrown object (
ThrownObject_OnContact0x00393538,ThrownObject_HitHumanTest0x003928d0,ThrownObject_HitHuman0x00392b88): against an object[thrown][struck]at 1 (0.5 for the second pair); against a human[object][26 HUMAN]at 0.5 or 1, or throughHuman_PlayImpactSoundat the human with 127BLOCKor 128WEAPON_BLOCKwhen he blocks (0.25-1). - Debris and rubble (
Rubble_Update0x003f4188,SubDebris_Update0x003c24e8,WoodSplinterBit_Update0x00407ae0):[piece][surface]at 1 when a piece lands.OverheadWeapon_Break(0x003ffe90): the weapon's material against itself. - Breakables by class (
Sound_PlayMaterialPairAt):DynCashreg,DynCbRadio,DynCtrlBox, the breakable doors (DynDoorBarBani,DynDoorBnstr,DynDoorFenceO,DynDoorParapet,DoorFence_OnHit),DynLizzies,DynMasks(with 88GLASS_SMALLpairs),DynTable,DynWoodbridge,DynBreakableLightand the stained glass (SubStainedGlass_Update0x003e5168, as car windows): their material against itself, or against 5, 35, 36, 106, 121 by the surface;WorldObject_HitEffectsByModel(0x003a6f68) against 106GRAVELand 103CASHREG. - Fixed sounds by hash: the molotov (
0x1be68950), the ride cart and train loops (0x326071de,0x26ac304b,PartTrainSound_Update's table), urine spray (0x1927f7ba), rotating objects (0x5adc8f1e,0xcd6754af,0x546e0515), broken neon and lights (0xf164dfe0), the CB radio and walkie-talkie tables (0x006f3190,0x005144e0), lock A (0xf4bad950,0xe108f888), the fire plume, generator and subway sparks tables (0x00514578,0x810e2b6f,0x005145d8,0x00514620), the motorbike gang (0x97116eb7), car steam (0xc1c45c2d),SubPla(0x813c858a), a swinging object (0x3fc9a583); by name: blood splattervags/character/blood_02(BloodSplatter_Update0x003aeb78), small firesvags/fire/fire_small_loop(SubFadeFlame_Init0x003c7de0), the level-51 busvags/cutscenes/l51/bus_idle_02(PartTruckSound_Update0x003fce60). Swinging and sliding doors play their record's hashes (Sound_PlayHashAt0x003a6ee8; the store door's first hit0xcf6586b2). - Trains and sparks.
PartTrainSound_Update(0x003db690) starts a sound only while its last one has ended and a camera is within 80 m: in the levels whose id is0x1for0x57, one ofvags/vehicles/train_pass1-5(0x005145f0) when a camera is within 10 m or nothing blocks the 100 m ray from the train to the player; in every other level one ofvags/vehicles/subway_squeak1-5(0x00514608) when a camera is within 40 m. It picks one of the five at random each time (Random_Int(4)is 0 to 4) and moves it with the train. The subway sparks (PartSSubwaySparks_OnMessage0x003db368, table0x005145d8) and eachsub_spark_effectburst (SubSparkEffect_Update0x003e1648, table0x00514620) play one of five entries at random, each entryvags/ambient/sparks/spark1orspark6(3:2 for the subway sparks, 2:3 for the bursts), banked insound,armies,galleryandlevel81. Confirmed (code) at the addresses cited; the names are confirmed (name hash): each is the CRC-32 of the name, whose last 16 characters stand before its record in the sound list's debug copy (Xbox assets). - The hashes, by name (confirmed (name hash): the CRC-32 of each name matches, and the tables hold them in this
order):
0x00514608holdssubway_squeak1-5as0xc2fff866,0x5bf6a9dc,0x2cf1994a,0xb2950ce9,0xc5923c7f; the sparks tables holdspark10x0c9417aaandspark60x92f08209;shocks_squeak_02and_03are0xe77006e7and0x90773671. These are the nine sounds of level99's checkpoint 2 that had no name; that its trains squeak rather than pass fits level99 not being level0x1for0x57(inferred).
Speech the game says¶
Everything in this section is confirmed (code) at the cited addresses unless a line says otherwise.
Besides the animation ids and the scripts' SoundPlayCommand, the game's own code says speech
commands through Human_SayCommand (0x002205e0, Saying a speech command) from 129 functions,
listed below with the commands (number and name, Speech), the volume and the flags: I
interrupt (cuts the speaker's current line; without it nothing is said while he speaks), C played as a cutting
line (Human_PlaySpeechCutting), N not duckable, L the speaker looks at a target. "Gesture" means only when
Ambient_MayGesture allows (above). Every call goes through the voice table, so a command the
speaker's voice set has no line for says nothing. The triggers are the functions' own (their pages: AI,
Combat, Crimes); a "variable" command is a parameter of the goal or action. Confirmed
(code) for every call's arguments.
| Address | Function | Commands (volume, flags) | Evidence |
|---|---|---|---|
0x002b6fa0 |
ArrestedGoal_Process |
within 4 m of a player, every 4 s, gesture: 126 unarrest_help from a gang member (brain 3), 72 scared from someone of a gang hostile to the arrested (1.5, -) |
confirmed (code) |
0x002af278 |
AttackTargetGoal_Process |
17 cheer2 (1, IC) |
confirmed (code) |
0x002af0b8 |
AttackTarget_Cleanup |
88 fuck_you (1, ICL) |
confirmed (code) |
0x002e3630 |
AvoidEnemiesGoal_Process |
variable (var, -) | confirmed (code) |
0x002e8858 |
BigBrawlerGoal_OnAttackWarning |
11 block or 14 mount, 50 % each (1.5, IC) |
confirmed (code) |
0x002e8bc8 |
BigBrawlerGoal_OnHit |
8 swear (1, -) |
confirmed (code) |
0x002e8f78 |
BigBrawlerGoal_Process |
87 kingohill (1.5, IC); 17 cheer2 (1.5, -C) |
confirmed (code) |
0x002ee7b0 |
BigLedgeThrowerGoal_Process |
87 kingohill (1.5, IC) |
confirmed (code) |
0x002ed718 |
BigThrowerGoal_Process |
17 cheer2 (1.5, IC) |
confirmed (code) |
0x0030bc68 |
BossRoofTactic_UpdateBanter |
144 cheer3 (1, IC) |
confirmed (code) |
0x0028ca50 |
Brain_OnFoeDown |
the one who downed him, gesture, not wounded: 143 whoop or 15 point, 50 % each (1, 2 for a player, IC) |
confirmed (code) |
0x002af928 |
Brain_Shout |
gesture, alternating: the brain's command (+0x2c, default 71 engage; 1.5 at a player), then for a cop brain 138 chase_cop (1, -) |
confirmed (code) |
0x0028ac80 |
Brain_SpotPlayers |
22 spot (1.5, ICL) |
confirmed (code) |
0x002accc8 |
BumLogicGoal_Process |
182 bum_hire (1, IC); 145 bum_beg (1, I) |
confirmed (code) |
0x002ac458 |
Bum_GiveItem |
148 kicked (1, IC) |
confirmed (code) |
0x002ac5e0 |
Bum_TakeMoney |
145 bum_beg (1, I); 147 bum_beg_resp (1, I); 2 follow (1, I) |
confirmed (code) |
0x002acaf8 |
Bum_UpdateRadar |
170 bum_beg_deal (1, I) |
confirmed (code) |
0x002d6638 |
CallGangGoal_Process |
169 surprise (1.5, IL); 22 spot (1.5, IC) |
confirmed (code) |
0x002a81f8 |
CallPolice_OnCrimeSeen |
9 scream (1, I) |
confirmed (code) |
0x002a8190 |
CallPolice_SayOnce |
127 catch_rat (1, I) |
confirmed (code) |
0x002b1860 |
ChaseGoal_Process |
23 search (1, -); 64 copresistco (1.5, I); 24 give_up (1.5, I) |
confirmed (code) |
0x002b2410 |
ChaseSupportGoal_Process |
23 search (1, -) |
confirmed (code) |
0x002ffb30 |
CivilianBrain_OnEvent |
205 help_girl (1.5, IC) |
confirmed (code) |
0x0030eec0 |
ConfrontTactic_Process |
variable (1, -) | confirmed (code) |
0x002c61f8 |
CopInteractGoal_Process |
192 shake_ho (1, I); 193 shake_ho_resp (1, I); 190 shake_bum (1, I); 191 shake_bum_resp (1, I) |
confirmed (code) |
0x002d4fe8 |
CowerGoal_Process |
9 scream (1, -L) |
confirmed (code) |
0x002c8af0 |
DealerFleeGoal_Process |
9 scream (1, I); variable (var, -) |
confirmed (code) |
0x002c89a8 |
DealerFleeGoal_Start |
9 scream (1, -) |
confirmed (code) |
0x002c7248 |
DealerGoal_Say |
variable (1, ?); 95 offer (1, IL) |
confirmed (code) |
0x002af670 |
EngageEnemyGoal_Start |
71 engage (1, -) |
confirmed (code) |
0x002678c8 |
Human_PlayGangDeathReaction |
13 near (1, IC) |
confirmed (code) |
0x00269b80 |
Hit_PlayBegReaction |
73 beg (1, -C) |
confirmed (code) |
0x00273a68 |
Tag_SayNearbyLine |
a crew member near a war-chief tagger says 80 tagcheer or 83 tagdone (1, -); Crew lines while tagging |
confirmed (code) |
0x00274ee0 |
Revive_Finish |
75 revive_thank (1, I) |
confirmed (code) |
0x002752a0 |
Revive_Start |
76 revive_reasure (1, I) |
confirmed (code) |
0x00283a30 |
Player_UpdateMugHold |
8 swear (1, I) |
confirmed (code) |
0x002cfb90 |
InvestigateGoal_End |
not chasing, gesture: 24 give_up, or 184 give_up_ne (1, -) |
confirmed (code) |
0x002d0100 |
InvestigateGoal_Process |
gesture: 183 ex_alert or 36 alert (1, I); 134 nosearch (1, I) |
confirmed (code) |
0x002d1100 |
Riot_OnAttacked |
17 cheer2 (1, IC) |
confirmed (code) |
0x002d11f8 |
Riot_AfterFight |
88 fuck_you (1, I) |
confirmed (code) |
0x002d2600 |
HarassGoal_Process |
88 fuck_you (1, -); variable (1, -) |
confirmed (code) |
0x002d42c0 |
PlaySpecialIdleGoal_Process |
variable (1, ICL); variable (1.5, ICL) | confirmed (code) |
0x002d5798 |
Hostile_Attack |
17 cheer2 (1.5, I) |
confirmed (code) |
0x002d58b8 |
HostileGoal_Process |
175 hostile (1.5, I) |
confirmed (code) |
0x002d8028 |
PathScout_SayLine |
23 search (1, -) |
confirmed (code) |
0x002db918 |
MoveToUseFlagGoal_End |
32 avoid (1, I) |
confirmed (code) |
0x002dd210 |
DestroyItemGoal_Process |
variable (1, -) | confirmed (code) |
0x002dde50 |
DestroyCarGoal_Process |
variable (1, -) | confirmed (code) |
0x002e63e0 |
ShopkeeperGoal_OnPlayerAction |
197 collect (1, I) |
confirmed (code) |
0x002ea6f0 |
BigFighterGoal_StartBreak |
34 agony (1.5, IC) |
confirmed (code) |
0x002eabc8 |
BigFighterGoal_Process |
variable (1.5, IC); variable (1, -C); 71 engage (1, -); 14 mount (1.5, -C); 8 swear (1, -); 143 whoop (1.5, IC); 8 swear (1.5, -C) |
confirmed (code) |
0x002ec780 |
BigFighterAGoal_Process |
variable (1.5, IC); 14 mount (1, -C); variable (1, -C); 71 engage (1, -); 8 swear (1, -) |
confirmed (code) |
0x002ee3d0 |
BigLedgeThrowerGoal_PlayTaunt |
variable (1, IC) | confirmed (code) |
0x002ee608 |
BigLedgeThrowerGoal_MaybeTaunt |
11 block (1, IC) |
confirmed (code) |
0x002f2438 |
ShooterGoal_OnHit |
12 pain (2, IC) |
confirmed (code) |
0x002f28b8 |
StationaryShooterAGoal_Process |
87 kingohill (1.5, IC) |
confirmed (code) |
0x002f4c68 |
StationaryShooterBGoal_Say |
variable (1.5, -C) | confirmed (code) |
0x002f52e8 |
StationaryShooterBGoal_Process |
87 kingohill (1.5, IC); 16 cheer1 (1, -); 16 cheer1 (1.5, -C) |
confirmed (code) |
0x002f5988 |
StationaryShooterBGoal_OnHurt |
40 fall (1, IC) |
confirmed (code) |
0x002f7348 |
BigBullGoal_Process |
8 swear (1, IC); 11 block (1, IC); variable (1, -C) |
confirmed (code) |
0x002f8468 |
HideAndSeekGoal_Process |
17 cheer2 (1, -); 71 engage (1, -); 143 whoop (1.5, -C); 15 point (1, -C) |
confirmed (code) |
0x002f8e70 |
LeftTurfGoal_Process |
71 engage (1, -L) |
confirmed (code) |
0x003069f0 |
Tactic_SayAckLine |
variable (1, -) | confirmed (code) |
0x0030d3b8 |
BossBirdieTactic_OnEvent |
34 agony (1.5, IC) |
confirmed (code) |
0x0031b768 |
ShadowTactic_Process |
135 shadow (1, -) |
confirmed (code) |
0x0031c840 |
TauntTactic_Process |
17 cheer2 (1, -) |
confirmed (code) |
0x0031e770 |
VandalizeTactic_OnObjectBroken |
143 whoop (1, I) |
confirmed (code) |
0x003200c8 |
WanderTactic_TryPickUp |
89 riot (1, -) |
confirmed (code) |
0x00322050 |
ChargeSubTactic_Process |
1 attack (1.5, IC) |
confirmed (code) |
0x00322510 |
GuardSubTactic_Process |
variable (1.5, IC); 1 attack (1.5, IC) |
confirmed (code) |
0x00322a40 |
LeaderSubTactic_Process |
1 attack (1.5, IC) |
confirmed (code) |
0x00323160 |
ThrowSubTactic_Process |
10 throw (1.5, IC) |
confirmed (code) |
0x002f9f78 |
FidgetAction_Start |
variable (var, -) | confirmed (code) |
0x002b3ab0 |
FightGoal_Process |
17 cheer2 (1, -C) |
confirmed (code) |
0x00416530 |
Flag_ActivitySound |
variable (1, I) | confirmed (code) |
0x002bd808 |
FollowAndDefendGoal_Process |
variable (1, -) | confirmed (code) |
0x0041c230 |
GameState_AddGuardingCop |
22 spot (1, IC) |
confirmed (code) |
0x00165d40 |
Gang_SaySpotLine |
variable (1.5, IC) | confirmed (code) |
0x002dacf8 |
GoalMoveToExitFlag_Process |
variable (1, I) | confirmed (code) |
0x002ab4b8 |
Goal_ProcessMove |
89 riot (1, -); variable (1, IL); 125 mumble (1, -) |
confirmed (code) |
0x002a83f8 |
Goal_ReportCrime |
33 rat (1.5, I); 129 hurryup (1, I); 84 phone_cop (1.5, I); 9 scream (1, I) |
confirmed (code) |
0x002c4360 |
Goal_ReportCrimeB |
22 spot (1, IL) |
confirmed (code) |
0x002bb858 |
GrabTargetGoal_Process |
9 scream (1.5, -) |
confirmed (code) |
0x0022c730 |
Grab_Link |
8 swear (var, -) |
confirmed (code) |
0x002c8ff8 |
HTLDefenseGoal_Process |
74 holdline (1, -) |
confirmed (code) |
0x002cc990 |
HideGoal_Process |
69 hide (1, -); 168 hide_stealth (1, -); 206 hide_response (1, -) |
confirmed (code) |
0x00265f70 |
Human_ApplyPendingDamage |
the attacker, after a hit that causes no reaction, when his anim is 9 or 0x4c, gesture: 60 % 17 cheer2 (11 block for anim 0x4c), else 8 swear (1, 1.5 when either is a player, -) |
confirmed (code) |
0x0022ec18 |
Human_Arrest |
25 arrested (1, I) |
confirmed (code) |
0x00269f30 |
Human_BlockHit |
11 block (var, -) |
confirmed (code) |
0x00245920 |
Human_HandleMessage |
34 agony (1, IC) |
confirmed (code) |
0x0021f700 |
Human_OnAnimSoundEvent |
12 pain (var, I); 152 lightlift (1, -); 34 agony (1, -N); 151 heavylift (1, I); variable (var, ?) |
confirmed (code) |
0x0021ed28 |
Human_SayCopLine |
variable (1, I) | confirmed (code) |
0x00233890 |
Human_SayGoalLine |
variable (1, I) | confirmed (code) |
0x002207d0 |
Human_SayObjectLine |
108 cb_wear (1, -); 110 hat_wear (1, -) |
confirmed (code) |
0x00219238 |
Human_StartBurning |
107 onfire (1, IC) |
confirmed (code) |
0x00238db0 |
Human_Tag |
37 nopaint (2, -) |
confirmed (code) |
0x002c69b0 |
IssueWarningGoal_Process |
32 avoid (1, IL); 36 alert (1, I) |
confirmed (code) |
0x00412dc8 |
Item_TakeOwned |
variable (1, -) | confirmed (code) |
0x002c5160 |
LeaveAreaGoal_Process |
46 cop1034f (1, IL) |
confirmed (code) |
0x002ae2e8 |
MeleeGoal_ProcessTarget |
71 engage (1, -) |
confirmed (code) |
0x002da588 |
MoveToFlagGoal_Process |
125 mumble (1, -) |
confirmed (code) |
0x002c1858 |
PatrolGoal_Process |
32 avoid (1, IL); 125 mumble (1, -) |
confirmed (code) |
0x002a9508 |
PedInteractGoal_Process |
variable (1, I); 179 question (1, I); 180 answer (1, I); 181 bye (1, I) |
confirmed (code) |
0x002aa760 |
PedReactionGoal_Process |
9 scream (1, -); 9 scream (1, I) |
confirmed (code) |
0x002aa598 |
PedReactionGoal_Start |
9 scream (1, -); variable (1, I) |
confirmed (code) |
0x002ad6c0 |
PeddlerGoal_Process |
variable (1, I) | confirmed (code) |
0x002fb8a0 |
PlaySoundAction_Update |
variable (var, I?) | confirmed (code) |
0x00303468 |
PlayerBrain_OnPrompt |
146 give_me (1, -) |
confirmed (code) |
0x002856b8 |
Player_UpdateMugging |
39 mugcop (1, IC); 19 mug (1, IC); 31 no_item (1, IC); variable (1, I); variable (1, IC) |
confirmed (code) |
0x00318130 |
PursueTactic_EndSearch |
24 give_up (1, I) |
confirmed (code) |
0x001f0420 |
RM_ChooseGangs_Render |
17 cheer2 (20, -) |
confirmed (code) |
0x002d1c38 |
RiotGoal_Process |
89 riot (1, I) |
confirmed (code) |
0x002d1288 |
RiotGoal_TryPickFight |
17 cheer2 (1, IC) |
confirmed (code) |
0x002e5fa0 |
ShopkeeperGoal_OnDisturbed |
188 cower (1, I) |
confirmed (code) |
0x002e6668 |
ShopkeeperGoal_Process |
185 store_greet (1, I); 189 dead_meat (1, I); 137 mug_grunt (1, I); 187 phone_gang (1, I); 186 store_chat (1, I); variable (1, I) |
confirmed (code) |
0x001140d8 |
Sound_PlayHumanCommand |
variable (1, ?NL) | confirmed (code) |
0x0021b290 |
Strike_Contact |
88 fuck_you (1, I) |
confirmed (code) |
0x002fb128 |
TauntAction_Start |
variable (var, -) | confirmed (code) |
0x002e7f50 |
TiredGoal_Process |
150 energy (1.5, IC); 149 tired (1.5, IC); 8 swear (1, -) |
confirmed (code) |
0x002e7c18 |
TiredGoal_StartBreak |
34 agony (1.5, IC) |
confirmed (code) |
0x002606e8 |
Uncuff_MashSuccess |
67 unarrest_thank (1, I) |
confirmed (code) |
0x00260ca8 |
Uncuff_Start |
68 unarrest_reasure (1, I) |
confirmed (code) |
0x002fb738 |
UsePhoneAction_Update |
variable (1, I) | confirmed (code) |
0x0041c338 |
WarChief_CheckCrewInRange |
variable (1, -) | confirmed (code) |
0x0041cc40 |
WarChief_SayCommand |
variable (1.5, IC); 3 defend (1.5, IC); 158 scatter (1.5, IC) |
confirmed (code) |
0x00306040 |
WarriorBrain_OnPrompt |
146 give_me (1, -) |
confirmed (code) |
0x003052f0 |
WarriorBrain_Think |
109 cb_whine (1.5, ICL) |
confirmed (code) |
0x002bf118 |
WarriorVandalStealGoal_TryVandalise |
variable (1, -) | confirmed (code) |
0x002b52c0 |
WoundedGoal_Process |
8 swear (0.75, I) |
confirmed (code) |
Lines played by name or by voice-table hash, not by command, confirmed (code):
- Gang warning barks and the spot line: Sound,
Gang_SaySpotLine(0x00165d40). - Banter (overheard conversations of gangs under the HanginOut, Idle, MoveToFlag, TravelPath, UseFlag and Wander
tactics, AI):
Human_SayStateResponse(h, line, near, far)(0x002208f0) takes the line of the speaker's state-response voice set (+0x3b4) forstatement(20) orresponse(21), the line number being the gang's counter+0x60c, moved on after each response by the responder's line count (Gang_CycleSpotLine). With the listener nearer (human+0x334) than that line'sfar− 20 m and no other near banter line alive (one game-wide, handle0x0065ff40) it plays at volume 1; otherwise, with no far banter line alive (0x0065ff48), thestatement2/response2line (77, 78), or the near line at 0.5 when there is none, plays when the listener is withinfar+ 10 m. Both throughHuman_PlaySpeech, after stopping the speaker's line. - Bums at a flag (
PedFlagGoal_Process,0x002a68b0, flag activity 6, at a random interval):vags/character/bums/bum_bum1_00<n>orbum_bum2_00<n>,nrandom 0-5, volume 1. - Boss lines:
BossBirdieTactic_Process(0x0030d208: command 17's line of one of three numbers, volume 4),InfoTactic_OnLeaderAnimStart(0x00315820),InfoTactic_Process(0x00315928),BossTactic_OnEvent(0x00309058),BigBrawlerGoal_Process,StationaryShooterGoal_Say(0x002f0930) andShooterGoal_Say(0x002f2230): fixed lines of those fights. - Idle dialogue (
Human_PlayIdleDialogLine,0x00220430, fromWarriorFollowTactic_Process): the human's next line of a name built from his voice set, the level's number, the checkpoint and his counter+0x196(up to+0x197lines, wrapping to 1). The lines live invags/character/voices/<voice>/asl<level>_c<checkpoint>_idle_<NN>. At spawnHuman_Init(0x002184ec-0x00218560) looks up lines 01, 02 and 03 for the current level and checkpoint (Sound_FindByName,0x00110d78):+0x197starts as the number found (0-3) and+0x196as 1 when any is found;HuSetIdleDialogMaxIteroverrides the count. Confirmed (code). - Scene and clip events 12 and 70 (human messages
0x8c,0xc6): a speech line by hash at volume 1; 14 and 71 (0x8e,0xc7): a positional sound by hash at the human, replacing the sound in his+0x168; 69 (0xc5) stops that sound (Human_HandleMessage,0x00245920).
Pick-ups, items and the interface¶
Confirmed (code). 2D sounds are PlaySound2DByHash / ByName with flags 0, 0 and duckable 1; interface cues are
Sound_PlayInterfaceCue (2D, flags 0x12) by number from the SoundCfgInterfaceSound table
(Sound and music).
| Event | Sound | Where | Evidence |
|---|---|---|---|
| A pick-up gives an inventory item (key, flash, spray can, money, loot, cuffs ...) | the item's pick-up sound (item +0x24, Inventory), 2D |
Human_PickUpObject 0x0023bf00 |
confirmed (code) |
| Taking from a weapon pile (types 17-23, 47) | cues 25-31 by pile type (17 BRICKPILE 25 ... 23 POOLBALLPILE 31; 47 BEERPILEHEAVY 27) at the object, positional |
human message 3, Human_HandleMessage |
confirmed (code) |
| Taking from a spray pile (type 44) | item 3's pick-up sound, 2D | the same | confirmed (code) |
| Mugging: the money taken, the pocket handed over | item 2's sound; the pocket item's sound, or item 3's or item 1's | Player_UpdateMugging 0x002856b8, Mug_HandOverPocket 0x002334d0 (Crimes) |
confirmed (code) |
| The mugging camera starts, ends | vags/misc/mug_intro (handle kept), then vags/misc/mug_outro after stopping it |
CamMug_Start 0x00137e08, CamMug_Update 0x00138b90 |
confirmed (code) |
| Buying from a dealer | vags/interface/powerup |
DealerGoal_OnBuy 0x002c74d8 |
confirmed (code) |
| Using a flash; a revive completed | cue 23 | Flash_Use 0x00284280, Revive_Finish 0x00274ee0 |
confirmed (code) |
| Triangle on a context action (lock, key) | cue 24 | ContextAction_Use 0x0024d530, Player_TriangleAction 0x002811f0, Player_UseItemCommand 0x002843f8 |
confirmed (code) |
| The car-stereo theft's turns, done | cue 34 per turn, cue 35 at the fourth | Theft_UpdateStereo 0x0027e908 |
confirmed (code) |
| The lock-pick dial | vags/misc/lock_spins_01 (shown), vags/misc/click_01 (judged) |
LockPickDial_Show 0x001b8428, LockPickDial_Judge 0x001b8d38 |
confirmed (code) |
| Rage full, rage starts, rage ends | vags/interface/rage_indicator_02 once when the meter reaches its maximum; vags/misc/rage_mode_06; vags/misc/rage_ends_01 |
PlayerHUD_Update 0x00214138, GameState_PlayRageSound 0x004194b8, RageSound_PlayEnd 0x00419400 |
confirmed (code) |
| Wanted level, money count, objective, announcement, hint | cues 2, 16, 17, 20, 21 | HUD_SetWanted, HudMoney_Update, HUD_SetObjective, HUD_ShowAnnouncement, HintBox_Update (HUD) |
confirmed (code) |
| A Warrior command chosen on the stick | cue 32 | WarCommandDisplay_ReadStick 0x001a7040 |
confirmed (code) |
| The stopwatch's last seconds | vags/interface/menu/menu_enter3, once a second |
StopWatch_Update 0x004233f8 |
confirmed (code) |
| The "go" sign | vags/misc/bleep27 while it shows, one at a time, not while paused |
GoSign_Render 0x0019ed28 |
confirmed (code) |
| A counter panel's bonus | vags/interface/menu/bonuspart_01 |
CounterPanels_SetValue 0x001c3280 |
confirmed (code) |
| The angry breathing | CfgBreathingSound's sound, 2D, its handle kept |
Breathing_Start 0x00419030 |
confirmed (code) |
The pick-up sound's lifetime¶
The sounds. config_preload2.lua gives the money, the flash, the spray can and the other power-ups the pick-up
sound vags/interface/powerup and the mission items vags/interface/mission_item (Inventory
items). From the disc (coney-tools audio list sounds, and the bank samples' frame flags
read by a scratch script; counts and flags only), corroborated against the code's loop test (Sound_IsLooping
0x0010f2c8, class flag 0x01):
| Sound | Hash | Kind | Class, flags | Priority | Volume | Rate | Size | ADPCM frame flags |
|---|---|---|---|---|---|---|---|---|
vags/interface/powerup |
0x7b3534e9 |
bank sample (in sound, armies, level3, level81, spookorama and four more banks, all alike) |
4, 0x00: not looping, not positional |
12 | 69 % | 18,000 Hz | 3,760 bytes, 235 frames | frame 232 0x01 (end), frame 233 0x07, the rest 0 |
vags/interface/mission_item |
0x814828dd |
bank sample (five banks) | 4, 0x00 |
12 | 69 % | 15,000 Hz | 1,712 bytes, 107 frames | frame 104 0x01, frame 105 0x07 |
So neither sound loops by its class, and each sample is a standard one-shot: the frame flagged 0x01 alone (end,
without repeat) ends the voice there, and the frame after it, flagged 0x07 (loop start, repeat, end), is the usual
silent padding frame that loops on itself if a voice ever reaches it (inferred from the SPU2's ADPCM flags; Coney's
decodeAdpcm(..., stopAtEnd = true) stops at the first end flag, as it should).
How the original plays and releases it, confirmed (code):
Human_PickUpObject(0x0023bf00; each item's call, e.g. at0x0023c174) plays the item's sound withPlaySound2DByHash(mgr, hash, 0, 0, duckable 1)(0x0010fcd8): no position, the default (null) owner, and the returned handle is discarded (the next instruction reloads its registers): nobody can stop it, and nothing has to. The dealer's and the mugging's plays (table above) are 2D as well; whether they keep the handle was not checked.AudioManager_Playstarts the bank sample on a free SPU2 voice (13-47) or a victim's (Starting a sound); being class0x00, no loop is requested.- Each update
Tasks_Update(0x00112b10) frees a real task when its state is 2 or 3 or when the device reports its voice idle (AudioDevice_VoiceState, slot+0x108,0x0014d168→MS_GetVoiceState), which happens when the SPU2 reaches the end flag. A virtual task (no voice) is freed when its length, 0 whole seconds for these (AudioDevice_Durationrounds down), has passed, so at once.
So the original's pick-up sound ends on its own after about 0.37 s (powerup: 232 frames × 28 samples ÷ 18,000 Hz)
or 0.2 s (mission_item), whatever happens to the picked-up object; it is not tied to the object, the human or the
pause.
User item 22 (the pick-up sound looping until paused): nothing in the data or the original's code loops these
sounds, so the loop is Coney's. Coney decodes the sample to its end flag and gives a bank sample loop points only
for a looping class (repo:src/audio/sound_bank.cpp), so the data is not the cause either. The sound that loops
is the one heard with every money pick-up after it: the HUD's money count (below). The list
that follows was the plan before that was found; its step 1 is what found it. Coney fixes:
- Reproduce with
--event-logand look at thesoundevents: many0x7b3534e9plays (one a step) mean the pick-up is re-triggered (a pick-up, purchase or mugging path firing every update, or a record not removed), not a loop; one play means a voice that never ends. - For the one-play case, check that a mixer voice playing a
PcmSoundwith no loop points ends at its last frame and thatSoundEngine::updatefrees its task (!m_mixer.isPlaying(...)), and that a voice reused after a steal, or a looping sound's voice handle reused by the pick-up, keeps neither the old loop points nor the old task (the symptom fits a looping voice that lost its owner: pausing stops every voice, so it ends there). - Test: play
0x7b3534e9throughGameSound::play2Don the offline device and step past 0.4 s: the voice has ended and the task is free; with every voice busy (a steal), the same holds; a looping class sound stopped by handle while a pick-up starts does not leave either playing.
The money count's sound¶
The sound loops. Interface cue 16 (0x10) is vags/misc/count_money2 (hash 0x6c7f4d7d; Sound and
music), a bank sample of class 3 with class flags 0x01: looping,
2D, priority 12, volume 60 %, 22,050 Hz, 688 bytes (from the disc's sound list, coney-tools audio list sounds;
counts and flags only). A voice playing it repeats until something stops it.
How the original keeps and stops it, confirmed (code) in HudMoney_Update (0x001becb0), once a frame per
player panel (HUD: score and money):
- The panel keeps the sound's handle at
+0x50; the "no sound" value is the global at0x00598690(stored back at0x001beff8). - While the shown money counts toward the inventory's (the shown value
+0x44differs from the value read, and no±$Nchange is being held,+0x4c= 0): the kept handle is resolved (SoundHandle_Resolve,0x0010f578, called at0x001bef6c); only when it names no live sound is cue0x10played again (Sound_PlayInterfaceCue,0x0010fc30, at0x001bef84) and the new handle kept. So one looping voice plays for the whole count, and it is started again if it was lost (refused, stolen, or stopped) while the count goes on. - Otherwise (the count has reached the value, or a change is being held, below): when a handle is kept, the
sound is stopped at once (
Sound_Stop(mgr, handle, 0),0x00110318, at0x001befec) and the handle reset. - The hold. A new money value is first shown as the
±$Nline for 1,000 ms (+0x4c= the clock+0x48of0x0050b734plus 1,000) while the shown count stays put; only when that time has passed is the new value taken (+0x48) and the count, with its sound, starts (0x001bed0c-0x001bed34). The first change after the panel starts (+0xa20still 0) is taken at once. - While it counts, the function also zeroes word
+0x411cof the player's record (0x00600848[player], at0x001bef70); what that word is was not looked at.
So the count's sound lasts exactly as long as the count (after the hold) and never outlives it.
Watched running, confirmed (runtime) on 2026-10-21 in PCSX2 (level99 checkpoint 1, player 0's panel at
0x00619d70): with the shown money +0x44 set to 200 over 0 in the inventory, the play at 0x001bef84 was hit once,
on the count's first frame; the handle it returned stayed in +0x50 through all 35 shown values from 200 to 0
(about 1.2 s, many times the sample's length, so the voice was still live and looping); Sound_Stop at 0x001befec
was hit once, on the first frame after the count reached 0, and +0x50 went back to 0xffffffff.
In Coney before the fix, the HUD played cue 16 once as a count started, as a fire-and-forget sound (PlayerPanel,
repo:src/hud/player_panel.cpp, "Coney's choice"). Being a looping class, its voice never ended: each money
pick-up, mugging or purchase left one more count_money2 voice looping until a bank change (the main menu) stopped
it. That is user item 22, reproduced in a disc test (repo:tests/platform/disc_level99_money_sound_test.cpp: a
$50 pick-up walked into on level99 left the sound live 5 s after the count). Coney fix, done: the panel keeps
the handle, starts the cue on a counting frame when the kept sound is not playing, and stops it on the first frame
without counting. The 1,000 ms hold before a count is a separate difference (Coney counts at once and shows the line
meanwhile).
World one-shots¶
- Wind gusts (
WindManager_Update,0x00407318): a gust starts at random (one chance in0x00514794× 60 per update); during itvags/ambient/environment/wind_blows_<NN>(NN01-10 in turn) plays 2D when no player stands on covered ground, the garbage is on and not paused, the level is not 51 at checkpoint 8, the last gust sound has ended and 20 s have passed since it started. Confirmed (code). - Doors, glass, radios, props and other objects: World objects, Radios.
Covered ground¶
Human +0x5b7 is set while a player stands on a triangle with flag bit 5 (0x20; Human_SnapToGround through
Human_SetCoverFlag5 0x002195e0, which also pauses the wind-blown garbage). It decides the ambient emitters'
listener filter (0: only players not on covered ground hear the emitter; 1: only players on it), the × 0.25 owner
duck of Task update and the wind gusts. Confirmed (code); that bit 5 marks indoor ground is
inferred (level95's filter-1 emitters are room sounds such as tMusic_01, and the garbage stops).
level95: music, ambience and conversations¶
From level95.lua, level95_coney.lua and level95_clubhouse.lua, read with coney-tools (the bindings:
Sound bindings). Confirmed (code) for the scripts' calls:
- Matrix and bank:
SndLoadMatrix('sound')inMain; the level bank issound(Banks). - Music outside:
SetupConeyEnvironment(run when the player leaves the clubhouse by a door) sets the system music's moods (SoundSetMusicTrack: 0music/ambient_idle1andmusic/2moog1a_loop, 1music/120f_bit_biz_1, 2music/2nd_trialloop_120b) and turns it on (SoundEnableSystemMusic(true));level95_coney.luasets the same. So outside the music is the system music: a random track of the current mood, looping. - Music inside:
SetupClubhouseEnvironmentturns the system music off; the music heard indoors is thetMusic_01/tMusic_02loop emitters (filter 1, name kindmusic, so ducked to 0.5 while a music channel plays). Opening the mission or subway menu playsMenuTracklooping at music volume 0.5 (the ambient track stopped); closing it stops the music and restores volume 1. - The ambient track outside (
level95_coney.lua'ssoundbox): volume boxes callsoundbox.Enter(message 3: stop the ambient track,CfgSetOutdoorMode(false)),soundbox.Exit(message 4: play the last outdoor track again, outdoor mode on) and, for outdoor boxes,soundbox.EnterOutdoor(play that box's track). The defaults arevags/ambient/city/distant_traffic_loopoutside andvags/ambient/city/inside_room1_loopin rooms. Its setup also turns the effects on (SoundEnableEffects(true),SetNormalEffects). - Ambient emitters:
AddAmbientSoundEmitters(fromRegisterObjects) adds 66 emitters withAddAmbientSoundEmitter2(41 of mode 4, one-shots; 24 of mode 3, loops; one of mode 1; listener filter 1, heard only on covered ground, fortElectric01,tElectric02,tMusic_01andtMusic_02, 0 for the rest) andSetAmbientEmitterPositionsfortDogs01,tDogs02,tENVIRO_02andtENVIRO_03;t_pinball_machine(filter 1) is added and switched byEnableAmbientEmitter(events.PinballSound, ...)as Warriors play pinball. Every emitter follows Ambient emitters: range, plays, delays, mode and filter. - The clubhouse conversations are scripted (
events): for each cluster of Warriors with enough talkers,events.ChatEventruns after a random 1-5 s; it picks a Warrior who saysSoundPlayCommand(h, 20 statement, 'events.FinishChatEvent', false, ...)(no interrupt) while the other looks at him, then the other says 21responsewith the same callback, which queues the next chat. At the pinball machine a random one of three says 143whoopor 202pinball; at the workout cluster 201workout. The lines come from the Warriors' voice sets (type 193,warr_sw, itsCfgCharvoice). - Outside, the street humans' own speech: gang banter (above), the civilians' and cops' goal lines (the table
above:
PedInteractGoal179question, 180answer, 181bye, crime reports, cop shakedowns, bums) and the dealers.
Coney's implementation¶
Coney plays these events in repo:src/audio/ (the engine and its players) and in the subsystems that raise them;
the sections above say where Coney differs, such as the pick-up sound and
the money count's sound.
Open questions¶
- What the attacker's body
+0x38is (it silences his hits).