Cars¶
Verified against: SLUS_212.15 (NTSC-U, SHA1 e9cb2cc49aa046b9e494313dce2f5038ed17b2f4), static analysis only
(Ghidra), and a disc check (2026-10-06) of the car models and the Object List, reported as counts, sizes and frame
positions, with Coney's renders of the models. Runtime: the colour (below) and, 2026-10-10, PCSX2 2.9.94, a copy of the
user's slot 6 (level99), the scenarios car_punch and car_glance (repo:research/traces/scenarios/): squares
and plain punches at the sedan in car slot 2, its parts' damage and its rock read each update
(What can hit a car).
Purpose¶
The parked cars scripts make with CarSpawn: their six types, the per-type record of boxes and parts, what the part
ids of CarRemovePart, CarSetPartOpen and CarSetPartDamage name, the paint colour of CarSetColor, and where a
car's model and textures are. The lists are in Cars; the car's task class and pool are on
Tasks.
In one paragraph: a car is a task of 0x1310 bytes (18 of them). Its initialiser matches the name against six type
names and keeps the index; the index selects a 0x5f0-byte record of constants that gives two physics boxes and 26
parts (body, roof, bumpers, bonnet, boot, panels, lights, four doors with their windows, four wheels). Parts can be
opened, damaged and removed, and removing a door removes its window. The model is a RenderWare clump named
<type>_geo with 47 atomics, found through the Object List of warriors.glr.
Original structure¶
Car.cpp holds only four functions at 0x00173170-0x00173928 (CarInstance, Source map); the car
task's code is at 0x00387498-0x0038e0f0, in TaskEngine/ (Source map, inferred).
Names are ours.
| Address | Name | Role | Evidence |
|---|---|---|---|
0x00387bc8 |
Car_Init(car, name) |
type index from the name, physics, the police lights, a handle | confirmed (code) |
0x00387d50 |
Car_MakeBodies |
two physics boxes from the type record | confirmed (code) |
0x0038c7d8 |
Car_RemovePartBits(car, part, on) |
marks a part (and its linked part) removed | confirmed (code) |
0x0038d020 |
Car_OpenPart(car, part, open) |
opens or closes a door, the bonnet or the boot | confirmed (code) |
0x0038df38 |
Car_SetColour |
CarSetColor |
confirmed (code) |
0x00389b40 |
Car_UpdateRender |
when dirty, recomputes the model's colour and transform | confirmed (code) |
Data¶
Types¶
0x00512ba8 holds six string pointers, in index order: car_osedan, car_coupe, car_wagon, car_copcar,
car_van, car_sullycar. Car_Init compares the name with each (strcmp, 0x00435ba0) and stores the first match's
index at +0x11e0; a name that matches none leaves the index unset. Index 3 also spawns part_copcar_lights at the
car (name at 0x0057e248) and keeps its handle at +0x1210. +0x12f0 is the CRC-32 of the name
(0x00143f68). Confirmed (code).
Type record¶
0x0057e4c0 + 0x5f0 × type, read-only data. Confirmed (code) for the reads named; the meaning of each field is
inferred:
| Offset | Size | Meaning |
|---|---|---|
+0x00, +0x10 |
vec3, vec4 | first physics box: size and offset (Car_MakeBodies); the sedan's 2.40 × 6.05 × 1.08 m |
+0x20, +0x30 |
vec3, vec4 | second box: size and offset (the sedan's 2.40 × 2.61 × 0.45 m, the cabin) |
+0xf0 + 0x30 × p |
26 × 0x30 | the parts |
A part record, p < 26:
| Offset | Meaning |
|---|---|
+0x00 |
side bits (inferred from the data: 0x02 front, 0x20 back, 0x10 and 0x01 the two sides, 0x04 top) |
+0x01 |
how it comes off: 0x10 at the first damage, 0x02 flies off from a hit, 0x04 flies off when blown off (What is drawn) |
+0x02 |
the part removed with it, 0xff none (Car_RemovePartBits reads 0x0057e5b2 + 0x5f0 × type + 0x30 × p) |
+0x03 |
0x01 on the doors, 0x10 on the bonnet and boot |
+0x10 |
the part's box, vec3 in metres |
+0x20 |
float: the restitution a loose piece bounces with (Physics); 0.1, 0.5 on the wheels |
The part ids with what we read them to be (inferred from the side bits and boxes): 0 body, 1 roof, 2 and 3 the front and back bumpers (2.36 m across), 4 bonnet, 5 boot, 6 and 7 front and back panels, 8 and 9 headlights, 10-13 side panels, 14, 16, 18, 20 doors, 15, 17, 19, 21 their windows (linked: removing door 14 removes 15, and so on), 22-25 wheels (0.76 m across).
The car object¶
Fields of the car task besides the task head, confirmed (code) at the functions named:
| Offset | Meaning |
|---|---|
+0x1a0 + 0xa0 × p |
state of part p; byte +0x96 bit 2 open, bit 3 changed (Car_OpenPart) |
+0x11e0 |
type index |
+0x11e4, +0x11e8, +0x130c |
the two physics boxes and their body (Car_MakeBodies) |
+0x1a0 + 0xa0 × p + 0x90 |
the part's damage, 0 to 1 (0x0038a4d8) |
+0x11f0 |
removed (hidden) parts, one bit per part (Car_RemovePartBits, a quiet explosion) |
+0x11f4 |
parts flying off as loose pieces (0x0038a010); Car_OpenPart refuses them |
+0x11f8 |
parts that are off: damage reached 1, or removed |
+0x11ec |
parts already hit once: a part's first hit plays its effect (Hit effects); cleared only by CarMakeGoodAsNew (Car_ResetParts, 0x003875e8) |
+0x1200 |
updates left before a car set on fire explodes (120 when a molotov hits, What can hit a car) |
+0x1204 |
the handle of its stereo (CarSpawnRadio), released when it explodes |
+0x1208 |
the handle of whoever last damaged it, scored when it explodes |
+0x1210 |
the handle of the police car's part_copcar_lights, ended when the roof (part 1) comes off |
+0x1220-+0x12c0, +0x12d0 |
the rock after a hit (The rock) |
+0x12d6 |
rocking; +0x12d7 counts the settled sub-steps that end it |
+0x12d8 |
s16, the gang that may damage it, 0x19 anyone (CarCanBeDamagedBy, 0x0038e030) |
+0x12fc |
the prepared vags/fire/car_explodes<n> sound |
+0x12d5 |
exploded (Car_DoExplode; Car_TryExplode refuses a car with it) |
+0x12e8, +0x12ec |
paint colour, two copies (Car_SetColour) |
+0x1300 |
the render object; +0x1308 dirty |
Model and textures¶
The car's model and textures are found by name through the Object List of warriors.glr
(WAD contents): the record whose name hash is the CRC-32 of the type name gives
the model <type>_geo (chunk 0x47, a RenderWare clump read by the same reader as other models, 0x0017f2c0) and a
texture dictionary. Disc check: all six models are clumps of 47 atomics and 48 frames, with no frame names; the
coupe and the wagon use car_osedan_tex, the police car car_copcar_tex, the van and Sully's car dictionaries whose
names are not recovered (hashes 0x5230bc4b, 0x3827d44a). Each model is in one standalone entry and 3-56 packs.
That the car uses this Object List record is inferred (the record's name and model hashes match; the lookup is not
traced).
The atomics (disc check of all six, the same layout in each): every atomic has its own geometry (47 geometries) of one untextured material, and its own frame; frame 0 is the root, frame 1 (the body's) sits on it at the origin, the parts' frames hang from frame 1, and each window's frame hangs from its door's. The models stand with z up and their front towards +y (the bumper of part 2 and the headlights at y ≈ +2.8 m on the sedan).
| Atomics | What | Evidence |
|---|---|---|
| 0-25 | part p is atomic p: the undamaged car |
confirmed (code) for the wheels: Car_UpdateRender places atomics 22-25 (0x16 + i) from the car's four wheel matrices at +0x70; inferred for the rest (each frame sits where the part's side bits put it) |
| 26-46 | the damaged form of part p (1-21) is atomic p + 25, on a frame at almost the same place |
inferred: Car_OpenPart turns atomics p and p + 25 together (0x0038cb70, a hinge turn of ±45° about an axis the low four bits of the part record's +0x03 byte pick); drawn alone they are the crumpled car |
The body (part 0) and the wheels have no damaged form; Car_OpenPart only acts on parts 4, 5, 14, 16, 18 and 20, so
p + 25 never reaches a wheel.
Placing the model (Car_UpdateRender 0x00389b40, each frame the car moved; confirmed (code)): the car's transform
(position and rotation, vtable +0xa4) becomes a matrix whose four rows are each turned from the game's axes into
RenderWare's, (x, y, z) → (x, z, −y) (the sign flip and pexcw word swap), and that matrix is set on the clump's root
frame (CarInstance_SetMatrix). The atomics keep their own frames under it, so each atomic's world matrix is its frame
composed up the parents (window → door → frame 1 → root) and then the car's. When the bytes +0x12d4 and +0x12d6 are
both set (inferred: the car is driven or simulated), the four wheels (atomics 22-25) instead get their own matrices:
wheel matrix +0x70 + i × 0x40 times the inverse of the car's matrix (CarInstance_SetPartMatrix); otherwise they keep
the model's wheel frames (inferred for a parked car). The same pass sets the render object's tint +0x24 from the car's
two colours (a blend over the update, Colour_LerpRatio). Disc check (sedan): frames 0 and 1 are identity; the part
frames carry their own turns (180° about z, 90° turns about several axes, e.g. frames 3, 15, 22), so a part drawn
without its frame's rotation, or with the rotation applied in the wrong order, comes out misplaced or turned.
How each atomic is drawn is in Graphics: drawing a car: culling off in both passes;
the six glass parts (mask 0x2a80c0) only in a second pass after the water with Z write off; the paint only on
the parts of mask 0x157c31 (0, 4, 5, 10-14, 16, 18, 20; not parts 1-3, 6-9, 15, 17, 19, 21 or the wheels); the
environment map or dual texture from the car's second texture resource.
Behaviour¶
Parts¶
- Remove (
CarRemovePart(car, part, on)→0x0038c7d8): withon, set bitpartin+0x11f0and+0x11f8, and the linked part's bit in+0x11f0; without, clear the bit. Scripts remove parts 5, 17, 18, 19 and 21. Confirmed (code). - Open (
CarSetPartOpen(car, n, open)→0x0038d5b8):n0-5 names parts 5, 4, 14, 16, 18, 20.Car_OpenPartdoes nothing for any other part, for a removed one or one marked in+0x11f4; it sets bit 2 of the part state and, once the model is loaded, calls0x0038cb70(a hinge turn, the atomics) for atomicpand, whenp≤ 21,p + 25. Confirmed (code). - Damage (
0x0038a4d8(amount, car, part record, part, instant, flag)): addsamountto the part's damage (+0x230 + part × 0xa0;instantsets it to 1); at 1.0 the part comes off (bit in+0x11f8). Callers: the hit handler0x0038bea0(0.34 per hit from a weapon or thrown object, 0.51 from a human with flag0x400000, 0.115 from other humans, on the parts the hit's zone selects),CarSetPartDamage,0x0038b0e8(mask0x2a80c0), andCar_DoExplode(0x0038ab50, every part, instant). Confirmed (code). - Boot item (
CarPlaceInTrunkOnDetach): when part 5 comes off through a non-instant damage call and+0x12e4is set,Car_ReleaseTrunkItem(0x0038d188) moves the stored object to the boot, or makes adyn_moneypickup (type 0x1c) holding the stored amount there. An explosion (instant) orCarRemovePart(bits only) never releases it. A player picking updyn_moneygets that much money (0x0023bf00, item 2). Confirmed (code).
What is drawn¶
Each atomic is drawn or not by the part it belongs to (0x00172940, 0x00172d60; atomic a > 25 is the damaged
form of part a − 25). Confirmed (code):
- a part in
+0x11f0(removed) draws neither form; - the undamaged atomic draws while the part's damage is below 0.5, and a wheel's (22-25) always;
- the damaged atomic (
p + 25) draws once the damage is 0.5 or more.
So a part swaps to its crumpled form at half damage and the body (part 0) never changes. The parts 6, 7, 15, 17, 19
and 21 (mask 0x2a80c0) are drawn in a separate pass (the render object's +0x41; inferred: the glass); a part
must also be in the mask +0x11fc (not traced). A part that comes off without being removed keeps drawing its
damaged form where it was, unless it flies off (below).
Coming off (0x0038a4d8, damage reaching 1): the part's bit goes into +0x11f8; a door (14, 16, 18, 20) taken
off by a hit also takes its window; part 5 releases the boot item (Parts). Then by the part record's byte
+0x01: 0x10 makes any damage take it off at once; 0x02 (a hit) or 0x04 (an instant removal, the explosion)
makes it fly off (0x0038a010): bit in +0x11f4, a lifetime of 360 at the part's +0x94, and a velocity
outward from the car (a hit: the record's side bits pick the direction, 3-6 m/s and a small spin; the explosion: away
from the car's centre and up, 12-16 m/s, a larger spin, with a sub_flaming_debris particle system on the piece).
A quiet explosion puts those parts in +0x11f0 instead (hidden, no piece).
A flying piece has no body of its own: Car_UpdateLoosePart (0x00387f18) falls, sweeps and bounces it each update
and counts the lifetime down, so a piece lasts 360 ticks (6 s); below 91 it sends message 0x15 to the object held
in +0x8c, and at 0 the part is removed (put in +0x11f0). Physics has the steps. Confirmed (code)
at that address.
Hit effects: glass, sparks and steam¶
A car's damage effects come from one shared sub_car_damage particle (made once by the car manager when its first
car is created, CarTaskManager_Create 0x0038e430, handle at manager +0x14, manager at 0x005971b4), sent
message 0x3f with a kind, a position and a direction. Its handler (SubCarDamage_OnMessage 0x003df730) makes
the effect at once. Confirmed (code) at the addresses cited.
When. Only Car_OnHit (0x0038bea0) and Car_OnLanded (0x00389f00, Physics) send it:
- Each hit that touches any part outside the glass mask (
s5 & 0xffd57f3f) sends kind 0 at the hit point (hit record+0x90), direction (0, 1, 0). - Then, for each hit part
p(bits 1-29 of the hit's part mask) not removed, flying or off, after its damage: the first time that part is ever hit (bitpof+0x11ec, set here; what clears it is not traced) the car callsCar_BreakWindow(car, p, hit)(0x0038a830), which picks the kind below and sends it. A part coming off later, a window's damage reaching 1,CarRemovePartand the explosion send nothing; only the explosion's flying pieces carry an effect (sub_flaming_debris, What is drawn).
Car_BreakWindow, by part (position and direction as sent):
| Part | Kind | Position | Direction |
|---|---|---|---|
| 4 (bonnet) | 3 | the point (0, 2.743, 0.023) in the car's frame (its rotation +0x20, position +0x10) |
(0, 0, 1) |
| 6, 7 (front and rear glass, inferred) | 6 | the part's damaged atomic (p + 25) world position |
its frame's second row, + for 6 and − for 7 |
| 15, 19 | 4 | the same | its frame's first row, negated |
| 17, 21 | 4 | the same | its frame's first row |
| 26, 27 (hit bits beyond the 26 parts; inferred: the headlights) | 1 | the hit point | (0, 1, 0) |
| 28, 29 (inferred: the rear lights) | 2 | the hit point | (0, 1, 0) |
| any other | 0 | the hit point | (0, 1, 0) |
What each kind makes (0x003df730):
| Kind | Effect |
|---|---|
| 0 | a sub_car_spark_emitter at the position with strength 1.0 (so sparks, and rubble at 25 % per direction, Script types), and a dust puff (Particles_Dust) of size 1.95-3.0, colour 0x8b7d6920, at the position + a random ±0.5 m per axis |
| 1 | while the particle pool has room: miniglass pieces, each at the position + a random ±0.05 m per axis, given the direction, a size of 0.01-0.04 and colour 0 (the default pale shard); the loop runs while its index is below 4 + Random_Int(6), drawn again each pass, so about 4 to 10 |
| 2 | the same with sub_coloured_glass and colour 0x5c0a18ff (0xRRGGBBAA, a dark red) |
| 3 | one sub_car_steam at the position |
| 4 | one sub_glass shatter at the position with the direction, shard size 0.06 and size word 0x10001 (1 × 1 m): a small pane, so 10 glasstest shards of 0.06 and the GLASS_SMALL × GLASS_SMALL sound (entry 88) |
| 5 | one sub_hood_smoke at the position (from Car_OnLanded: (0, 1.84, 0.02) in the car's frame) |
| 6 | one sub_glass shatter, shard size 0.09, size word 0x10002 (2 × 1 m): 20 shards of 0.09 and the GLASS × GLASS sound (entry 2) |
The shatter, its shards, its camera culling (some camera within 15 m and the point within 10 m of a view) and its
sound are the glass pane's: Objects: the shatter. So a car window does make a sound when it
first breaks: the sub_glass shatter's material-pair sound, played before the culling. It has no sound of the car's
own. Confirmed (code) at 0x003df730 and 0x003e4be0 for the arguments; the shatter's sound choice from
Objects.
Explosion¶
CarExplode(car, quiet) → Car_TryExplode (0x0038ab18, nothing when +0x12d5 or +0x1200 is set) → Car_DoExplode
(0x0038ab50). Confirmed (code):
- Without quiet: particle systems
sub_fireball_emitterandpart_explosion_screen_shakeat the car; the car's sound (0x00110258with+0x12fc); object+0x38= 5.0 and the airborne flag (+0x540x4000000). - Every part 1-29 not already off, removed or flying: parts 1-25 take damage 1 at once (above, so each one
draws its damaged form, and those with record flag
0x04fly off, or hide when quiet); parts 26-29 are only put in+0x11f0and+0x11f8. Each one's bit also goes into+0x11ec. - Without quiet: whoever is in
+0x1208, when a player controls him, scores statistic0xbwith half the parts counted (at most 10), and+0x1208is cleared; a heat-wave effect (0x0019c1d0) 0.7 m above the car; the car is updated every tick; it sends itself message0x19(below); an AI event0x17to the humans within 30 m (0x002936a8); 300 damage to every human within 5 m with a clear line (Explosion_DamageHumansInRadius,0x00392638); glass broken within 10 m (World_BreakGlassInRadius, World objects). - Always: the first physics box shrinks 0.25 m in height and its offset rises 0.25 m (the wreck sits lower on
its collision);
+0x12d5= 1; the object the car holds at+0x1204(not traced) is released (its vtable+0x4c); flags of activities 13 (hubcap_idle) and 26 (write_on_pad_C) within the car's reach + 0.25 m and of activity 3 (wreck_idle) within 0.2 m are switched off (Flags_DisableNear).
So an exploded car draws its body, the damaged form of every part that did not fly off, and its four wheels; the pieces that flew off are drawn on their own until their lifetime runs out (inferred from the lifetime field).
Message 0x19. The record: message 0x19, subject the car, +0x00 = 0 (no other object), the short +0x04 =
−1, the short +0x06 = 1. The marshaller (Scripts) gives the callback
(car, NilHandle, -1, 1): n is the signed short, and the flag is pushed as the number 1 (+0x06 ≠ 0; the script
system's slot +0x54 pushes an unsigned integer as a Lua number), not a boolean. Confirmed (code) at 0x0038adfc
and 0x00384ce0.
Every sender of 0x19 (the only two; confirmed (code) at 0x0038bea0 and 0x0038adfc). The callback always
gets (car, other, n, flag): other the human (or NilHandle), n the signed short +0x04, flag the short
+0x06 as 1 or 0.
| When | other |
n |
flag |
|---|---|---|---|
| a hit damages part p (1-25) that was not yet broken (the hit, step 4), once per part | the human responsible: the striker, or the thrower or holder of the object | p | 1 if this hit broke the part (a window always; a door or panel when its damage reaches 1), else 0 |
that hit leaves every breakable part of the car's type broken (mask 0x3ffffe; 0x3fffe for type 1, 0x3fff7e for type 4) |
the same human | −2 | 1 |
an object of class 8 (a molotov, inferred) hits the car: it catches fire and explodes 120 updates later (+0x1200) |
the thrower | −1 | 0 |
the explosion itself (Car_DoExplode, step 3), unless quiet |
NilHandle |
−1 | 1 |
A hit with no human behind it (an object nobody threw or holds) sends no per-part message. Parts 26-29 send none.
The same hits also send message 6 to the volume boxes around the human, with the car as
the object. So level34's riot meter (3 points per call with flag 1) counts each part a Warrior breaks, the
all-broken call and the explosion; level5's car bonus counts 15 broken parts or the explosion (n = −1).
Who hears it. The car's message slot (vtable 0x00544c08 +0x44, 0x00389700) only forwards to the car
manager's table (0x00512c7c + 0x844, 0x0038ebf8): the callback SetGeneralCarMsgHandler stored for that number
(+0x18 + 4 × message) runs with the car as self. A car has no handler component: its vtable +0x3c (get)
returns 0 and +0x34 (set) does nothing, so SetMsgHandler on a
car takes a component from the pool, keeps the name in it and is never called. Only the general handler hears a car's
messages. Confirmed (code) at 0x00389700, 0x003860b8, 0x004da1a8.
Windows, hits and the stereo¶
A car's windows are its parts 15, 17, 19 and 21, not glass panes: no code makes a pane for a car (the only
caller of the pane spawn 0x0039c0e0 is SpawnBreakableGlass, 0x00378fa0), and level99 places no type-12 pane.
Glass type 12's stereo rule (A pane's life) only serves panes a script places itself. Confirmed
(code) at the functions cited.
The windows. Each window part's record (+0x01 = 0x18) has bit 0x10, which makes any non-instant damage set
the part's damage to 1 at once (0x0038a4d8): one hit breaks a window. Parts 14, 15, 18 and 19 have side bits
0x10 and lie on the car's −x side (left, with the front at +y); 16, 17, 20, 21 have 0x01 and lie on +x. The
zone tables below put 14/15 and 18/19 at x ≤ −0.6, and the burst of a breaking window 15 or 19 is aimed along −x of
its frame (0x0038a830). The sedan's window boxes (part +0x10) are 0.44 × 0.96 × 0.25 m (15, 17) and 0.44 × 0.89 ×
0.32 m (19, 21); where each sits is its atomic's frame in the model (Model).
Hit zones. Which parts a hit reaches comes from where the hitter stands, in the car's frame (x across, y along,
from the car's position +0x10 and quaternion +0x20). Two tables per type, both read at 0x0038b8d0 and
0x0038b520; offsets are from the type record:
- Body: the band of y among the five falling thresholds at
+0x40(0 above the first ... 5 below the last) and the column of x against+0x54= −0.6 and+0x58= 0.6 (0 at x ≤ −0.6, 1 up to 0.6, 2 beyond) pick a u32 part mask at+0x74 + 4 × (3 × band + column). - Cabin, only while |y| < half the second box's length (
+0x24): the band among the five thresholds at+0x5cand the column against+0x70= 0 (0 at x ≤ 0, else 1) pick a mask at+0xbc + 4 × (2 × band + column); it is ORed in. - Bit 1 (the roof) is always dropped.
The sedan (car_osedan, type 0), as parts per band, x ≤ −0.6 / middle / x > 0.6 (confirmed (code) from the data):
| Body band (y) | Parts |
|---|---|
| ≥ 2.716 | 2, 8, 10, 26 / 2, 4, 8, 9 / 2, 9, 11, 27 |
| 1.028 to 2.716 | 10 / 4 / 11 |
| −0.034 to 1.028 | 14, 15 / 14-17 / 16, 17 |
| −1.597 to −0.034 | 18, 19 / 18-21 / 20, 21 |
| −2.778 to −1.597 | 12 / 5 / 13 |
| below | 3, 12, 28 / 3, 5 / 3, 13, 29 |
| Cabin band (y) | x ≤ 0 | x > 0 |
|---|---|---|
| ≥ 1.207 | 6 | 6 |
| 1.007 to 1.207 | 6, 15 | 6, 17 |
| 0 to 1.007 | 15 | 17 |
| −1.007 to 0 | 19 | 21 |
| −1.207 to −1.007 | 7, 19 | 7, 21 |
| below | 7 | 7 |
Bits 26-29 have no part record (the hit handler only marks them removed); 6 and 7 are hit only from the cabin table,
which reads as the windscreen and the rear window (inferred). The other types share the masks with their own
thresholds (+0x40 / +0x5c): the coupe's rear body band repeats its two doors (14-17), the wagon adds 12 and 13 at
the back, and the van's masks differ more; dump them by type when needed. The sedan's box is 2.40 × 6.05 m (+0x00),
the cabin 2.61 m long (+0x24).
Targeting a car (Player_PickTarget, 0x0027a6c0, Combat). The car pass (world +0x844,
0x0038e860 with reach 1.0 and filter 0x00279f50) comes after the human passes and before the objects (+0x840)
and glass (+0x84c). Confirmed (code):
- A car is a candidate when the human is less than 1 m outside its box along both its x and y (|x| − half width < 1 and |y| − half length < 1), scored by the smaller of the two, squared.
- The filter
0x00279f50: a human standing within the box's footprint (0x00279e00, i.e. on the car) needs the car 0-2 m below; any other needs the car within 3 × 54° of the search heading (0x002790a8) and within 2 m in height (0x00510970). - The aim point (
0x0038c990): when the car's position is below the human's feet, the feet − 0.25 m; otherwise the parts the human can hit from where he stands (0x0038b520), less the removed ones (+0x11f0,+0x11f8,+0x11f4), must be non-empty, and the point is 1 m ahead of the human (his facing × (0, 1, 0)), at his feet + 1.5 m when those parts include a window or 6 / 7 (mask0x2a80c0), + 1.0 m for the bonnet or boot (0x30), else at the feet. So a window target sits 1.5 m up, andPlayer_ObjectAttackplays 662 for it (Breakables). 0x0038b520takes the nearest face of the box from outside it (±x beside the car, ±y before or behind it, the corner's direction diagonally) and returns parts only when the human faces that face (his forward · the face's outward normal ≤ −0.7); then the zone tables above, from his position.
The hit. A human's strike that touches a car does not send message 1. Strike_Contact (0x0021b290) finds the
struck object is a car (0x00389848: its type word has 0x100000) and calls the car's hit handler directly (vtable
0x00544c08, {delta, fn} at +0x100 / +0x104: 0x0038bea0), which returns the parts it hit. Confirmed (code).
The handler:
- Ignores the hit while the car is exploding (
+0x12d5,+0x1200); a car whose s16+0x12d8is not0x19takes hits only from members of the gang with that id (gang+0x2c). - Picks the parts: for a plain human hit (record
+0x08without0x400000), from where the attacker stands (0x0038b8d0, the tables above, no facing test; a player whose feet are above the car's position takes the cabin table in body bands 1-4 whatever his y, instead of only within the cabin's length); for a human with0x400000(the charge, inferred) and for thrown objects, from the contact point (+0x60) in the body or cabin table by the physics box struck (What can hit a car). Thrown objects drop the windows from body hits (0xffd57fff) and the doors from cabin hits (0xffeabfff). - For a plain or object hit, an intact window protects its door: with 15, 17, 19 or 21 in the mask and not yet
broken (kept bits
+0x11f8), its door (14, 16, 18, 20) is dropped from this hit. So the first square at the front-left door breaks window 15 only; later ones dent door 14. - Damages each part (0.115 a plain human hit, 0.51 a
0x400000hit, 0.34 an object; the windows break at once), broadcasts message0x19per part, plays a part's first-hit effect (0x0038a830: windows 15/19 effect 4 along −x, 17/21 along +x) and reports the 30 m noise (AI_ReportNoise,0x002936a8) and message 6 to the volume boxes around the striker (VolumeBoxes_SendDamageMessage,0x00413018).
Strike_Contact then counts a player's car hit (statistic event 0xb) when it reached a part not already broken.
The stereo (CarSpawnRadio → 0x0038c868, once per car, handle at +0x1204). The dyn_carstereo record is
added to the world objects (+0x840) at the car's position + its rotation × (−0.75, 0.25, 0.1): the vec4 at
0x0057e4a0 + 0x5f0 × type, the same for all six types. Its object flag 0x8000 (pickable) is cleared, so it
cannot be taken yet. When part 15 reaches damage 1 (0x0038a4d8), the stereo gets 0x8000 back and its
virtual at +0x124 with (3, 0, 0), the world object's context registration (0x00391c98 in the shared world-object
vtable 0x005453a0, Scripts), which registers it as a kind-3 context record
(Crimes): triangle within 2 m now offers the theft. Only window 15 frees it, the window
beside the stereo. Confirmed (code). The car's transform update (0x0038bb48)
moves the stereo to the same offset, and the boot item to the vec4 at 0x0057e4b0 + 0x5f0 × type (sedan and Sully's
car (0.00, −2.34, −0.01), police car (0.00, −2.26, −0.02), wagon and van 0, the coupe (0.00, −2.34, −0.05)) while
the boot is shut. Both vectors are 0x20 and 0x10 bytes before the type record as this page counts it. The cars of
level99 stand at z 1.04-1.09, so the origin is about 1 m above the ground and the stereo about 1.15 m up.
What can hit a car¶
A car is struck through its physics body, the two boxes of the type record (box 1 the lower body,
box 2 the cabin, Physics): whatever touches one of them calls the car's hit handler Car_OnHit
(0x0038bea0, vtable 0x00544c08 +0x104). There is no other hit volume and no per-part shape: the part a hit
damages comes from the zone tables, never from the part's own box. Confirmed (code) at the addresses cited.
Who strikes, and what each does. Car_OnHit sorts the striker into four kinds by the hit record's object
(+0x00) and takes a damage per part, a rock strength (The rock) and a way to pick the parts:
| Kind | Striker | Damage a part | Rock | Parts from | Then |
|---|---|---|---|---|---|
| 3 | a human, any strike (punch, kick, the square's object attack) | 0.115 | 2.0 | where he stands (Car_GetHitZone, the zone tables, no facing test) |
an intact window shields its door |
| 2 | a human with held flag 0x400000 (the charge or dive, inferred) |
0.51 | 4.0 | the contact point, always the body table, column 0 at x ≤ 0 else 2 (no middle) | no window shield: door and window together |
| 1 | a world object someone holds (a melee weapon) | 0.34 | 3.0 | the contact point, the body or cabin table by the box struck | only the parts on the holder's side; each part once per swing |
| 0 | a world object nobody holds (thrown, rolling) | 0.34 | none | the contact point, as kind 1; the body table drops the windows (0xffd57fff), the cabin table the doors (0xffeabfff) |
an intact window shields its door |
- A human's strike reaches the car through the strike test:
Human_TestStrikes(0x0033f110) tests the clip's enabled strike shapes against every body near the human, the car's body included (an object's body is tested against its own shapes, here the two boxes), and the first overlap callsStrike_Contact(0x0021b290), which sees a car (Car_FromObject, type word0x100000) and callsCar_OnHit. So any punch or kick whose hand, forearm, foot or shin overlaps either box hits the car, whether the car is the square's target or not; the square's object attack (661 / 662, Breakables) is only the way the player aims at it. Once struck, the body goes on the striking body's contact list and is not struck again until the shapes go off. Confirmed (runtime) (car_glance): the player 0.14 m beside the sedan's right side, facing 55° away from it (so the car fails the target's facing test), pressed square: the plainS1(clip 12) broke window 21 five updates after it started, and a secondS1reached door 20 (its first-hit bit); the car rocked both times. Standing at the car's y = −0.08 m, just behind the −0.034 threshold, both hits took the rear door's zone (20, 21), not the front door beside his hand. - The parts come from the striker's feet, not from the hand. For kind 3 the contact point is not used: standing beside the front door, every hit reaches the front door's zone wherever the fist lands. The square's aim point (Targeting a car) only decides the clip (661 at the feet, 662 at 1.5 m for glass).
- A charge (kind 2) takes no middle column: the contact point's x picks the left (x ≤ 0) or right column of the body table, whatever box it struck. It damages a door and its window together.
- A held weapon (kind 1) keeps a mask of the parts this swing has already reached in the holder's
+0x568(one bit per part; set to −1 when the car is exploding); the first time a swing reaches any part it reports the 30 m noise and message 6. Of the zone's parts it keeps only those on the holder's side: when the holder's x along the car's first axis is below the contact point's, the +x parts (9, 11, 13, 16, 17, 20, 21, 24, 25, 27, 29) are dropped (0xd7ccd5ff), else the −x parts (8, 10, 12, 14, 15, 18, 19, 26, 28;0xebf32aff). - A molotov (an object whose attribs kind
+0x86is 8) damages nothing. It makes the0x0057e350particle system at the car (direction (0, 0, 1)), sets+0x1200= 120, preparesvags/fire/car_explodes<n>(n1-3) at the car into+0x12fc, sends message0x19(car, thrower, -1, 0), and for a player thrower keeps his handle in+0x1208and counts crime 11 (Stats_AddCrime(…, 0xb, 1)); then the 30 m noise and message 6. The car's update counts+0x1200down once per update and callsCar_DoExplodeat 0 (Explosion). - The gang lock: a car whose
+0x12d8is not0x19ignores a striker (or holder, or thrower) whose gang (brain+0x20c→+0x2c) differs.CarCanBeDamagedBy(car, gang)(lua_CarCanBeDamagedBy0x00378790→0x0038e030) sets it. - The contact point (hit record
+0x60, x and y) is read against the type's thresholds without any transform. It is the point in the struck box's own frame, along the box's axes from the box's centre (not the car's origin), and+0x90holds the same point in the world, which the rock, the dent sound and the window-break effect use. Confirmed (code) at0x0034bbe8and0x003453f8(Physics: Unit 5); so a cabin-box hit's bands are measured from the cabin box's centre (inferred). Bit 1 (the roof) is not dropped on this path: a thrown object, a weapon or a charge reaching a cabin band whose mask holds 1 takes the roof off at once (its record flag0x10), and the roof coming off ends a police car'spart_copcar_lights(message0x15, then its vtable+0x4c;Car_AddPartDamage0x0038a4d8). Confirmed (code); not seen at runtime. - After the parts, for each part 1-29 reached, not removed, flying or off: parts 1-25 take the damage
(
Car_AddPartDamage), parts 26-29 are only marked removed and off (+0x11f0,+0x11f8). A part among 4, 5, 10-14, 16, 18 and 20 whose damage crosses 0.5 in this hit (its dent) playsSound_PlayMaterialPair(1, 129 CAR_DAMAGE, 129 CAR_DAMAGE)at the hit point (Sound events). Message0x19and the first-hit effect follow as Explosion and Hit effects say. The handler returns the parts reached outside the glass, andStrike_Contactplays the impact sound (9, or0x1afor a charge) on material 13 when it returns any, at volume 0.5 for a player, 1 for an AI human. - What the hit gives the player.
Strike_Contactcounts crime 11 for a player when the hit reached a part not yet off (and for every player when the striker is a Warrior whose gang leader is a player). A held human slammed into the car (0x1400000, the throw into an object) takes 100 pending damage (one less than his health when he would die and the state flag0x800000000is set), the camera shakes, and his thrower gains style 3 and rage. The car pays nothing itself: its money is the boot item (Parts) and the stereo (Windows, hits and the stereo, Crimes). There is no car alarm: the only alarm sound (vags/ambient/alarms/alarmbell_loop,alarm_emitter) belongs to scripts' sound emitters (Sound_AddScriptEmitter), and no car code plays it. Confirmed (code).
Runtime: squares at a door (car_punch, confirmed (runtime)). The player held 0.44 m outside the sedan's right
side beside its front door (the car's x = +1.64, y = +0.03), facing it, square every 30 to 60 updates (an attack
and its recovery take 60):
| Square | Clip | Lands (updates after the press) | Result |
|---|---|---|---|
| 1 | 662 (aim point 1.5 m up, window 17 intact) | 13 | window 17 off (damage 1, bit 0x20000 in +0x11f8 and +0x11ec); the door shielded |
| 2-10 | 661 (only door 16 left in the zone) | 10 | door 16 + 0.115 each: 0.115, 0.23, ... 0.92; the 6th square crosses 0.5 (the dent); the 10th takes it to 1: the door is off and flies (bit 0x10000 in +0x11f8 and +0x11f4) |
| 11 | 12 (S1) |
nothing left in the zone, so no car target: a plain punch, which reaches no part |
The cabin's mask (17) and the body's (16, 17) give the same parts here, as the tables say. Each hit rocked the car.
Without the feet held, each attack's recovery (clip 389) steps the player back about 0.5 m; after two hits he stood
1.1-1.2 m out, beyond the car target's 1 m, and the next squares were plain S1s that did not reach the car.
The rock¶
A hit of kind 1-3 whose striker's +0x568 is 0 makes the car rock (Car_StartHitRock, 0x0038b1f8, confirmed
(code)): a point under a 4 m spring is pushed down, and the car tilts by the spring's angle.
- From the car's position towards the hit point (unit
d), the pointp= position +dwith z = 0 (+0x1230), its velocity 0, a push of (0, 0, −strength) in its force (+0x1240: 2 a human, 3 a weapon, 4 a charge), and an anchor 4 m above it (+0x1290, z = 4.0). The tilt axis (+0x180) isd× (0, 0, 1), normalised. - Spring 0.05, damping 0.05, rest length 4.0 (
+0x1270,+0x1274,+0x1278);+0x12d6= 1, the car's rotation restarts from its resting one (+0x190), the car updates every tick, andvags/vehicles/shocks_squeak_0<n>(n1-3) plays at the car (volume 1). - Each update (
CarTask_Update,0x00388c80), six sub-steps: the force (the push only in the first) less gravity 0.03 / 4 in z, plus the spring alongp− anchor, −((length − 4) × 0.05 + (relative velocity · offset) × 0.05) × 0.5; the velocity gains the force / (4 × mass), the mass 0.8 for types 0-4 and 1.0 for Sully's car (0x00512b94);pmoves by velocity / 4. The rotation is the resting one turned about the axis by atan2(p.z, 4) × 0.05. When the vertical velocity has been under 1e-5 for more than four sub-steps, the rock ends.
Confirmed (runtime) (car_punch): p.z dips to −1.51 at the hit and −1.71 three updates later, swings back to +0.13
and settles at −0.30 after about 30 updates; the rock flag clears 47 updates after the hit. The tilt peaks near
1.2° and the car stays leaning about 0.2° towards the side hit (the quaternion's x and y 0.0015, −0.0011 long
after). The stereo and the boot item ride the tilt (their offsets are turned by the rotation each update).
Type tables¶
The zone tables and boxes of all six types, read from the type records (0x0057e4c0 + 0x5f0 × type), confirmed
(code) from the data. Box sizes are full sizes (Car_StartHitRock and Car_GetHitZone halve +0x00); offsets are
from the car's position. Every type has the body's x columns at −0.6 / 0.6 and the cabin's split at 0.
| Type | Box 1 (size; offset) | Box 2, the cabin | Body y thresholds | Cabin y thresholds |
|---|---|---|---|---|
0 car_osedan |
2.399 × 6.05 × 1.084; (0, 0, −0.419) | 2.399 × 2.614 × 0.452; (0, −0.281, 0.352) | 2.716, 1.028, −0.034, −1.597, −2.778 | 1.207, 1.007, 0, −1.007, −1.207 |
1 car_coupe |
2.14 × 5.27 × 1.05; (0, 0.094, −0.37) | 2.14 × 2.2 × 0.39; (0, −0.28, 0.35) | 2.271, 0.781, −0.7, −0.7, −2.271 | 0.52, 0.32, 0, −0.32, −0.52 |
2 car_wagon |
2.27 × 6.08 × 1.08; (0, 0, −0.4) | 2.27 × 3.6 × 0.42; (0, −0.72, 0.352) | 2.88, 1.027, −0.034, −1.039, −2.483 | 1.795, 1.595, 0.97, 0, 0 |
3 car_copcar |
2.48 × 5.98 × 1.11; (0, 0, −0.39) | 2.48 × 2.63 × 0.46; (0, −0.281, 0.39) | 2.7, 1.028, −0.034, −1.597, −2.7 | 1.207, 1.007, 0, −1.007, −1.207 |
4 car_van |
2.48 × 5.43 × 1.52; (0, 0, −0.52) | 2.48 × 4.74 × 0.69; (0, −0.281, 0.59) | 2.51, 2.019, 0.702, −0.742, −2.458 | 1.7, 1.6, 0, 0, −2.0 |
5 car_sullycar |
2.0 × 5.224 × 1.101; (0, 0.02, −0.416) | 2.0 × 2.891 × 0.566; (0, −0.649, 0.418) | 2.394, 0.846, −0.708, −1.57, −2.171 | 0.44, 0, −0.697, −1.144, −1.579 |
A band is the count of thresholds the y is below (Table_BandIndexBelow, 0x00389f40: band 0 at or above the
first); equal thresholds make an empty band (the coupe's body band 3, the wagon's cabin band 4, the van's cabin
band 3). The cabin table applies only while |y| is under half box 2's length (+0x24), measured from the car's
position, not from box 2's offset.
The masks. The police car and Sully's car use the sedan's masks exactly (the sedan's table); the
sedan's cabin masks also hold bit 1, the roof, in every band but band 4 (7, 19 / 7, 21), which the standing
path drops and the contact path keeps. The others differ from the sedan in these rows (parts per column, x ≤ −0.6 /
middle / x > 0.6 for the body, x ≤ 0 / x > 0 for the cabin):
| Type | Table, band | Parts |
|---|---|---|
| coupe | body 3 | 14, 15 / 14-17 / 16, 17 (its doors again: no rear doors) |
| wagon | body 4 | 12 / 12, 13 / 13 (no boot in the middle) |
| wagon | cabin 4, 5 | 1, 12 / 1, 12; 1, 12 / 1, 13 (no rear glass 7) |
| van | body 3 | 1 / 1, 5 / 5 |
| van | body 4 | 12 / 12, 13 / 13 |
| van | body 5 | 3, 18, 19, 28 / 3, 18-21 / 3, 20, 21, 29 (the rear doors at the back) |
| van | cabin 0-5 | 6 / 6; 6, 15 / 6, 17; 15 / 17; 1, 12 / 5, 13; 1, 12 / 1, 13; 18, 19 / 20, 21 |
All broken (message 0x19 with −2): the parts 1-21 for every type (0x3ffffe) but the coupe, 1-17 (0x3fffe),
and the van, all but 0 and 7 (0x3fff7e).
The part records are the same in every type except the boxes below and the van's flags: its boot (5) and side
panels 12 and 13 have +0x01 = 0x18 (off at the first damage, never flying), and its parts 18-21 have side bits
0x20 (the back). The +0x01 bytes elsewhere: 0 body 0x00, 1 roof 0x18, 2-3 bumpers 0x02, 4 bonnet 0x07,
5 boot 0x06, 6-9 0x18, 10-13 0x02, doors 14 and 18 0x46, 16 and 20 0x26, windows 0x18, wheels 0x04.
Part boxes (m; the sedan's are in Cars, and Sully's car's are the sedan's):
| Part | Coupe | Wagon | Police car | Van |
|---|---|---|---|---|
| 1 | 2.01 × 1.78 × 0.54 | 0 | 0 | 0.35 × 1.45 × 1.40 |
| 2 | 2.36 × 0.35 × 0.70 | 2.34 × 0.19 × 0.73 | 2.30 × 0.28 × 0.39 | 2.15 × 0.26 × 0.68 |
| 3 | 2.34 × 0.40 × 0.66 | 2.31 × 0.31 × 0.50 | 2.18 × 0.43 × 0.28 | 2.22 × 0.29 × 0.42 |
| 4 | 1.94 × 1.87 × 0.42 | 2.07 × 1.90 × 0.55 | 2.01 × 1.83 × 0.32 | 1.71 × 0.66 × 0.64 |
| 5 | 1.74 × 1.24 × 1.20 | 1.91 × 0.83 × 1.08 | 1.74 × 1.19 × 0.23 | 0.57 × 1.45 × 1.40 |
| 6 | 1.86 × 0.60 × 0.37 | 2.02 × 0.66 × 0.40 | 1.91 × 0.68 × 0.41 | 1.84 × 0.47 × 0.49 |
| 7 | 1.18 × 0.30 × 0.18 | 1.72 × 0.46 × 0.48 | 1.68 × 0.40 × 0.29 | 0.18 × 0.02 × 0.03 |
| 8, 9 | 1.03 × 0.23 × 0.30 | 0.95 × 0.11 × 0.44 | 1.01 × 0.10 × 0.33 (9: 0.09) | 0.91 × 0 × 0.44 |
| 10 | 1.95 × 0.82 × 0.52 | 1.96 × 0.79 × 0.42 | 1.75 × 0.80 × 0.31 | 0.77 × 0.77 × 0.39 |
| 11 | 1.95 × 0.82 × 0.52 | 1.96 × 0.79 × 0.42 | 1.73 × 0.85 × 0.26 | 0.77 × 0.77 × 0.39 |
| 12 | 1.81 × 0.83 × 0.37 | 2.15 × 1.16 × 0.39 | 1.76 × 0.73 × 0.27 | 1.68 × 1.39 × 0.40 |
| 13 | 1.81 × 0.83 × 0.37 | 2.14 × 1.16 × 0.38 | 1.76 × 0.73 × 0.26 | 1.68 × 1.39 × 0.40 |
| 14 | 1.37 × 1.26 × 0.52 | 1.28 × 1.08 × 0.47 | 1.22 × 1.18 × 0.43 | 1.31 × 1.83 × 0.78 |
| 15 | 0 | 0.15 × 0.95 × 0.40 | 0.18 × 0.96 × 0.41 | 1.07 × 0.48 × 0.14 |
| 16 | 1.37 × 1.26 × 0.52 | 1.29 × 1.08 × 0.53 | 1.22 × 1.18 × 0.47 | 1.31 × 1.83 × 0.78 |
| 17 | 0 | 0.15 × 0.95 × 0.40 | 0.18 × 0.96 × 0.41 | 1.07 × 0.48 × 0.14 |
| 18 | 0 | 1.30 × 1.03 × 0.57 | 1.22 × 1.18 × 0.41 | 0.81 × 1.52 × 0.31 |
| 19 | 0 | 0.30 × 0.91 × 0.43 | 0.17 × 0.87 × 0.41 | 0.50 × 0.51 × 0.15 |
| 20 | 0 | 1.30 × 0.58 × 1.03 | 1.22 × 1.18 × 0.41 | 0.74 × 1.51 × 0.34 |
| 21 | 0 | 0.30 × 0.91 × 0.43 | 0.31 × 0.88 × 0.47 | 0.50 × 0.51 × 0.15 |
| 22-25 | 0.76 × 0.76 × 0.27 | 0.76 × 0.76 × 0.27 | 0.76 × 0.76 × 0.27 | 0.79 × 0.79 × 0.28 |
A zero box belongs to a part the model does not use (the coupe's rear doors and its window parts, the roof of most types); the part still sits in the zone masks, so a coupe's door zone still breaks "window 15" as a part.
Hit bits 26-29 have no part record and no atomic. They sit only in the body table's front corner bands (26 left, 27 right) and back corner bands (28 left, 29 right); a hit marks them removed and off, and their first hit sprays pale glass (kind 1) at the front and dark red glass (kind 2) at the back (Hit effects). So they are the lamps' glass, front lamps and tail lights, with nothing drawn (inferred from the masks and effects). The explosion marks them too.
Colour¶
CarSetColor(car, {c1, c2, c3, c4}) turns each number into a byte (× 255, 0x0017aca8) in the order given and stores
the four bytes as one word in +0x12e8 and +0x12ec, marking the car dirty. Car_UpdateRender passes both words to
0x00338240, which spreads the bytes in reverse order (pextlb, prevh) and blends the two, and stores the result in
the render object (+0x24). Confirmed (code) for the packing (Colour_PackFloat: byte i = c(i+1) × 255) and the
reversal (Colour_LerpRatio reverses the bytes on unpacking and does not reverse them back), and confirmed
(runtime), PCSX2 2.9.94, level99, the user's slot 5 (copied): the render tint's bytes in memory order
(RwRGBA r, g, b, a) are (c4, c3, c2, c1). So c1 is alpha, c2 blue, c3 green and c4 red:
- Order. Car slot 0 stores the word
0xe5cc99ff(bytesc1..c4= 255, 153, 204, 229) and its tint reads0xff99cce5, bytes (229, 204, 153, 255). Writing bytes (255, 0, 0, 255) to the radio car's+0x12e8/+0x12ec(dirty byte+0x1308= 1) turned its tint to (255, 0, 0, 255) and the car red on screen. - The radio car in that scene is car slot 1,
car_osedan(type 0) at (57.5, 37.9, 1.1), bytesc1..c4= (255, 140, 165, 165): tint (165, 165, 140). The (255, 153, 204, 229) car is slot 0, a coupe (type 1) at (73.2, 44.8), not the one by the player. - What reaches the GS (GS dumps of the same frame with the tint unchanged and with red): the body's vertex colours
are
min(1, light) × tint / 255on the GS scale (Lighting), alpha0x80, texture modulated (a 64 × 64 8-bit texture). With red, the darkest vertices read (27, 0, 0): the object ambient 0.212 alone (level99, Lighting); with the real tint the same vertices read (17, 18, 16) = (0.212, 0.222, 0.232) × (165, 165, 140) / 255 × 128. Over the car's 2,140 body vertices the channels run 17-94, median about (29, 31, 29): about a quarter of full brightness, so the dark grey-green look is the tint × a mostly ambient light × the texture.
Coney's implementation¶
world_objects::kCarTypeNames and kCarParts (src/world_objects/car_types.h) hold the six names and the 26
undamaged atomics. The level file's 0x47 reader (readPreinstanceObjectChunk, src/platform/level_file.h) reads a
clump of several atomics as a LevelClumpObject (world::extractClumpModels), and the reference renderer draws the
first 26 atomics of each car, each at its frame and given the dictionary's first texture, for
Cars (Running Coney).
Parked cars in play (2026-10-06), written from this page and Crimes:
world_objects::Cars(repo:src/world_objects/cars.h) is the level's pool of 18 cars:CarSpawntakes the type index of its name and its CRC-32,car_copcarspawnspart_copcar_lights(Particles),CarSetColorpacks its four numbers as Colour says and marks the car dirty,CarMakeGoodAsNewclears the removed and open parts, and removing door or window part 14, 16, 18 or 20 also removes the next.CarSpawnRadioputs a stereo in the car; a broken window 15 frees it and a theft takes it once.- The bindings (
repo:src/scripting/car_bindings.h) areCarSpawn,CarSetColor,CarMakeGoodAsNew,CarSpawnRadioandCarPlaceInTrunkOnDetach;CarSpawnreturnsNilHandlewhen the pool is full. - Part damage and the boot item (
Cars::damagePart(),Cars::placeInTrunk()): a part's damage adds up (an instant call sets it to 1) and at 1 the part comes off (its kept removed bit); the boot coming off a non-instant call moves the pinned boot object there, or adds adyn_moneyspawn record holding the boot's dollars; an instant call loses the item, andCarRemovePartreleases nothing. A window's damage is 1 at once, and window 15 coming off frees the stereo. - Windows and hits (
repo:src/world_objects/car_hits.h, from Windows, hits and the stereo): the body and cabin zone tables by where the hitter stands, the facing test, the car pass's reach (1 m outside the box, the 3 × 54° cone, 2 m in height), the aim point (1 m ahead at feet + 1.5 m for a window, so square plays 662), andCars::humanHit(): an intact window shields its door, each part takes 0.115.PlayLevelModeoffers each car the player can target among the object targets and passes the strike toCars::humanHit(). The stereo and the boot item sit at the documented offsets. - The stereo's model (
world_objects::stereoDraws()): the play mode drawsdyn_carstereoat the stereo's offset, turned with the car, while it is in the car or freed, and no more once stolen. - Hit effects (
Cars::takeHitEffects(),PlayLevelMode::carHitEffects(),carBreakWindowEffect()): a human hit that reaches a part outside the glass reports sparks at the hit point, and each part's first hit ever (+0x11ec) its own effect byCar_BreakWindow's table: the side windows' small shatter and the front and rear glass's large one (placed by the part's damaged atomic and a row of its frame, which the platform resolves), the bonnet's steam, the lights' glass. The explosion sends none. The hit point is the square's aim point (Coney's reading: the strike shapes do not test cars yet). It is the hook the effects and the glass sound are drawn and played from. - Explosion (
CarExplode,repo:src/scripting/mission4_bindings.h,Cars::explode()): a car not yet exploded takes instant damage on every part (so a boot item is lost) and is marked exploded; withoutquietits message0x19goes to its own handler and then to the cars' general one (SetGeneralCarMsgHandler,MessageHandlers::setGeneralCar()), with the car as self:(car, NilHandle, -1, 1). - A hit's message
0x19(Cars::humanHit()with its reports, delivered by gameplay throughMessageHandlers::deliverFromCar()): each part 1-25 not already off that the hit damaged sends(car, human, part, flag), the flag 1 when this hit took it off, and the hit that leaves every part 1-25 off then sends(car, human, -2, 1). The hit is also the human's damage done, message 6 to the volume boxes he stands in (Triggers). platform::ParkedCars(repo:src/platform/parked_cars.h) draws each car from the type's Object List model in the two passes of Drawing a car (repo:src/graphics/car_draw.h): both sides of every triangle, the opaque parts after the humans with Z written, the glass parts (6, 7, 15, 17, 19, 21) after the glass panes without, from the last car; a part's damaged form from half damage, the wheels always undamaged, the removed parts not at all; the paint on the parts of the paint mask (0, 4, 5, 10-14, 16, 18, 20), white elsewhere. A car adds nothing to the collision mesh: as in the original, a walker meets and climbs it through its shell in the level's own mesh (Characters, Physics).
Coney's stand-ins, where this page is silent:
- Cars take the objects' lights, chosen per atomic rather than once for the car; the environment map (the car's second texture resource) and the first second's fade-in are not drawn.
- Every type uses the sedan's zone tables and box (Type tables now has them all); the car pass's candidate joins the object targets, nearest first, rather than coming before them.
- Only the square's object attack hits a car, and not yet: the gang lock (
+0x12d8), the exploding car, a player above the car taking the cabin table, the charge's, weapons' and thrown objects' contact-point hits, a molotov's fire (its-1, 0message), the rock, the dent and squeak sounds and the statistic (The fix). - The money pickup's 15 s life is not applied.
- A name that is not one of the six types still makes a car, which draws nothing.
- The explosion's pieces flying off (the car shows every part's damaged form where it was), effects, sound, 300
damage within 5 m, statistic and alert to the AI are not built; its message carries 0 as the other object and the
number, and both the car's handler and the general one are called (which the manager's slots reach, and in which
order, is not on the page). A car with
+0x1200set is not skipped.
Coney status and the fix for the implementer¶
Checked 2026-10-10 by playing car_punch and car_glance on Coney (level99 checkpoint 4, the release build of
main) beside the same car. The user's report, "no destruction, only the glass breaks, and hits must land on very
particular spots", comes from these gaps:
- Only the square's object attack hits a car.
PlayLevelMode::stepObjects()passes a car hit toCars::humanHit()only when player 1's object attack aimed at the car lands; the strike shapes never test the car. So the car must be the square's target: within 1 m of its box, faced within 45° of a face's normal, before any human target. Incar_punchConey matched the original for the two aimed squares (662 broke window 17, 661 reached door 16), but incar_glanceits twoS1s beside the car hit nothing, where the original's broke window 21 and reached door 20. In the original any punch or kick that touches a box hits (What can hit a car). - Every type uses the sedan's tables and box, so on a coupe, wagon or van the zones, the 1 m target margin and the aim point are in the wrong places (Type tables).
- Damage builds slowly and shows little: a door takes 0.115 a punch, dents at the 5th hit and comes off at the 9th, which the original shares; but Coney draws no flying door (it keeps the damaged form in place), plays no dent sound or squeak and no rock, so the hits between look like nothing.
- Not built: the charge, weapon and thrown-object hits (contact point, damages, side mask, the roof), the gang lock, the player-on-top cabin rule, the molotov's fire and countdown, the explosion's pieces, effects, sound and area damage, crime 11, the car-slam damage, and the money pickup's 15 s life.
The fix, as pull requests (each with its tests first; the parity scenarios are car_punch and car_glance):
- Strikes hit cars. In the humans' strike step (
strikeObjects()), sweep each enabled strike sphere from its centre at the last update to its centre now against each car's two boxes (box 1 and box 2 at the car's pose, sizes and offsets per type), as for any object body: the sphere-box sweep and the strike loop are specified exactly in Physics: Unit 5. Segments (forearms, shins, the spine) never reach a box, a sphere whose centre is outside a box it already touches at the start does not hit it, and a shape just switched on is tested with no move. The first hit callsCars::humanHit()with the striker's feet (kind 3: parts by where he stands) and puts the car on the shapes' struck list. The square's object attack then needs no special path: its clip's shapes strike the car like any other clip. KeepcarTargetable()/carAimPoint()for the aim only. Tests: a unit test that a hand sphere swept into box 1 beside the rear door damages parts 20/21 (window first) from feet at the car's y = −0.08; one that a shape 0.1 m short of the box hits nothing; one that a car struck once per window is not struck again until the shapes go off. Playthrough:car_glanceon level99 checkpoint 4 asserts window 21 breaks on the first square. - Per-type tables. Replace
zonesOf()'s stand-in with the six types' boxes, thresholds, masks (with bit 1), part boxes, part flags (the van's0x18) and all-broken masks; add the player-on-top rule (cabin table forced in body bands 1-4 when his feet are above the car's position). Tests: per type, a table-driven test of the zone at a few points (the coupe's band 3 gives 14-17, the van's back gives its rear doors 18-21, the wagon's middle band 4 gives 12, 13); the all-broken message per type. - Contact-point hits. Kinds 2, 1 and 0: the charge (0.51, body table, two columns, no window shield), a held
weapon (0.34, the holder's side only, each part once per swing via a per-human mask cleared when the shapes go
off), a thrown object (0.34, no windows from the body, no doors from the cabin); the roof coming off ending the
police lights; the gang lock (
CarCanBeDamagedBy); the molotov (fire particle, 120-update countdown, the prepared sound, message(car, thrower, -1, 0), crime 11). Tests: each kind's parts and damage from a synthetic contact; a molotov car explodes on the 120th update; a locked car ignores another gang. - Feedback: rock and sounds. The rock of The rock (state per car, six sub-steps an update, ends on
settling, leaves the lean),
shocks_squeak_0<n>at its start, the dent sound (129 × 129 when a part among 4, 5, 10-14, 16, 18, 20 crosses 0.5), the strike's impact sound on material 13 at volume 0.5 for the player. Tests: the rock'sp.z sequence for a strength-2 push (−1.51, −1.71, settling at −0.30 within 30 updates, ending by 47) within 0.01; the dent sound once on the 5th door hit. - Flying pieces. A part with record flag
0x02coming off by a hit flies (What is drawn, Physics): bumpers, panels 10-13, doors with their windows, and the wheels when blown. Tests: the piece's launch speed range and its removal after 360 ticks. - The explosion's effects (separate files from the particles track): fireball and screen shake, the sound, the
heat wave, 300 damage within 5 m, glass within 10 m, the AI event, the statistic, the box shrink, the hubcap and
wreck flags, and its pieces with
sub_flaming_debris. Tests: who takes the 300 (a human 4.9 m away with a clear line does, one behind a wall does not). - Crime 11 and the slam: a player's car hit that reaches an intact part counts crime 11 (Crimes); a held human thrown into the car takes 100 pending damage. Tests: one crime per such hit, none for a hit on parts already off.
Open questions¶
- What sets the draw mask
+0x11fc. (Answered: the part record's+0x20is the bounce restitution, and a flying piece lasts 360 ticks before0x00387f18removes it; Physics.) - What the object a car holds at
+0x1204is (answered: its stereo, The car object). - The lookup that takes a car to its Object List record.
- Which lights a car takes (the objects' selection is named, not its result; the boxes of the collision: Physics).
- Where a car's stereo sits (answered: Windows, hits and the stereo) and the effect kinds of message
0x3f(answered: Hit effects). Answered too:0x002936a8and0x00413018are the 30 m noise and message 6 to the volume boxes;+0x11ecis cleared only byCarMakeGoodAsNew; hit bits 26-29 are the lamps' glass (Type tables). - The zone thresholds, masks and boxes of the other types (answered: Type tables).
- Whether the contact point
+0x60of an object's or a charge's hit is in the car's frame (inferred), and the charge, a weapon and a thrown bottle against a car, at runtime (a door's 9th hit and flight: answered, What can hit a car).