The in-game HUD¶
Verified against: SLUS_212.15 (NTSC-U, SHA1 e9cb2cc49aa046b9e494313dce2f5038ed17b2f4). Runtime claims were made in
PCSX2 2.9.94 (2026-10-06) on a copy of quick-save slot 1 (level99, checkpoint 3), reading the HUD over PINE, writing
the player's rage, health and the panel's force-show flag, and measuring PCSX2's screenshots (1240 × 930 for the
game's 640 × 448); they say so.
This page was split because it had grown too large: the objective and hint messages, the action prompts and the instruction arrow are on HUD messages; the radar, the target panel and the health rings on HUD radar, target panel and health rings; the function index is in three pages by source file (widgets and the HUD interface, the HUDLua bindings and the message, mission select and panel files). Every moved heading keeps a pointer here under its old anchor.
Purpose¶
What the HUD puts on screen during play, where, in which colours, and what drives each part: the player panel (name banner, rage meter, score, money and item counters), the hint box, objective and announcement messages, the counter panels, the instruction arrow, the radar's place on screen, the crew status list (The crew status list), and the calls that show and hide it all. The sprite and text machinery underneath (widgets, sprite sheets, fonts, markup, draw order) is on GUI; the radar's blips and icons are on GUI: the radar; the script bindings are in the HUD bindings.
In short: health is not on the screen's edge but on the ground: two flat rings at the player's feet, the outer one showing health and the inner one power (or rage while raging), shown on SELECT, in a fight stance, at low health and after a flash (The health rings); the player's current target gets a health ring too. The player panel at the top left is the character's name banner, tinted with the rage colour, a thin rage meter under it, the score and the money, with up to four item counters (flash, spray paint, handcuffs, keys) beside the money. It shows when something on it changes (or SELECT is pressed), stays 2 s and fades out over 1 s. Hints sit in a dark box at the bottom left; objective and announcement messages appear at the same place and hide the hint box while they show. The radar is a disc of the level's map at the bottom right; there is no full-screen map. START opens the pause menu (Pause menu).
Original structure¶
Everything in this section is confirmed (code) at the cited addresses unless a line says otherwise.
GUI/HUDInterface.cpp (0x001ad588 to its stub 0x001b3ea8), GUI/HUDLua.cpp after it (the bindings'
callees, to 0x001b8f78), GUI/TutorialHUD.cpp (the hint box), GUI/ScrollInHUD.cpp (queued messages),
GUI/RadarHUD.cpp, GUI/ChecklistMessageHUD.cpp; the player panel class lives at 0x00211ca0-0x00214c28, after
the profile-manager screens, in a file not yet named (Source map). Names are ours.
| Address | Name | Role | Evidence |
|---|---|---|---|
0x00600840 |
the HUD object | one static object, about 0x22600 bytes |
confirmed (code) |
0x001acee0 |
HUD::HUD |
constructor: every part below; +0x08/+0x0c point at the two player panels |
confirmed (code) |
0x001af010 |
HUD_Update(hud, a, b) |
per frame from mode 1 (a and b go to the two radars' updates) (Level loading) |
confirmed (code) |
0x001b1688 |
HUD_Render(hud) |
from the overlay pass 0x00156658 (Boot) |
confirmed (code) |
0x001b1f38 / 0x001b20f8 |
HUD_Hide / HUD_Restore |
HideHud / RestoreHud |
confirmed (code) |
0x001b2030 / 0x001b2088 |
hide / show both player panels | HidePlayerHud / ShowPlayerHud |
confirmed (code) |
0x001b2200 |
HUD_AttachPlayer(hud, kind, slot) |
binds a player panel to a human (from Human_MakePlayer) |
confirmed (code) |
0x001b2610 / 0x001b2658 |
radar on / off for one player | confirmed (code) | |
0x00211ca0 |
PlayerHUD::PlayerHUD |
the player panel (0x4a50 bytes; interface vtable 0x0053edf8 at +0x4120) |
confirmed (code) |
0x00212840 |
PlayerHUD_Init(panel, player) |
builds the panel from the layout table | confirmed (code) |
0x00214138 |
PlayerHUD_Update |
values, colours and positions each frame | confirmed (code) |
0x00213290 |
PlayerHUD_Render |
fade, rage flashing, draws the parts | confirmed (code) |
0x0020dc98 |
PlayerHUD_SetBanner(panel, kind) |
picks the name banner | confirmed (code) |
0x00211ef8 |
PlayerHUD_ApplyVideoMode |
patches the layout table per video mode | confirmed (code) |
0x001a1008 / 0x001a1138 |
HudBar_Setup / HudBar_Draw |
the meter used for rage (and the mini-game bars) | confirmed (code) |
0x001b6c50 |
HudCounter_Setup |
icon plus number (the item counters) | confirmed (code) |
0x001cdc80 |
HintBox_Update |
TutorialHUD.cpp; the box at HUD +0x8a10 (0x00609250) |
confirmed (code) |
0x001ce3c0 / 0x001ce218 |
HintBox_Queue / HintBox_InsertByPriority |
queue a hint by priority (Hints) | confirmed (code) |
0x001ce1e8 / 0x001ce718 |
HintBox_QueueGameHint / HintBox_WithdrawGameHint |
the game's own hints, by CfgTutorialMessage id |
confirmed (code) |
0x001ce5c0 / 0x001ce748 / 0x001ce550 |
HintBox_Withdraw / HintBox_FlushPriority / HintBox_Contains |
remove one hint, flush by priority, HUDCheckTutorialText |
confirmed (code) |
0x001bb250 / 0x001bb2f0 |
MarkupText_SetText / MarkupText_IsExpired |
the markup text widget's text and its <DISPLAYTIME> / <FREEZE> end |
confirmed (code) |
0x0019ef80 / 0x0019f1b0 / 0x0019f528 |
ActionPrompt_Setup / _SetText / _Update |
the action prompts at HUD +0x6b40 (Action prompts) |
confirmed (code) |
0x001b3cb0 |
HUD_ShowAnnouncement(hud, kind) |
a built-in announcement in the widget at +0xe340 |
confirmed (code) |
0x001ce9a8 |
Tutorial_CallCallback |
calls the HUDSetTutorialCallback function on each player-1 hit (Tutorial callback) |
confirmed (code) |
0x001c8b08 / 0x001c9028 |
ScrollIn_Queue / ScrollIn_Update |
queued messages at HUD +0x8dd0 |
confirmed (code) |
0x001dad88 |
HUD_SetObjective |
HUDSetObjective |
confirmed (code) |
0x001b7330 |
InstArrow_Update |
the instruction arrow at HUD +0x134a0 |
confirmed (code) |
0x001c3de0 |
Radar_Setup |
the radar's batches and map texture | confirmed (code) |
0x001c60b0 |
Radar_Render |
the radar disc (GUI has the blips) | confirmed (code) |
0x0017bc28 / 0x0017b8a8 |
Im2D_DrawTexturedDisc / _Ring |
a textured disc, and a ring with a fading edge | confirmed (code) |
0x0020e6f8 |
TargetPanel_Update |
the target's name and bar | confirmed (code) |
0x00221108 |
Human_ReportBarsToHUD |
sends health and power fractions to the HUD, which ignores them | confirmed (code) |
0x0024b780 |
Reticules_Update |
per update: who gets a health ring, with what alpha (The health rings) | confirmed (code), runtime |
0x0024a230 |
Reticule_QueueHealthRings(yaw, lift, human, alpha, view) |
one human's two rings: values, colours, size | confirmed (code), runtime |
0x0017b1e0 / 0x0017b2e0 |
GroundRing_Queue / GroundRing_DrawQueued |
the ring queue (0x005fd330, 0xc0 bytes each, count 0x0050ccd4) and its draw |
confirmed (code), runtime |
0x0024af78 / 0x00249cb0 |
Reticule_AddTargetMarker / Reticule_AddRumbleTeamDisc |
the locked target's marker; the Rumble team disc | confirmed (code) |
Data¶
Coordinates¶
Positions are GUI coordinates (Graphics): x right and y down, [0, 1] across a
safe area that maps to 5.2 %-94.8 % of the width and 4.5 %-95.5 % of the height. On the 640 × 448 screen:
The layout tables store a point as four floats (x, 0, y, 1) (the 3D vector form, y in the third float); sizes as
(w, h) in the first two. Confirmed (code): vadd.xyz of table vectors at 0x00212840, and the drop shadow
(0.0025, 0, 0.004) at 0x001a2690.
Video modes. Every value below is the default mode (NTSC, interlaced, 4:3: device flags 0x01,
Graphics), which is what the static data holds. 0x00211ef8, 0x001af010 and
0x001cdc80 overwrite many of them each frame for 16:9 (0x04), progressive (0x20) and the other modes; those
variants are not listed here. Confirmed (code).
The HUD object¶
One static object at 0x00600840 (0x22600 bytes), built by HUD::HUD (0x001acee0) at start-up and set up per
level by HUD_InitLevel (0x001ad588); parts set up there only when not already set up. Offsets from the object;
confirmed (code) at 0x001ad588, 0x001ae980, 0x001af010 and 0x001b1688 unless marked.
| Offset | Part | Set up / update / draw |
|---|---|---|
+0x0000, +0x0004 |
the two players' input records (VirtualPad, 0x2c bytes; Pads) |
0x001ae4d8 |
+0x0008, +0x000c |
the two player panels (PlayerHUD, static at +0x19130 + slot × 0x4a50; an allocated ANHud in levels 60-69) |
interface at panel +0x4120 |
+0x0010 |
a per-human widget fed by HUD_TagAddHuman / _TagRemoveHuman (role not traced) |
0x0019fbb0 / 0x0019fda0 (levels below 100) / 0x001a03a8 |
+0x15b0 |
a small widget hidden by HideHud (+0x08) |
0x001a8ed8 / 0x001a8f90 |
+0x15d0, +0x3f10 |
the two radars (0x2940 bytes each); positions +0x3f00, +0x6840 |
Radar_Setup / 0x001c5210 / Radar_Render |
+0x6850 |
the stopwatch, made at (0.5, 0.135) and moved by StopWatchHud_ApplyVideoMode (0x001cd400): on screen in the default mode its minutes form stands at the top right, right-aligned (confirmed (runtime) at the gym's heavy bag, Workouts) |
0x001cd578 / StopWatchHud_Update* / 0x001bb1c0 |
+0x6b40, +0x70d0 |
the action prompts | ActionPrompt_Setup |
+0x7660 |
a widget (role not traced) | 0x001bb650 / 0x001bbbb0 / 0x001bb8a8 |
+0x8960 |
the score board | 0x001c2638 |
+0x8a10 |
the hint box | 0x001cd988 / HintBox_Update |
+0x8dd0 |
the scroll-in queue, at (0.025, 0.6), colour (206, 206, 206, 206) | 0x001c8880 / ScrollIn_Update |
+0x9350 |
a markup text (role not traced) | 0x001acc60 / 0x001b9548 / 0x001bb1c0 |
+0x9540 |
a per-player widget drawn for humans in state 0x002238c0 |
0x001cb6f0 / 0x001cb798 |
+0x9640 |
a widget active while +0x964c |
0x001a4348 / 0x001a43d8 |
+0x9970 |
the four generic bars of HUDEnableBar kinds 0, 1 and 3 (0x3f0 bytes each, 0x0060a1b0) |
Scripted bars |
+0xa930 |
how many generic bars are shown (split between the two columns in split screen) | |
+0xa940 |
the kind-2 gauge (0x0060b180), active while +0xa94c |
0x001a3720 / 0x001a3990 / 0x001a3c00 |
+0xacb0 |
the five counter panels (HUDGetNewPH), at (0.9, 0.4); +0x3394 the stack's height |
0x001c2e38 / 0x001c3608 / 0x001c37f8 |
+0xe050 |
the spinner | 0x001ae378 |
+0xe150 |
the centred announcement (markup text, colour 0x6060bfff) |
Announcements |
+0xe340 |
the announcement (markup text, white) | HUD_ShowAnnouncement |
+0xe530 |
the RM_Intro screen | HUD_OpenRumbleIntro |
+0xe650 |
an Armies of the Night widget (AotN levels only) | 0x0019e030 / 0x0019e758 / 0x0019ed28 |
+0xea30 |
the credits | Credits_Load |
+0xebc0 + p × 0x430 |
the mash meters | Crimes |
+0xf420 + p × 0x540 |
the lock-picking dials | 0x001b7eb0 (size 0.158) |
+0xfea0 + p × 0xb10 |
the stereo-theft widgets | |
+0x114c0 + p × 0x450 |
a per-player widget (role not traced) | 0x001ab108 |
+0x11d60 |
a widget (role not traced) | 0x001b75f0 / 0x001b7608 / 0x001b7890 |
+0x134a0 |
the instruction arrow | |
+0x135e0, +0x136e0 |
the two fixed-camera icons | |
+0x137e0 |
19 sprites of the split-screen divider (0x100 bytes each) |
|
+0x14ae0 |
the six text-progress rows (0x560 bytes each), shown while +0x16b20 |
HUDEnableTextProgress |
+0x16b30 |
the Rumble widget (levels 100 and up), at (0.125, 0.7), sprite word 0x20e0000 |
0x001b6358 |
+0x177a0 |
shown (RestoreHud 1, HideHud 0) |
|
+0x177a4 |
active (set by HUD_InitLevel, cleared by HUD_ShutdownLevel) |
|
+0x177a8 |
draw the centred announcement while hidden | |
+0x177ac, +0x177b0 |
radars wanted on (HUDTurnOnRadar); radars turned on |
|
+0x177b4 |
fixed-camera icons enabled (written, never read: the icon) | |
+0x177b8 |
a radar item removed by HUD_RadarClearPending |
|
+0x177bc |
per player, the action object's hint id queued in the hint box | |
+0x177d0, +0x18280 |
the two radar frames (0xab0 bytes each) |
0x001aa290 / 0x001aa9b0 / 0x001aaba8 |
+0x18d30 |
the captions (Scenes) | Captions_Init |
+0x18e60 |
the caption pager (GUI) | 0x001cb398 / 0x001cb418 |
+0x18ee0 |
the Armies of the Night credits text (colour 0xff00b8f5), value +0x190d0, text buffer +0x190d4 |
|
+0x1911c |
the Armies of the Night boss picture (a sprite widget) | |
+0x19124 |
a scale per video mode (1.0 default) | |
+0x19128 |
the spinner's rate | |
+0x225d0 |
per player, the reticule request byte | HUD_SetReticuleRequest |
+0x225d2 |
a copy of game state +0x33a |
|
+0x225d4 |
radar 2 started | |
+0x225d5 |
an Armies of the Night level (60-69) | |
+0x225d8 |
the divider needs drawing |
The player panel layout (0x0050fa10)¶
A base per player, then a table of 0x240 bytes per player at 0x0050fa40 + player × 0x240 whose entries are
offsets from the base. Bases: player 0 (0.10, 0.05), player 1 (0.785, 0.05); 0x0050fa30 (0.04, 0.56) is
used for player 1 in a two-player split (0x00214138). Confirmed (code); values read from the executable.
| Table offset | Part | Player 0 entry | Player 0 on screen (GUI) | 640 × 448 |
|---|---|---|---|---|
+0x60, size +0x70 |
name banner | (-0.105, -0.005), size 0.06 |
(-0.005, 0.045) |
x 30, y 39 |
+0x80, size +0x90 |
rage meter (left end) | (-0.107, 0.035), 0.30 × 0.009 |
(-0.007, 0.085) |
x 29, y 55; 172 × 3.7 px |
+0xa0 |
score | (-0.103, 0.064) |
(-0.003, 0.114) |
x 31, y 67 |
+0xc0, +0xe0 |
money, its icon offset | (-0.095, 0.104), (0.012, -0.001) |
(0.005, 0.154) |
x 36, y 83 |
+0x00 |
text size of score and counters | (0.04, 0.05) |
||
+0x20, size +0x30, colour +0x10 |
backing plate | (0.07, 0.02), 0.46 × 0.095, 0x00111111 |
||
+0x100...+0x1e0 |
the four item counters | see below | ||
+0x1f0, +0x210-+0x230 |
nine tally marks | (-0.095, 0.104), size 0.04 |
Player 1's entries differ only in x (for example the banner (-0.015, -0.005), the meter (-0.018, 0.035)), so its
panel is the same shape at the top right. Runtime check: the rage meter's left end measured at 29 px across and
55 px down (screen fractions 0.047 and 0.124), the formula gives 29 and 55; the banner's left edge at x 0.05 of the
screen, and its centre at y 0.086, which fits a banner anchored by its left edge and its vertical centre (measured
for this widget only).
The backing plate (rect 78 of part_page0) is drawn with the table's colour 0x00111111, whose alpha is 0, and the
fade never touches its alpha, so it is invisible in the default mode. Confirmed (code) at 0x00212840,
0x00214138, 0x00213290; not visible in the screenshots.
The name banner¶
Everything in this section is confirmed (code) at the cited addresses unless a line says otherwise.
PlayerHUD_SetBanner (0x0020dc98) gives three sprite widgets the whole first rectangle of one sprite sheet, chosen
by a kind that 0x00229570 derives from the character id (human +0xcc):
| Kind | Character ids | Sheet record |
|---|---|---|
| 0 | 5, 7-10 | 0x31 |
| 1 | 0xf, 0x11 |
0x22 |
| 2 | 0x12, 0x14, 0xbc |
0x23 |
| 3 | 0xb, 0xd, 0xe |
0x1f |
| 4 | 0x1e, 0x20 (Rembrandt, id 32) |
0x2f |
| 5 | 0x15, 0x17-0x19 |
0x24 |
| 6 | 0x1a, 0x1c, 0x1d, 0xbf |
0x32 |
| 7 | 0x21, 0x23-0x25, 0xbe |
0x30 |
| 8 | 1, 3, 4, 0xbd |
0x21 |
| 9 | 0x26-0x28 |
0x20 |
10, 11, 0x15 |
0x2b, 0x2c / 0x2d-0x30 / any other whose s8 +0x1b0 is 1 |
0x2a-0x2e by language (W_GameState + 0x120: 0 0x2a, 1 0x2e, 2 0x2b, 3 0x2d, 4 0x2c) |
0x14 |
any other whose s8 +0x1b0 is 0 |
0x25-0x29 by language (0 0x25, 1 0x29, 2 0x26, 3 0x28, 4 0x27) |
| any other | any other (12 when +0x1b0 is negative, else 0x14 + +0x1b0) |
0x31 |
The sheet record is the high half of the sprite word (record << 16, rectangle 0); the widget makes its own sprite
batch over that sheet. Confirmed (code); kind 4 shows "REMBRANDT" (confirmed (runtime)). Which character each other
banner names is not checked.
The three widgets: two black copies at depth 10,000 offset by (+0.0025, +0.004) and (-0.001, -0.002) (0x00510054,
0x00510058, 0x00510060, 0x00510064, alpha 0xff), then the banner itself at depth 11,000, all of size 0.06 at the
banner position. The banner's colour is the rage colour (below), or (100, 100, 200, 160) while the human has state
flag 0x200000 (0x00228168, not traced). Confirmed (code) at 0x00214138; the red banner confirmed (runtime).
Colours¶
| What | RGBA | Where |
|---|---|---|
rage colour, normal (+0x4148) |
(170, 43, 43, 255) red |
0x00212840 |
rage colour, raging (+0x4144) |
(217, 158, 12, 255) gold |
0x00212840 |
| meter background | (80, 80, 80, 255) grey |
0x00212840 |
| meter capacity | (255, 255, 255, 255) |
0x00214138 |
| flash and spray counters | (99, 219, 75, 255) green |
0x00212840 |
| handcuff and key counters | (35, 83, 188, 255) blue |
0x00212840 |
| money and score text | white (255, 255, 255, 255) |
0x00212840 |
| hint box background | (0, 0, 0, 128) |
0x0050eb90 |
| radar disc | (191, 191, 191, 240), or blue (100, 120, 200, 240) (below) |
0x0050e9e8 |
In a level numbered 100 or more (0x0041d160: the level record's +0x04 ≥ 100) player 1's two rage colours are
swapped. Confirmed (code). The meter's red, white and grey confirmed (runtime).
The rage meter¶
A HudBar (0x001a1008, the first 0x70 bytes of the player panel). Its fields, confirmed (code):
| Offset | Meaning |
|---|---|
+0x00, +0x04 |
width, height (GUI) |
+0x10 |
left end (x, 0, y, 1) |
+0x20 |
background colour |
+0x24 |
capacity colour |
+0x28 |
fill colour |
+0x2c / +0x2e |
first rectangle / sprite batch (rectangle 54, batch 3 = part_page0) |
+0x38 |
fill fraction 0-1 |
+0x3c |
capacity fraction 0-1 |
+0x50 |
drawn (0 hides it) |
+0x5c |
right to left |
Values (0x00214138): fill = min(rage, rage maximum) / 150 and capacity = rage maximum / 150, each clamped to
0-1. Rage is human +0x650 and the maximum the Warrior class's s16 +0x00 (Combat); 150 is the
constant 0.0066667. So a Warrior with maximum 78 has a white strip over 52 % of the meter, and the red fill grows
along it. Confirmed (code); 40 of 78 confirmed (runtime): red to 27 %, white to 52 %, grey to the end.
Drawing (0x001a1138), left to right, all at the meter's height: the left cap (rectangle 56) in the background
colour; the body (rectangle 54) over the inner width (width less the cap's width) in the background colour; the
capacity strip (rectangle 55) over inner width × capacity in the capacity colour; the fill strip (rectangle 55) over
inner width × fill in the fill colour; the right cap (rectangle 57). The stretched rectangles are inset by one texel
on each side. Confirmed (code).
Flash and trail, unused. HudBar_Draw(flashBelow, bar, flash, trail) takes the threshold in f12 and the bar,
flash and trail in a0-a2. Every one of its 14 calls passes flash = 0 and trail = 0 (0x001a33a4,
0x001c0aac, 0x001c0ac0, 0x001c2504, 0x001d737c, 0x001d7390, 0x001d73e4, 0x001d7400, 0x00209034,
0x002119dc, 0x00211a18, 0x00211a44, 0x002135d0, 0x0021360c), so flashBelow (0.25 at most sites, 0 for the
mash meter) changes nothing and no bar flashes or trails through this path. Confirmed (code). The rage meter's
gold pulse below is the panel's own colour switch, not this flash. For reference, the dead path: with flash set and
the fill ≤ flashBelow, a byte at +0x65 drops by 16 each draw and jumps back to 255 when it falls under 32 (255,
239, ..., 47: a 14-draw cycle), and the background parts only (the two caps and the body, not the capacity strip
or the fill) get that byte ÷ 2 ORed into their colour's red byte. The trail (+0x44-+0x4c) would draw the fill
rectangle between the old and new fill ends for 64 draws after a drop, at alpha (count & 3) × 85 (a 4-draw flicker).
Confirmed (code) at 0x001a1138.
Fill colour: the rage colour for raging (human +0xe0 flag 0x80000) or not. A full meter (rage ≥ maximum)
plays vags/interface/rage_indicator_02 once and flashes: with t = game time mod 400 ms, f = |200 − t| / 200 (a
triangle wave), the fill is gold while f³ < 0.2 (0x005100c8) and red otherwise, so a short gold pulse every
400 ms. Confirmed (code) at 0x00214138, period 0x005100cc = 200.
FlashRageBar(player, n) (0x001b3f58, panel +0x4114): with n > 0 the whole meter is drawn for n frames
and skipped for n frames, repeating; n = 0 draws it every frame. Confirmed (code) at 0x00213290. The tutorial
passes 5: on and off every 5 frames (1/6 s at 30 frames a second).
The Warrior command menu¶
Everything in this section is confirmed (code) at the cited addresses unless a line says otherwise.
A war chief (human +0x3ac = 1) orders his crew from a six-slot menu held open with R2 and steered with the
right stick; releasing R2 gives the highlighted order. Each player panel owns one (WarCommandDisplay, panel
+0x1ef0, built by WarCommandDisplay_Init, 0x001a62c0); the commands themselves are on
AI: Warrior commands. Confirmed (code) at the addresses given unless marked. The
menu's input and issue steps are confirmed (runtime)
(crew-command-menu): see the end of this section.
Input (Player_UseItemCommand, 0x002843f8; pad commands from Combat: 1 is R2 held, 2 R2
released). Nothing happens while the human has a state flag of 0x80040000, or 0x20000 (that branch only handles
triangle), or has no player index (human +0x1b0 = -1), or while that player's menu is locked (game state +0x42e +
player). Release is tested before hold:
- R2 held (every frame it is down):
WarCommandDisplay_Open(0x001a6d28). When the display is allowed (+0x155c:WCEnableCommand,HUDShowWarCommand) and the human is a war chief, it is shown (+0x1554= 1), the "issued" flag (+0x1548) and the after-issue counter are cleared, the eighteen sprites are shown with any fade cancelled, and the camera's right stick is switched off for that pad (0x0050b1b0[pad]= 0, which Camera tests). It does not pause or slow the game: neither function touches the game timer, a time scale or the game mode, and the handler returns 0, so the rest of the frame's input (the left stick, attacks) still runs. The selection (+0x1540) is not reset: the menu opens on the last highlighted slot (slot 0 the first time). For a human who is not a war chief the display is hidden instead. - R2 released: when the display is set up (panel
+0x1efc),WarCommandDisplay_Issue(0x001a6c58).
Picking a slot (WarCommandDisplay_ReadStick, 0x001a7040, from WarCommandDisplay_Update each frame while
the menu is shown, not yet issued and not locked) reads the right stick's raw bytes (pad record +0x1a x, +0x1b y,
0-255, y down; Front end):
- Dead zone: nothing unless (x − 127)² + (y − 127)² > 12,100, a radius of 110 of 127: about 87 % deflection. With a stick value v in [-1, 1] and raw ≈ 127.5 + 127.5 v, a push of 0.9 picks and 0.8 does not. Inside it the highlight stays where it was.
- Angle, clockwise from up (up 0°, right 90°, down 180°, left 270°): a quadrant base (0°, 90°, 180°, 270°) plus asin(|d| / 128) of one axis's offset d from 128, not atan2, so it is exact only at full deflection. d is the x offset in the up-right and down-left quadrants and the y offset in the down-right and up-left ones; an offset of 128 or more counts as 90°. A 45° push of magnitude 0.9 (offsets about 81 and 81) reads 39° up-right.
- Sectors (without hysteresis):
| Slot | Stick | Angle | Command |
|---|---|---|---|
| 0 | up | 337.5°-22.5° | 0 follow |
| 1 | up-right | 22.5°-87.75° | 2 defend |
| 2 | down-right | 92.25°-157.5° | 4 scatter |
| 3 | down | 157.5°-202.5° | 3 hold |
| 4 | down-left | 202.5°-267.75° | 5 wreck (steal / vandal) |
| 5 | up-left | 272.25°-337.5° | 1 attack |
- Straight left or right (within 2.25°,
0x0050d2b4= π/80) selects nothing new. The slot-to-command map is the jump table at0x00553fe0(WarCommand_FromSlot,0x001a8530; 7 for any other slot); command 6 has no slot. The command names follow AI (read from the lines, inferred). - Hysteresis: the highlighted sector reaches π/16 = 11.25° (
0x0050d2b8) further into each neighbour until the highlight moves, so a stick resting on a boundary does not flicker. - Disabled commands are not skipped: the stick can highlight them; the text then says so (below) and issuing one does nothing.
- Sound: interface cue
0x20each time the highlight changes. The stick is read every frame (the interval0x0050d280is 0, against the timer at0x0050b8b8).
Issuing (WarCommandDisplay_Issue, once per opening, only while shown and not yet issued): the highlighted slot is
kept (+0x1544), its command stored as the player's last (game state +0x41c + player) and given through
0x0041c4e0(gameState, chief, command, forced 0, pos 0, arg 0) (chief = display +0x156c); 0x0051480c is set
to 0xff. A disabled command has its text cleared first and the dispatcher then refuses it
(AI, step 1), though the last-command byte has already been written. Only R2's release
issues: there is no flick or timeout, and releasing without touching the stick issues the highlighted slot (the
previous choice). An open menu also issues when the pause menu closes (PauseMenu_Close, 0x001dcb20) and on any
frame the HUD is not drawn (HUD_Render's hidden branch, 0x001b1688). The chief's spoken line comes from the
dispatcher (Speech).
After issuing (WarCommandDisplay_Update, 0x001a7e48): for 10 updates (0x0050d300) the menu stays as it is,
then the camera's right stick is switched back on (0x0050b1b0[pad] = 1) and the sprites fade out: the chosen slot's
plate over 1,500 ms (0x0050d310), everything else over 500 ms (0x0050d30c); the chosen slot blinks, hidden 2 of
every 4 updates (0x0050d24c = 2, WarCommandDisplay_BlinkSlot, 0x001a7d10). The display closes (Shutdown)
when its text has expired (MarkupText_IsExpired: the text's <DISPLAYTIME>, inferred). It closes at once if the
chief goes down (0x00227dd8), and a lock that arrives while it is open ends it as if issued, with no command.
Seen at runtime (PCSX2 2.9.129, level 99's street, a war chief with two Warriors; the
crew-command-menu recipe): with R2 held (pad command 1) and the right
stick at full deflection toward a slot, the display's shown flag (+0x1554) read 1 and its highlight (+0x1540) the
slot of the table above, for all six directions (60 degrees apart), each reached from a different slot; releasing R2
(command 2) issued the slot's command: the gang's tactic and the crew's goals changed to it. The street disables pad
commands 1 and 2 for the player (a disabled command is matched into the record's pending +0x24, so the human never
sees it): with only WCEnableCommand and HUDShowWarCommand run, R2 and the stick left the display hidden and the
tactic unchanged, and after EnableCommand(player, 1, true) and (player, 2, true) the same input gave the command.
A stick exactly up (raw 128, 0) left the highlight on slot 3 (down) in three runs of three; a raw x of 133 or 123 with
y 0 selected up (two runs each). Exactly down (128, 255) moved it from slot 1 to slot 3 in three runs of three. Why up
fails at x 128 and down does not is not traced.
Layout (default video mode, GUI coordinates). The centre x is the layout record's (0x006006a0 + player ×
0x80): 0.5, set by Open; in a level numbered 100 or more with two players (0x001fe198 = 1), 0.23 for player 1
and 0.76 for player 2. Each slot is three sprites at one point, back to front: a black backing (sprite word 0xd0100
of instance 6, size 0.077), a plate (the same rectangle, size 0.07) and the icon (part_page0, instance 3, size
0.08):
| Slot | Position | Icon rectangle |
|---|---|---|
| 0 | (x, 0.795) | 0x56 |
| 1 | (x + 0.069, 0.795) | 0x5a |
| 2 | (x + 0.071, 0.93) | 0x58 |
| 3 | (x, 0.93) | 0x55 |
| 4 | (x − 0.071, 0.93) | 0x59 |
| 5 | (x − 0.069, 0.795) | 0x5b |
So two rows of three, about 0.07 apart, the top row at y 0.795 and the bottom at 0.93 (WarCommandDisplay_Place,
0x001a77b0, from base y 0.99 0x0050d20c and the offsets 0x0050d214-0x0050d23c). Between them, at (x, 0.86)
(0x0050d2fc), the name of the highlighted slot: a markup text (font slot 3, colour (191, 191, 191), the
record's +0x10) over a box sprite (rectangle 0x4e, colour (0, 0, 0, 143)) sized to the text plus (0.013, the
record's +0x64). The text is entry slot of the command-text table (0x006007c0, filled by CfgWarriorCommand
from GSTRING.COMMAND; indexed by display slot, not command
id); entry 6 (0x006007d8), the out-of-range text, replaces it while the crew is out of range (game state
+0x414 + player, set by the range check; confirmed (runtime), below) and entry 7
(0x006007dc) while the highlighted command is disabled. Each is read every update while the menu is open and no
command has been issued yet (WarCommandDisplay_Update, 0x001a7e48, which copies the byte to +0x154c); after an
issue the text is left as it was until it expires. Runtime check (PCSX2 2.9.94, a level80 checkpoint state with
the scripted-pad and right-stick patches, R2 held, sticks at rest): with the byte left alone the menu named the
highlighted command; with the byte rewritten to 1 every few milliseconds the same menu showed the out-of-range text
in its place. Written once, the byte went back to 0 within a second: the player's update checks the range every 40
updates and clears it while a member is within 50 m. Colours (WarCommandDisplay_Render,
0x001a8590, values from 0x001a8b48): the highlighted slot's icon white, plate gold (255, 183, 0), backing grey;
the others' icon white, plate dark (35, 35, 35), backing black; a disabled command's icon (30, 30, 30). The 16:9
and other modes overwrite the offsets and sizes (0x001a7e48); they are not listed here.
No d-pad path: the player's command handler gives the menu nothing on the d-pad (d-pad right is the flash);
scripts order the crew with WCIssueCommand.
Score and money¶
- Score (
0x001c7730, panel+0x70): seven digits,"%07d", of the player's score in the stats object (0x006fe490, Inventory and statistics); leading zeros grey and the rest white (confirmed (runtime), "0000628"). It counts toward a new value by (difference / 16) ± 1 per frame, and a change shows a"+N"(or"-N") beside it that shrinks over the 400 ms before it is removed at 1,000 ms (0x0050ea3c,0x0050ea40). Drawn only in levels numbered below 100. Confirmed (code) at0x001c7928,0x00213290. - Money (
0x001bea00, panel+0x400): inventory item 2 (Inventory items), clamped to -99..9999, counting toward a new value by (difference / 16) ± 1 per frame, and a floating±$Nline (<MONEYPLUS>/<MONEYMINUS>markup) for 1 s after a change, during which the count waits. While it counts, the looping cue0x10plays, one voice kept by handle and stopped when the count ends (Sound events: the money count's sound). Confirmed (code) at0x001becb0; the sound's keep and stop confirmed (runtime). Hidden when 0 (inferred: the runtime panel with 0 dollars showed none).
Item counters¶
Four icon-plus-number widgets, each shown only while its count is at least 1 (count clamped to 9):
| Panel offset | Item | Icon (part_page0 rectangle) |
Colour | Getter |
|---|---|---|---|---|
+0xe30 |
1 flash | 29 | green | 0x001c68e8 |
+0x1260 |
3 spray paint | 30 | green | 0x001c96e0 |
+0x1690 |
5 handcuffs | rectangle of 0x00212840 |
blue | 0x001aaf88 |
+0x1ac0 |
6 skeleton keys | rectangle of 0x00212840 |
blue | 0x001aadf0 |
They fill four slots in the order above, each counter taking the first free one (0x00213718). A slot's
position is an offset from the panel's base; x is relative to x0 = 0x00510040 (player 0, 0.0) or 0x00510044
(player 1, 0.089), and the slots move right as the money's digits grow. Line 1 is y 0x00510048 (0.104), line
2 y 0x0051004c (0.146); values read from the executable. The slots live in a static record per panel layout,
0x0050fec0 + layout × 0xc0: four slots of 0x30 bytes, each x +0x00, y +0x08 and the item it holds +0x20
(0-3 in the order above, 4 = free; every update first frees all four). Confirmed (code) at 0x00213770:
| Money | Slot 0 | Slot 1 | Slot 2 | Slot 3 |
|---|---|---|---|---|
| 0 or less | x0 − 0.095, line 1 |
x0 − 0.045, line 1 |
x0 + 0.005, line 1 |
x0 + 0.055, line 1 |
| 1-9 | x0 − 0.045, line 1 |
x0 + 0.005, line 1 |
x0 + 0.055, line 1 |
x0 − 0.09, line 2 |
| 10-99 | x0 − 0.028, line 1 |
x0 + 0.02, line 1 |
x0 + 0.07, line 1 |
x0 − 0.09, line 2 |
| 100-999 | x0 − 0.005, line 1 |
x0 + 0.042, line 1 |
x0 − 0.092, line 2 |
x0 − 0.04, line 2 |
With 1,000 or more the slots keep their previous places. Confirmed (code) at 0x00213770. A changed count makes the
panel show (the fade timer restarts). Icon size 0.04, text size the table's (0.04, 0.05).
Hint box layout (0x0050eb50)¶
Set at every HintBox_Update (0x001cdc80), default mode, confirmed (code) and read back at runtime:
| Offset | Value | Meaning |
|---|---|---|
+0x00, +0x08 |
0.016, 1.0 | the text's x, and the bottom it sits on (one player) |
+0x10, +0x18 |
0.245, 1.0 | the same in a split screen |
+0x28 |
0.73 | a width matching the measured box (0.73 wide); the text itself wraps at the widget's own +0x1d4, 0.74 with one player (Hints; inferred: +0x1d4 read 0.74 at runtime, and the level99 fence-climbing hint is one line in a box about 0.726 wide, PCSX2 slot07) |
+0x40 |
0x80000000 |
box colour, black at alpha 128 |
+0x50, +0x54 |
-0.018, -0.035 | box offset from the text's top-left |
+0x60, +0x64 |
0.03, 0.035 | box size beyond the text's |
+0x70, +0x74 |
4,000, 200 | written every update but read by nothing found (the box's functions read only +0x00-+0x64 through its pointer +0x3a4); a hint's time comes from its markup (Hints) |
The box is the text's rectangle grown by those amounts and drawn first; the text is placed so that its bottom is at
y = 1.0 less the box's height. Runtime check: the box spans screen x 0.052-0.708 and y 0.812-0.923 (GUI x
0.00-0.73, y 0.84-0.97).
Behaviour¶
The HUD's frame¶
Everything in this section is confirmed (code) at the cited addresses unless a line says otherwise.
HUD_Update (0x001af010) runs once per mode-1 frame with both players' pads; HUD_Render (0x001b1688) in the
overlay pass (Boot). Mode 0xb (mission complete) runs the overlay pass but not HUD_Update, so the HUD is
drawn as it was left. Confirmed (code) at 0x0015d160.
Not during a letterbox. HUD_Render draws nothing (and turns both radars off) unless player 1's screen
effects have the letterbox fully out: state +0x1e8 = 0 and level +0x1ec = 0 (Graphics).
So no HUD part, text or prompt shows while a cinematic scene's bars are in or moving, whatever HideHud says; the
scene's subtitles are drawn by the caption system after the HUD (Scenes: subtitles). Confirmed
(code) at 0x001b1688. HUD_Update likewise does its work only while the HUD is active (+0x177a4) and player 1's
letterbox is fully out; otherwise it only turns the radars off. So the hint box, the scroll-in queue, the
announcements and the panels neither advance nor expire while a scene's bars are up, but do while the HUD is merely
hidden. Confirmed (code) at 0x001af010. What shows the HUD again after a scene:
Who shows the HUD again.
Render order (0x001b1688), when the HUD is shown (+0x177a0 = 1, set by RestoreHud): the radars and their
frames, the instruction arrow and two other widgets, the counter panels, the bars of HUDEnableBar, each player
panel, the announcement, per player the score boards and other panels, the centred custom announcement (+0xe150),
then the scroll-in messages, the hint box, and last the action prompts. When hidden (HideHud), only the player
panels' hide is applied (0x001b2380) and the centred custom announcement is still drawn when +0x177a8 is set. The
2D pass sorts batches by depth afterwards (GUI), so this order matters only within one batch.
Confirmed (code); the roles of the unnamed widgets are not traced.
What hides what (the bottom-left text), confirmed (code) at 0x001b1688:
| Showing | Hidden that frame |
|---|---|
an announcement (+0xe340, until its markup time ends) or a mini-game panel |
the scroll-in message, the hint box, the action prompts |
| a scroll-in message (objective) | the hint box |
Hidden parts are not paused: a hidden hint keeps its place and its timer runs on. The prompts are also not drawn in an
Armies of the Night level (0x0041d110).
The player panel¶
Everything in this section is confirmed (code) at the cited addresses unless a line says otherwise.
Attach. Human_MakePlayer (0x00229c40) calls HUD_AttachPlayer for the human: if panel slot (0 or 1) is not
attached yet, it runs its Init once (0x00212840), attaches it, sets the banner (0x0020dc98), and hides it again
when the HUD is hidden (+0x177a0 = 0); it returns the slot, or -1. The panel index is kept at human
+0x380. Confirmed (code).
Health and power are not shown on the panel (they are on the ground: The health
rings). Each frame 0x00221108 passes the player's health fraction (record +0x144 /
+0x146) and power fraction (+0x148 or +0x14a over their maxima) to 0x001b2430 / 0x001b2460, which call
0x0020dfd0, a function that returns at once. This holds for PlayerHUD; the Armies of the Night panel draws a power
bar and a health bar (ANHud). Confirmed (code); confirmed (runtime): health 314
and 900 of 900 left the panel unchanged.
Visibility. The panel draws when attached (+0x40f8) and shown (+0x40fc). Its fade: +0x411c holds the
time of the last activity; 0 means "now" and is stamped on the next render. For 2,000 ms after it (0x00510070) the
panel is opaque, then its alpha falls linearly to 0 over 1,000 ms (0x00510074) and it is not drawn. The alpha
multiplies the banner, the meter's colours, the counters and the score. Confirmed (code) at 0x00213290.
Activity (resets +0x411c to 0, confirmed (code)):
ForceShowPlayerHud(player, true)(+0x4104≠ 0): every frame, so the panel stays up until it is turned off;- a change of score, money or an item count (
0x001c7928,0x001becb0,0x001ab038and the other counters); - SELECT pressed (
0x00144bf0(pad, 0x100)) while the per-player record's+0x1bis set; - the action prompt (below) naming one of the prompts
Spray,Flash,BladesorGive Mon....
Confirmed (runtime): with no activity the panel was invisible; setting +0x4104 brought it up.
Other parts (drawn when their state applies, not seen in level99's street): nine tally marks (panel
+0x4150, rectangles 366, every fifth 367, of part_page0, size 0.04, in the player's colour +0x4148, count
+0x413c, enabled +0x4130); the mini-game panel (+0x3480, 0x001bf7e0: two dark (32, 32, 32) bars and
hud_minigames icons 5-9; the mugging drives it, 0x001b28b8-0x001b2918, Crimes);
the Warrior command display (+0x1ef0, 0x001a62c0, the d-pad pictures of big_font 256-261
with command icons, HUDShowWarCommand). Confirmed (code) for the structure; their layouts are not covered here.
Objectives (HUDSetObjective)¶
Moved to HUD messages: objectives, hints, prompts and scripted bars.
Hints (HUDSetTutorialText)¶
Moved to HUD messages: objectives, hints, prompts and scripted bars.
The queue at runtime¶
Moved to HUD messages: objectives, hints, prompts and scripted bars.
The tutorial callback (HUDSetTutorialCallback)¶
Moved to HUD messages: objectives, hints, prompts and scripted bars.
Announcements and other messages¶
Moved to HUD messages: objectives, hints, prompts and scripted bars.
Scripted bars (HUDEnableBar)¶
Moved to HUD messages: objectives, hints, prompts and scripted bars.
Action prompts¶
Moved to HUD messages: objectives, hints, prompts and scripted bars.
The talk prompt¶
Moved to HUD messages: objectives, hints, prompts and scripted bars.
The instruction arrow (HUDEnableInstArrow)¶
Moved to HUD messages: objectives, hints, prompts and scripted bars.
The radar on screen¶
Moved to HUD radar, target panel and health rings.
The target panel¶
Moved to HUD radar, target panel and health rings.
The health rings¶
Moved to HUD radar, target panel and health rings.
Showing and hiding¶
The HUD's shown flag is +0x177a0. Nothing is saved when it is hidden: each part keeps its own flags, and
RestoreHud shows a part only if those still allow it. The HUD's per-level set-up (0x001ad588) sets +0x177a4
(the HUD is active), +0x177ac (the radars come back on their own, below) and ends with HideHud, so a level
starts with the HUD hidden until something below shows it. Confirmed (code).
| Part | HideHud (0x001b1f38) |
RestoreHud (0x001b20f8) |
|---|---|---|
| each player panel | hidden (+0x40fc = 0), when attached |
shown (0x0020e028) only if attached and its "may show" flag +0x4108 is set |
the widget at +0x15b0 |
+0x08 = 1 |
+0x08 = 0 |
each radar (+0x15d0, +0x3f10) |
blips off, "hidden by the HUD" +0x0c = 1 |
only a radar that is on (+0x04, set by HUDTurnOnRadar): blips on, +0x0c = 0, +0x08 = 1; a radar that is off stays off |
the score board +0x8960 |
hidden, +0xac = 1 |
+0xac = 0; shown only when enabled (+0xa8) and both +0xa0 and +0xa4 are set |
both radar frames (+0x177d0, +0x18280) |
their parts off | their parts on (when set up, +0xa98) |
| hint box, objectives and scroll-in messages, announcements, counter panels, bars, arrow, prompts | untouched | untouched |
The parts in the last row are simply not drawn while +0x177a0 is 0 (The HUD's frame); a hint
or objective queued while hidden (HUDSetTutorialText, HUDSetObjective) is kept, and HUD_Update keeps stepping
it while no letterbox is up. HideHud does nothing in an Armies of the Night level whose game state +0x14c is 1.
Confirmed (code).
ShowHuddoes nothing: its function (0x001b3ec8) is a bare return (binding);level99'sMaincallsShowHud(0)thenRestoreHud()(Scripts). Confirmed (code).HidePlayerHud/ShowPlayerHudclear or set each panel's "may show" flag+0x4108and hide or show it. Confirmed (code).
Who shows the HUD again¶
Besides a script's RestoreHud, three engine paths call it. Confirmed (code) unless marked.
- The letterbox going out (
0x0018d910, the letterbox step of each view's screen effects, run in the overlay pass). Starting a letterbox move (in or out, state 1) sets the record's+0x1f4to 0; when the bars reach level 0 the time is stamped there; the next step that finds the bars out (state 0, level 0) with+0x1f4not −1 shows both player panels (0x001b2330), callsRestoreHud, and sets+0x1f4= −1. So every cinematic scene shows the HUD as its bars finish going out, 1.5 s after its end (Scenes: ending), whatever hid it (SuperRunScene'sHideHud, the scene start's ownHideHud); a script'sScreenQueueEffecttype 2 or 3 does the same. A skipped scene ends the same way (Scenes: skipping). Bars that only go in restore nothing until they go out, and a scene played without bars (Bars= false) leavesSuperRunScene'sHideHudin force until a script'sRestoreHud. This is howlevel99's HUD returns afterl99_c1:P1.StartTrainingcalls noRestoreHud. Confirmed (runtime) on a copy of slot 1: with+0x177a0and the panel's shown flag cleared by PINE writes and a letterbox put in and out by writing the screen-effects record (1.5 s each way), both stayed 0 until+0x1f4was stamped as the bars reached 0, and on the next frame the panel showed,+0x177a0became 1 and+0x1f4−1. A real scene's end was not watched. - Play resuming (mode 1
Resume,0x00158580, when a mode pushed over play is popped, such as the pause menu, mode 0xa):RestoreHudwhen the HUD is active (+0x177a4) and hidden, game state+0x14cis 0, and player 1 is not in a scene (Human_IsInSceneState,0x00227d28: human+0x280≠ −1 or human flag0x800000). ItsSuspend(0x00158660) callsHideHudunless0x005e5580is set (not traced) or in an Armies of the Night level. So pausing hides the HUD under the menu, and closing the menu shows it even if a script had hidden it, unless player 1 is in a scene. - After wasted or busted. The death camera's start (
0x0011daa8, and the same steps in mode 1Update,0x00158728) keeps+0x177a0in player 1's camera record (+0x1fc) and callsHideHud; mode 0xc's handler (0x00155408) callsRestoreHudif it was shown, when player 1's camera is still that type-0xc camera.
The other hides: a cinematic scene's start calls HideHud (and hides the panels, 0x001b2380) when the global scene
state 0x0051489c + 0x410 is 0 (Scenes: starting, step 4, at 0x0039da90); HUD_Render draws
nothing while a letterbox is up (The HUD's frame). The Armies of the Night end screen
(HUD_ANLaunchEndScreen) hides and later restores it. Confirmed (code).
The radars across a scene¶
While a letterbox is in or moving, HUD_Render and HUD_Update turn both radars off every frame
(0x001b2658, which clears each radar's on flag +0x04 and +0x177b0), so the RestoreHud at the end finds them
off and leaves them off. HUD_Update brings them back on its own: when the letterbox and the fade (screen effects
+0x1d4, +0x1d8) are both at 0, +0x177ac is set and +0x177b0 is 0, it turns both on (0x001b2610); while a
fade runs it turns them off. +0x177ac is set by the level's set-up and by HUDTurnOnRadar (0x001b4328, for one
player or both) and cleared by HUDTurnOffRadar (0x001b43a8). So after a scene the radars return only if the last
radar call was "on": level99's P1.SetupCombat calls HUDTurnOffRadar before l99_c1, so its radars stay off
after the intro. Confirmed (code); confirmed (runtime) with the letterbox writes above: with +0x177ac 0 radar 0
stayed off after the bars went out, with it 1 both radars came on in the frame +0x177a0 became 1.
Pads¶
Each player has a 0x2c-byte input record (HUD +0x00/+0x04) whose byte +0x19 is the pad record it reads
(-1 none) and +0x1b whether it is bound; each pad record's +0x42 names its owner.
HUD_UpdatePadBindings (0x001ae828) runs in every menu and mode update and assigns player 0, then player 1 once
player 0 is bound. For one player (0x001ae638): player 2 in a one-player game in mode 1 is unbound; an unbound
player takes the pad its human's player record names; failing that, outside modes 1 and 10, the first connected pad
record (of the 8) that the other player does not use on which START is pressed (0x00144bf0(pad, 0x800)); a
bound player whose pad record names another owner is rebound to it. PM_Greet clears every binding first
(0x001ae5a0), so the title screen's START picks player 1's pad. Confirmed (code).
The split-screen divider¶
In split screen (HUD_IsSplitScreen, or more than one view with 0x0050b194 > 1) the HUD lines up 19 sprites
(HUD +0x137e0, set up with size 0.1, sprite word 0x170, depth 300, colour (191, 191, 191, 255)): with
0x0050b1a4 = 0, 15 of them in a column at x *0x0050d47c, y from *0x0050d480 in steps of *0x0050d48c,
sized (*0x0050d494, *0x0050d498), unrotated (the other 4 hidden); with it set, all 19 in a row at y
*0x0050d488, x from *0x0050d484 in steps of *0x0050d490, sized the other way round and rotated a quarter turn.
HUD_Render draws them once after each layout (+0x225d8). Confirmed (code) at 0x001af010, 0x001b1688; that the
sprites form the line between the two views is inferred.
The fixed-camera icon¶
Everything in this section is confirmed (code) at the cited addresses unless a line says otherwise.
A "this camera can't be turned" hint: a crossed-out film camera (part_page0 rectangle 87, sprite word 0x57,
batch 3) that appears while the player pushes the right stick on a camera that ignores it, and fades out over a
second after he lets go. Confirmed (code) at 0x001af010 unless marked.
- Set-up (
HUD_InitLevel,0x001ad588): oneBaseWidgetper player at HUD+0x135e0+ player ×0x100, size 0.1, depth 11,000, colour (191, 191, 191, 255), hidden but active;+0x177b4= 1. - When (each
HUD_Update, per player): if the player's camera type (camera vtable+0x1ec) is 0Cam_Fixed, 1Cam_Locked, 5Cam_Transitionor 9Cam_Rail(Cameras), or his camera switch 0 is off (0x0050b1b8[player]: right-stick turning and the zoom buttons, default on, switch 0), then pushing the stick (HUD_IsPlayerStickPushed,0x001aef50: a raw byte+0x1aor+0x1bof the pad record outside 64-176) shows the icon and cancels its fade (BaseWidget_CancelFade,0x001a25b8: end time+0xf4= 0). Not pushing changes nothing. With any other camera and the switch on, the icon is hidden at once. Pad+0x1a/+0x1bare stored with the right stick's floats+0x10/+0x14(0x00149990) and are what the camera's right-stick step reads, so this is the right stick. - The fade: later in the update a shown icon gets
BaseWidget_StartFade(icon, 1000)(0x0050d49c= 1,000 ms), which starts only when no fade runs;BaseWidget_RenderAlpha(0x001a2690) draws it at its alpha × (time left ÷ 1,000), andBaseWidget_IsFadeDonehides it when the time is up. A pushed update cancels the fade and the same update restarts it, so the icon stays fully opaque while the stick is held and fades linearly to nothing over 1 s after release. - Place and size (set each update while shown; GUI position; the size is an overlay height, the width following
from the rectangle's aspect). In the default mode (device flag
0x01only) none of these globals is rewritten, so the static values hold: (0.9, 0.64) (0x0050d468,0x0050d46c), size 0.09 (0x0050d450;0x0050d454= 0.08 also passed). In two-player views (split screen, or0x0050b194> 1 with more than one view) player 0's icon is at (0.11, 0.64) (0x0050d460,0x0050d464, never rewritten) and player 1's at (0x0050d468,0x0050d46c). Other modes rewrite them: 16:9 (0x01with0x04) x 1.05, size 0.08; progressive 16:9 (0x01,0x20,0x04) (1.0, 0.63), size 0.08 × 0.08; with0x01clear, (0.9, 0.63) and 0.06 × 0.058 in 4:3, (1.09, 0.58) and 0.075 × 0.08 in 16:9. - On in story play.
+0x177b4is written (by the set-up andHUD_SetFixedCamIconVisible) but never read. What gates the icon is each widget's active flag (+0x08:BaseWidgetvtable0x005394b8slot+0x40sets it,0x004e3fb8, slot+0x48reads it), whichBaseWidget_RenderAlphatests with visible (+0x04); the set-up turns it on.HUDEnableFixedCamIcon(false)turns it off, and the scripts call it only inlevel60-level64(once each, withfalse; disc scan of the compiled scripts); nothing turns it back on within a level. So in every other level it shows on a fixed, locked, transition or rail camera, or while a script has switch 0 off.
The spinner¶
Everything in this section is confirmed (code) at the cited addresses unless a line says otherwise.
A sprite at HUD +0xe050 (0x001ae378: size 0.09, depth 11,000, part_page0 rectangle 92 (sprite word 0x5c,
batch 3), colour (191, 191, 191, 255), not visible and active from the set-up, at (0.95, 0.83) in the default
mode, (0.9, 0.76) or (1.09, 0.77) in the others). It is level loading's "blinking element" 0x0060e890 (HUD
0x00600840 + 0xe050). Confirmed (code) unless marked:
- It blinks; it does not turn. While shown (
+0xe054),HUD_Update(0x001b0580-0x001b0658) adds the rate+0x19128to the global0x0050d50cand then writes 0 to the widget's rotation (+0xa0, radians), re-places it by video mode and updates it. Nothing reads0x0050d50c(its only two references are that read and write), andLoadScreen_DrawPulse(0x001613f0) also zeroes the rotation (0x0060e930) before drawing. So the rate is dead: the widget is always drawn upright, whatever+0x19128holds. - The blink is
LoadScreen_DrawPulse's: the colour (170, 43, 43) × 1.3 = (221, 56, 56, 255), faded linearly to transparent over the first 1,100 ms of each 2,200 ms of the real-time clock (0x0050b8b8slot+0x34, ms) and back over the second (Level loading). The colour it leaves stays on the widget; nothing restores the grey. - The rate argument. Every caller passes the real-time clock's milliseconds × 0.001 (seconds since the clock
started, the clock value itself, not a frame time):
HUD_Render(0x001b18a4),Preload_DrawLoadingIndicator(0x00161600) andMemCardLoadScreen_Tick(0x001619d0). Since the angle is dead, a reimplementation can ignore it. - Drawn by
HUD_Render(0x001b1688) in two places. (1) The fade branch: no letterbox, captions not freezing, the profile manager done (PM_IsDone, flag0x0050f5b0= 1), HUD+0xea3cand+0xe53cboth 0, player 1's fade level (0x005fdeb8 + 0x1d8) at least 1.0 (the screen fully faded), and the top game mode not0x14: shown, updated, drawn byLoadScreen_DrawPulse, hidden again, and nothing else of the HUD is drawn that frame. (2) The normal branch, while the HUD is shown (+0x177a0):BaseWidget_Renderon it right after the instruction arrow (0x001b1a48), which draws only while it is visible (+0xe054). - Who leaves it visible:
HUD_SetSpinner(rate, hud, on)(0x001b24d0) is turned off again in the same call byHUD_Render's fade branch and byPreload_DrawLoadingIndicator, butMemCardLoadScreen_Tickonly turns it on. So after the start-up memory-card load it stays visible, drawn by the normal branch in its last pulse colour, until the next fade branch turns it off (inferred from the code; not seen at runtime).
What level99 uses¶
The first mission's scripts call, among the HUD bindings: HUDSetObjective, HUDRemoveAllGoalText,
HUDSetTutorialText, HUDFlushTutorialText, HUDSetTutorialCallback, HUDEnableGameTutorialText,
HUDEnableInstArrow, FlashRageBar, ForceShowPlayerHud, HUDTurnOnRadar, HUDTurnOffRadar, the radar objective and
item calls, HUDGetNewPH / HUDSetPHValue / HUDReleasePH, HUDSetAnnounceMsg, ShowHud, HideHud and RestoreHud
(the mission1 flags of the HUD bindings). It does not end on a HUD screen: the
mission ends through HUDLaunchMissionComplete and mode 0xb (Front end,
Scripts), which draws no screen of its own.
Function index¶
Every function of the HUD files, by source file in address order, with the name it has in Ghidra (ours). Rows link to
the section that describes the behaviour where there is one. The files and ranges are from the Source
map. The tables live on three pages: HUD parts to GUI/HUDInterface.cpp,
GUI/HUDLua.cpp and GUI/MessageHUD.cpp to the game state; the
headings below point to them.
Before GUI/BaseWidget.cpp (no path string): HUD parts¶
Moved to HUD function index: widgets, radar frame and the HUD interface.
After GUI/BaseWidget.cpp (no path string): mash meter and chase HUD¶
Moved to HUD function index: widgets, radar frame and the HUD interface.
The mash meter on screen¶
Moved to HUD function index: widgets, radar frame and the HUD interface.
After GUI/BaseWidget.cpp (no path string): the chase gauge¶
Moved to HUD function index: widgets, radar frame and the HUD interface.
GUI/ChecklistMessageHUD.cpp¶
Moved to HUD function index: widgets, radar frame and the HUD interface.
After GUI/ChecklistMessageHUD.cpp (no path string): radar arcs, Warrior command display¶
Moved to HUD function index: widgets, radar frame and the HUD interface.
After GUI/Credits.cpp (no path string): the radar frame¶
Moved to HUD function index: widgets, radar frame and the HUD interface.
After the stub 0x001aad70 (no path string): item counters and status icons¶
Moved to HUD function index: widgets, radar frame and the HUD interface.
Before GUI/HUDInterface.cpp: the HUD object¶
Moved to HUD function index: widgets, radar frame and the HUD interface.
GUI/HUDInterface.cpp¶
Moved to HUD function index: widgets, radar frame and the HUD interface.
GUI/HUDLua.cpp: the bindings' HUD calls (first part)¶
Moved to HUD function index: the HUDLua bindings.
GUI/HUDLua.cpp¶
Moved to HUD function index: the HUDLua bindings.
The gang-count indicator¶
Moved to HUD function index: the HUDLua bindings.
What the count counts¶
Moved to HUD function index: the HUDLua bindings.
The panel's tally (players 0 and 1)¶
Moved to HUD function index: the HUDLua bindings.
The lock-pick dial layout¶
Moved to HUD function index: the HUDLua bindings.
The dial next to the original (2026-10-11)¶
Moved to HUD function index: the HUDLua bindings.
GUI/HUDLua.cpp: the scripted bars¶
Moved to HUD function index: the HUDLua bindings.
After GUI/MessageHUD.cpp (no path string): the status column¶
Moved to HUD function index: message, mission select and panel files.
GUI/MissionSelectHUD.cpp: the money counter and the mug meter¶
Moved to HUD function index: message, mission select and panel files.
The mug meter on screen¶
Moved to HUD function index: message, mission select and panel files.
After GUI/MissionSelectHUD.cpp (no path string): bars and counter panels¶
Moved to HUD function index: message, mission select and panel files.
Before GUI/RadarHUD.cpp (no path string): the generic bar¶
Moved to HUD function index: message, mission select and panel files.
After GUI/RadarHUD.cpp (no path string): item and score counters¶
Moved to HUD function index: message, mission select and panel files.
GUI/ScrollInHUD.cpp¶
Moved to HUD function index: message, mission select and panel files.
After GUI/ScrollInHUD.cpp (no path string): spray counter and the stereo-theft panel¶
Moved to HUD function index: message, mission select and panel files.
The stereo panel on screen¶
Moved to HUD function index: message, mission select and panel files.
After GUI/SubTitle.cpp (no path string): the tagging panel¶
Moved to HUD function index: message, mission select and panel files.
After GUI/TextEntryPad.cpp (no path string): text progress, stopwatch and hint texts¶
Moved to HUD function index: message, mission select and panel files.
GUI/TutorialHUD.cpp¶
Moved to HUD function index: message, mission select and panel files.
After PM_TooManyProfiles.cpp (no path string): the player panel base and the target panel¶
Moved to HUD function index: message, mission select and panel files.
After PM_TooManyProfiles.cpp (no path string): the Armies of the Night panel¶
Moved to HUD function index: message, mission select and panel files.
The player panel (no path string)¶
Moved to HUD function index: message, mission select and panel files.
Game-state functions¶
Moved to HUD function index: message, mission select and panel files.
Coney's implementation¶
src/hud/ is the HUD, platform-neutral, stepped on the fixed 1/30 s step with the game time in ms; src/platform/hud_layer.h
loads its sheets and draws it through the sprite batches and the 2D pass.
hud::Hud(0x001acee0): the panels, the hint box, the checklist and its scroll-in queue, the announcement, the counter panels, the arrow and the radars' state.update(HUD_Update) steps them;render(HUD_Render) draws in the render order with what hides what, nothing while hidden. A mode that stops stepping it (mode 0xb) draws it as it was left. Showing and hiding as the page says:levelSetUp(before the level's script, so a level starts hidden),hideAll/showAllkeeping each part's own flags, andupdaterunning the letterbox's restore mark (armed by the bars, stamped on the first step they are out,RestoreHudon the next) and the radars across a scene; under the bars nothing else steps. Coney's placement: the restore mark is the view's (0x0018d910), run inHUD_UpdatefromHudFrame::letterbox. Play's suspend and resume (GameplayMode) hide the HUD under a mode pushed over play and show it again unless player 1 holds a scene role;0x005e5580and game state+0x14care not checked.levelShutDown(HUD_ShutdownLevel,0x001ae980) runs fromGameplayMode::endLevel, the one teardown every way out of a level goes through (a debug jump, the story's next mission, a restart, leaving to the menus): every part the level set up or a script filled goes (the hints queued and showing, the tutorial callback, the objectives and their messages, the announcements, the prompts, bars, counters, stopwatch, mini-game panels and the radar's blips), so no hint or objective of one mission survives into the next. The level's start (GameplayMode::enter) also empties the animation callbacks.repo:tests/gamemodes/story_start_test.cppchecks every start path: a debug jump, a reload, the story's next mission, the pause menu's two restarts and a direct start (--play-level). After each, no hint, objective, tutorial callback or animation callback remains, and a text set on the new level's box shows at once.repo:tests/platform/disc_level80_story_test.cppchecks the same on the disc, jumping from the story throughlevel80's four checkpoints. Coney's choice: the game-state switches the HUD object holds for Coney (the game's tutorial hints,W_GameState + 0x56e2; the first-objective hint's flag), the shown flag and the spinner are kept, as they are not the level's; the nextlevelSetUphides both anyway.hud::PlayerPanel: the layout, the banner by type, the meter (caps and strips ofpart_page054-57, one-texel inset, the full meter's pulse and sound,FlashRageBar), the score and money counting by a sixteenth plus one with their popups, the counters and their slots, and the 2 s hold and 1 s fade. Activity: a changed value, SELECT, the force-show flag. The parts land within a pixel of the page's 640 × 448 figures (tests/hud/player_panel_test.cpp).hud::HintBox: the hint queue of 20 sorted by priority, interrupt and restart, a hint's time only from its<DISPLAYTIME>(else until flushed), the box laid out from the table and following the text's fade.ScrollInQueue,CounterPanels, the announcements and the action prompt: the objectives' messages at (0.025, 0.88); the five panels; the bottom-left and centred announcements, each replaced at once and timed by its markup; player 0's prompt at (0.5, 0.86) raised over the hint box or message. Nothing draws whileHudFrame::letterboxis set. Differs from the original (issue #238, Objectives): mode 2 queues the heading and then the bare text as two messages ofmseach, where the original queues nothing, flushes the objective's own message and, for slot 1 only, shows announcement 2; the queue never cuts the showing message short when a new one arrives and has no flush; mode 0 puts<CR>between the heading and the text although the heading string already ends with<CR3 0.01>; the icon template has no<MONEYFONT>and no trailing space; and a text's leading icon tag (<ROBJ_N>and the rest) is not read. On the player's path, a completed objective's script runs mode 2 and then mode 0 for the next objective, so Coney shows "New Objective" with no text and then the completed text alone, about 8.5 s each, before the new objective's message.- The bindings (
src/scripting/hud_bindings.h): every HUD bindinglevel99calls, throughBindingContext::hud.HUDSetTutorialCallbackkeeps the name; the play mode calls that function after the level's step with the anim id of each hit player 1 struck in it, landed or blocked (Fighter::strikes). Coney's choice: called after the step, not from inside the damage step. - Sound: the cues by the table
SoundCfgInterfaceSoundfills (the binding keeps each call for the HUD as well as filling the engine's table), and the named sounds, play throughaudio::SoundPlayer(Sound); silent without sound. A play returns the player's handle (HudSound), so the money count's looping cue0x10is kept while the count runs, started again if it was lost, and stopped when the count ends (PlayerPanel, Sound events: the money count's sound). - The action prompt (
repo:src/gamemodes/gameplay_mode.cpp, the level's partPlayLevelMode::promptOffer()): each frame of play player 0's text is chosen inHUD_Update's order: nothing while he tags, plays a scene part, mugs, steals a stereo or picks a lock; a held human set up for interrogation givesGSTRING.HUD0 (interrogate, whatever he carries), one with money or a pocket item who may be mugged givesGSTRING.HUD1; else a knocked-out partner to revive gives 4 (GameplayMode::revivableInReach(): the nearest revivable human friendly to him within 3 m, knocked out (his health gone or a script's), not cuffed, in sight, while player 1 holds a flash; the states0x80000000and0x100000000are not modelled); else the context records by kind: an action object's own text (1), a pickable door 15 (2), a freed car stereo 16 (3), a dealer's offer (4: his type's prompt, 6 for the flash, while his goal registers it, from the greeting on, within 1.75 m). A prompt naming the dealers' goods wakes the player panel. Last, a talkable Warrior or co-op partner within 1.5 m (GameplayMode::talkPrompt()) gives the swap string his brain's think set (AI:GSTRING.HUD0xc-0xe while one of the two holds an object), else his own text (ScriptState::talkText), else HUD string 9; triangle swaps (AI). The action object's second text (SetMsgHandlerEx'sprompt2) is queued as a priority-0 hint while it is in reach and withdrawn when it changes or leaves. - The prompt's button (user item 9, "n Give Weapon", fixed): the layout (
repo:src/gui/text_layout.cpp) gives<MONEYFONT>its rectangle base (MONEYFONT), so the<ST>ofGSTRING.ACTIONBUTTONdrawspart_page0rectangle 73, the triangle; the stick tags alternate their characters and the icon tags 23-31 are known (repo:src/gui/markup.h). Tests:repo:tests/hud/hud_test.cpp(a prompt starting with the action button),repo:tests/gui/text_layout_test.cpp, and the disc check"[disc][text]", which finds the triangle in all three swap prompts (0xc-0xe) and a stick picture in the revive prompt 4. - The prompt beside a downed Warrior (user item 8, "no revive prompt"): Coney's revive step read only a script's
knock-out, so a Warrior downed in a fight (his health gone) gave no prompt; it now counts both, a flash is still
needed, and d-pad left revives him (Combat: revive). He must also be revivable (flag
0x4), which every Warrior is from the moment he is made (Human_Init, Combat: human flags), in Coney too (ai::startingFlags). The disc testrepo:tests/platform/disc_revive_test.cppdowns alevel80Warrior with a strike (no script call), walks player 1 to him with the stick at 60 % and sees no prompt without a flash, string 4 with one, then the revive. The swap prompt (repo:src/ai/swap_prompt.h) has the blocked test, for the Warrior and the player alike, as far as Coney models the mask's states (knocked out, by health or by a script, arrested, wounded), so beside a downed Warrior no "Give Weapon" shows. A Warrior's brain that a script knocked out is off in Coney (BrDead), so its think clears the prompt instead (Brain::think). Still to do: the player's own knocked-out gate on the other prompt steps. - The stereo-theft panel (
repo:src/hud/stereo_hud.h, stepped byPlayLevelMode::stepStereoPanel()) as the stereo panel on screen says: up from the triangle press, the arrow round the backdrop's corners by stage, the gauge sized and turned by the angle turned and dropping by a doubling step while a stage is complete, the stick's four pictures and the turning ring, always mirrored (it never flickers); cue0x22per stage and0x23with the fourth; gone as the end clip starts. Turned sprites (the format-1 instances: this panel and the instruction arrow) go throughSpriteBatch::addSprite(sprite, rotation), a positive angle turning clockwise on screen as the original's widgets do. Coney's stand-in: the stick and ring are timed on the game clock, not the timer0x0050b8b8. - The mug meter (
repo:src/hud/mug_meter.h, stepped byPlayLevelMode::stepMugMeter()) as the mug meter on screen says, while player 1 mugs (mode 0): the twopart_page0bars, the stick's ball and dot following the raw left stick, the three arcs rippling two updates each at the last direction the stick picked while it is on target, andGSTRING.HUD0x180(0x181for modes 1 and 3) above; while it shows, the scroll-in message, the hint box and the action prompt are hidden. Coney's stand-ins: bar 1 is the off-target time against the allowance (record+0x0c, what fails a player's mugging in Coney) instead of the time since the start against+0x134. Coney has no modes 1-3 (a player victim and the hold), which need two players and the hold's states. - The lock-pick dial (
repo:src/hud/lock_pick_hud.h, stepped byPlayLevelMode::stepLockPickDial()) as the dial's layout says, while player 1 picks a lock: the three pins at the player's centre point at distance 1, turned by their angles (gone once every pin is done), then over every sprite the good and perfect wedges (whole rim vertices coloured, the next segment fading to clear) and the lock face, as 32-segment fans in 640 × 448 pixels with the video mode's horizontal and vertical radii (the interlaced 4:3 mode in play, as the rest of the HUD). The shapes go in two batches sorted above all the HUD's others, so they lie on top of the pins asShape2D_DrawQueueddoes. - The fixed-camera icon (
repo:src/hud/fixed_cam_icon.h) as the icon says: while player 1's camera is a locked one or camera switch 0 is off, pushing the right stick (a raw byte outside 64-176) showspart_page0rectangle 87 at (0.9, 0.64), fully opaque while held and fading over 1 s after; any other camera hides it.HUDEnableFixedCamIconturns both players' off until the next level's set-up. Coney has no fixed, transition or rail camera, so only the locked camera and the switch show it. - The tagging panel (
repo:src/hud/tag_hud.h, set byGameplayMode::updateTagPanel()) as the tagging panel says, while player 1 sprays: the path's dots but the last three, grey from 100 to 255, the painted cells in bytes 0-2 of his tag colour (HuTagColor; by default the dark maroon (80, 7, 5)) at alpha 205, the cursor between cells plain white while on track and fading white to black and back over 500 ms off it (and through a slip's pause, which keeps the flag), the charge bar (draining through pauses too) in the same colour shrinking down to its fixed bottom, and the frame at alpha 191; gone when the spray ends. - The gang-count indicator (
repo:src/hud/num_indicator.h) as the indicator says: the shared one (HUDSetNumIndicatorplayer 2), in a level numbered 100 or more, draws the blue header (sheet-table record0x20e, French0x20f, German0x210) with its two black shadows at (0.06, 0.944) and the gang's standing members as tally marks from x 0.125, turned 3.3 rad, recounted each update; German's layout moves both. Its count is the standing members,Gang_CountLiving(gang, 1)(hudGangCount(),repo:src/ai/gangs.h; what the count counts), where dead and dying are Coney's no health left. Coney's readings: at most nine marks; the shadows sort at the banners' shadow depth. Players 0 and 1's go to their panel tallies (PlayerPanel::tallyMarkPlace()): the same marks from the panel's base in the rage colour at the panel's fade, dropped below the money and counters. - In play: the play mode steps the HUD with player 1's rage, score, money and item counts (flash, spray paint,
handcuffs, keys from the inventory) and draws it over the frame; the story shares the
flow's HUD with the scripts. A disc test (
[disc][hud],repo:tests/platform/disc_level99_hud_test.cpp) playslevel99checkpoint 1 throughl99_c1unskipped: hidden and nothing drawn under the bars, then shown, drawn and the first hint up, and a pause hiding and showing it. The debug menus' HUD page sets its values (Debug menu).
The mash meter (repo:src/hud/mash_meter.h), as the mash meter says:
player 0's or 1's button sprite, the bar from its left end filled by the mash over its target and the L1 and R1 glyphs
alternating every 400 ms (the triangle blink for any other word); while one shows, the scroll-in messages, the hint box
and the prompts are hidden. The uncuffing shows it (Crimes). Coney's reading: the
bar's back runs its whole width with the fill over it from the left end and the caps outside.
The Warrior command menu (repo:src/hud/war_command_display.h, stepped by
repo:src/gamemodes/gameplay_war_commands.cpp), as the menu says: each frame of play, before
the level's step, pad 1's R2 release gives the highlighted slot's command to the dispatcher (unforced, through
script::giveWarriorCommand) and R2 held opens the menu for player 1 unless his menu is locked; the right stick's raw
bytes pick the slot (the 12,100 dead zone, the one-axis angle, the sectors, 11.25° of hysteresis, cue 0x20); a lock
arriving while it is up ends it with no order, and a hidden HUD gives the order at once; the camera's right stick is
off (FollowCamera::enablePadStick) from the opening until 10 updates after the order; then the chosen plate fades
over 1,500 ms and the rest over 500 ms, the chosen slot blinking, until the name's <DISPLAYTIME> closes it. It draws
the six slots and the highlighted slot's GSTRING.COMMAND name (entry 7 for a disabled command) at x 0.5.
Coney's readings: player 1 is the war chief (+0x3ac is not kept) and his state flags do not hold the menu back; a
stick byte of 128 or more is right or down; the backing and plate are flat squares (the sprite word 0xd0100 is not
mapped); the order is also given when play resumes under a closing pause menu (Coney's reading: any mode popped
over play) and the menu closes when the chief is knocked out; not built: the two-player and 16:9 layouts. A script
drives it as a player does: press r2, stick right -70 70 (up-left, attack), release r2
(repo:tests/hud/war_command_display_test.cpp).
The health rings (repo:src/hud/health_rings.h, drawn by repo:src/platform/play_level_world.cpp), as
the health rings say: each step after the HUD's, hud::HealthRings takes player 1 and
his target and queues the rings (nothing while the HUD is hidden: HideHud, a scene's start); the show list (SELECT
newly pressed, a fight stance or health at or below 20 % renewing it; 4 s hold, 0.5 s fades), HuForceEnableReticule
(GameState::forceReticules) at alpha 255, the target's outer ring fading in over 500 ms and the L1 marker under a
non-player target; the arcs from health, power or raw rage × the power class byte (PowerClass::ringByte), the colours,
the blink at or below 25 %, the rage-full flashes and the raging grey, the hit pulse (damage this step, 45 an update, at
most × 1.15), the start angle from the camera's heading. They are drawn after the blob shadows, each ring under where
its human is drawn this frame, as 64 flat-coloured triangles of part_page1 rectangle 1 with fog off. A disc test
([disc][objects], repo:tests/platform/disc_level99_world_test.cpp) checks that SELECT brings up player 1's two rings
after l99_c1.
The radar (repo:src/hud/radar.h, drawn by repo:src/platform/hud_layer.cpp), as
the radar on screen says: GameplayMode gives the HUD the level record's map (the
world name's sheet and arguments 13-15) and a locator for the blips' objects; the play mode gives each step player 1's
feet, heading and speed and the camera's heading. The disc is 32 segments of triangles over the whole map texture,
clamped as the GS dumps show (the sheet's own flags say wrap) and with no alpha test (the GS's test
there keeps the failing pixels' colour), filled to 0.825 R in (191, 191, 191, 240) and fading to R, drawn under every
other HUD batch, and nothing without a map; the zoom eases between HUDRadarSetRange's radii by the speed and
HUDSetRadarZoomScale; blips sit at (right, forward) × 0.12 / zoom (on the edge beyond it), sized as hud_radar_dot's
particles (0.7 × each icon's factor), white but the dealers' green icons 29-31, a new objective blinking for 100
updates; the player's arrow (icon 362, 0.03 wide, (178, 178, 178)) turned by his heading less the camera's, drawn as
two triangles. Coney's stand-ins: w = 1.33 (the measured disc) for the camera slot's value; rest 50 and fast 75
until a script sets them; the disc's state set by Hud::setRadarTint (blue while player 1 may hide,
Stealth) and blended over 500 ms; enemies and police (types 6, 8) never show, as no scanner marks
them yet; HUDAddRadarHuman makes every human a Warrior's blip (type 7, icon 365); a blip whose object the locator
cannot find is skipped, not freed. Tests: repo:tests/hud/radar_test.cpp.
The radar frame (repo:src/hud/crime_panel.h), as the radar frame says:
player 0's four arcs concentric with the disc, flat-coloured strips of 16 segments 54-57.6 pixels out (x 1.1 times), the
blue wanted pair growing up both sides from the bottom by the fraction of 10 s left on his gang's wanted time (full
above 0.9), eased 5 % an update and snapping on a jump over 0.5, drawn above 0.03; the orange second pair outside it (or
in its place) from the second timer (CrimeReports::secondWantedFraction, started by a gang's call for help). Coney's
placement: the easing runs in the HUD's update, not its draw. The arcs show only once the crime reports know player
1's gang (CrimeServices::playerOneGang, Crimes). The crime message
(Hud::setWanted): the crime report sends 7 and the wanted time's end 0xb; 7-9 sound the alarm (interface cue 2) on
the first show of a wanted spell and copy a crime text other than the one held into the centred announcement; any other
message clears the flag and the held text, leaving the copy to its own display time. In play the level's crime services
pass player 1's messages on with GSTRING.CRIME (CfgCrimeMessage) of his last crime type (GameplayMode).
The spinner (repo:src/hud/spinner.h), as the spinner says: part_page0 rectangle 92, 0.09 high,
upright, at (0.95, 0.83), drawn after the arrow while shown in the colour the last loading pulse left (the set-up's
grey before any); Spinner::drawPulse is the pulse, (221, 56, 56) fading out over 1.1 s and back over the next.
Coney's stand-in: the pulse runs on its caller's clock. The memory-card screen at start-up turns it on and pulses
it (Level loading); a level's set-up (Hud::levelSetUp) turns it off again,
Coney's stand-in for the fade branch on the level's first fully faded frame. Coney has neither the preload indicator
nor the HUD's fade branch itself.
Coney's stand-ins for the rings (marked in the code): the blend state the world pass leaves is taken as alpha
blending with Z test and no Z write; flat shading gives a triangle its last vertex's colour; the blink runs on the game
clock, black first; the fight stance is a lock-on or a block; a hit's pulse is the health lost in the step; the
civilian's class byte is Rembrandt's 35; the camera's heading is that of its forward. A boss's outer ring (a fighter
whose class +0x11b is 13, outside a Rumble level) shows the three bands (hud::bossBand; Coney's reading: the
band's percentage is the health's, scaled by the class byte as the others). Not built: the Rumble team disc and
pointer, two players' rings and icons.
Wrapping (repo:src/hud/hint_box.h, wrapText) follows MessageHUD_WordWrap (0x001bac20,
GUI): a break before the word that takes the line past the width, each line measured with its tags
stripped (a button icon takes no width), a leading <AUTOINDENT f> giving the width. The hint box wraps at 0.74, the
scroll-in messages at 0.7 (0x0050ea50); Coney has one player, so the split widths (0.52) are not used.
Coney's stand-ins (marked in the code): text sizes read as the glyph height ((0.04, 0.05) as w × h, 0.05 as h);
the count 0.022 right of its icon; handcuff and key icons
part_page0 31 and 34; the money's icon a $; the popups' places; the money cue once per count; the built-in
announcements' texts from GSTRING.ANNOUNCE by kind; a <FREEZE> hint shown for its time (at least 2 s) as the game
timer does not freeze yet; the handcuff counter counts inventory item 5 (Human_GetCuffCount is not traced); a prompt
wakes the panel when it contains Spray, Flash, Blades or Give Mon in any language;
the counter panels' texts right-aligned on x 0.96; player 1's other parts 0.09 right of player
0's; a HUD no level has set up (the debug pages, the tests) shown at start; no wasted/busted restore yet (Coney has
no death camera). The hub's HUD bindings (repo:src/scripting/hub_world_bindings.cpp):
HUDEnableClubActionText raises the prompt to y 0.125 (the other video modes' heights are not used); the action-cycle
animation (HUDTurnOnActionCycleAnim) draws player 0's button (part_page0, 0.1 high, grey 128, inferred: the
prompt widget's colour) centred on the prompt's anchor, its word swapping every framesPerIcon updates and blinking
blinkFrames on and off, and hides the text until a new or changed one;
HUDShowMissionSelect and ShowGameStatsInterface reach the front end, which has neither screen yet.
The radar's range and the chase HUD (2026-10-06): HUDRadarSetRange keeps both players' near and far radii
(RadarState::nearRange, farRange; 50 and 75 until a script sets them); HUDSetChaseHUDState_DESTROY clears the
chase HUD's flag. The radar's zoom eases between the two radii (The radar). Coney's
stand-in: the chase HUD is not built or drawn (no level script creates it), so its flag is only kept.
Open questions¶
- The crew status list: the fighting-far-off icon (rectangle
0x15) was never seen at runtime; the knocked-out member's radar blip changed look at runtime, not traced; whether the player's own entry can ever show (the code does not exclude him). - Which character each name-banner sheet (records
0x1f-0x32) names, beyond Rembrandt (0x2f). - The handcuff and key counters' icon rectangles, and the counters' exact text offsets.
- What
0x00225ff0tests; a runtime look at a shown prompt (the alignment and the button glyph are answered: Action prompts). - Which event raises each of the game's own hints (the 19 callers of
HintBox_QueueGameHint). - The caption pager (HUD
+0x18e60, GUI): which script or mode creates it and whose command 1 steps the captions (0x001cb340). - Who sets HUD
+0x177a8(the centred announcement while hidden). - The radar: the active camera's slot
+0xc4third value (w, measured about 1.33). - The health rings: the shape of
part_page1rectangle 1 (the ring's band), what the power class byte+0x40is meant as, the blend state the world pass leaves, and a runtime look at a target's ring, the rage-full flashes and a boss's bands. - The target panel's two sprites and the caller
0x00210e48's conditions; a runtime look at the panel. - Human state flag
0x200000, which turns the banner blue-grey. - The layouts of the other video modes (16:9, progressive, PAL) that
0x00211ef8,0x001af010and0x001cdc80apply (the radar's are above). - The counters' text offset.
- The money's icon (rectangle and sheet) and where the score's and the money's popups sit.
- The size arguments of the HUD's texts: a glyph's height, or
(w, h), or aFont_Sizescale. - Whether the built-in announcements (
0x00622e20) areGSTRING.ANNOUNCE. - The counter panels' text layout and alignment, and a bar panel's look.
- Player 1's score, money and counter offsets.
- The scripted bars (above): which sheet and rectangles kind 0's sprite word
0x020a0000names, kind 3's bar size, and the depths of the chase gauge's three sprites (Coney draws kind 0 with the generic meter, kind 3 at kind 1's size and the gauge in one batch). - The objective header's string (Objectives): how
HUD_SetObjective(0x001dad88) assembles it. The icon tags draw as their rectangles only inside<MONEYFONT>(MONEYFONT), so the header must draw them there; whether the code puts<MONEYFONT>around the icon alone, where<SIZE 0.8>and</SIZE>sit, the gap between the icon and the heading (does string0xe5/0xe6start with a space, or is it the icon rectangle's width?) and whether slot 1's icon is also at 0.8 are not traced. Coney builds<SIZE 0.8><MONEYFONT><YOBJ></MONEYFONT></SIZE>/<MONEYFONT><BOBJ></MONEYFONT>(a marked stand-in, issue #235). - Mode 2's header string (Objectives, issue #238): whether the
completion header uses the same HUD strings
0xe5/0xe6as mode 0's "new objective" header or other ones (for example a "complete" heading), which of the two is picked by the slot, whether it takes the same colour slot (4 / 6) and size, and whether the interface cue0x11plays on it. Coney queues mode 0's own header, then the text, both forms; the user sees the header read as a new objective, so this is probably wrong.