Skip to content

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): with on, set bit part in +0x11f0 and +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): n 0-5 names parts 5, 4, 14, 16, 18, 20. Car_OpenPart does 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, calls 0x0038cb70 (a hinge turn, the atomics) for atomic p and, when p ≤ 21, p + 25. Confirmed (code).
  • Damage (0x0038a4d8(amount, car, part record, part, instant, flag)): adds amount to the part's damage (+0x230 + part × 0xa0; instant sets it to 1); at 1.0 the part comes off (bit in +0x11f8). Callers: the hit handler 0x0038bea0 (0.34 per hit from a weapon or thrown object, 0.51 from a human with flag 0x400000, 0.115 from other humans, on the parts the hit's zone selects), CarSetPartDamage, 0x0038b0e8 (mask 0x2a80c0), and Car_DoExplode (0x0038ab50, every part, instant). Confirmed (code).
  • Boot item (CarPlaceInTrunkOnDetach): when part 5 comes off through a non-instant damage call and +0x12e4 is set, Car_ReleaseTrunkItem (0x0038d188) moves the stored object to the boot, or makes a dyn_money pickup (type 0x1c) holding the stored amount there. An explosion (instant) or CarRemovePart (bits only) never releases it. A player picking up dyn_money gets 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:

  1. 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).
  2. 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 (bit p of +0x11ec, set here; what clears it is not traced) the car calls Car_BreakWindow(car, p, hit) (0x0038a830), which picks the kind below and sends it. A part coming off later, a window's damage reaching 1, CarRemovePart and 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):

  1. Without quiet: particle systems sub_fireball_emitter and part_explosion_screen_shake at the car; the car's sound (0x00110258 with +0x12fc); object +0x38 = 5.0 and the airborne flag (+0x54 0x4000000).
  2. 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 0x04 fly off, or hide when quiet); parts 26-29 are only put in +0x11f0 and +0x11f8. Each one's bit also goes into +0x11ec.
  3. Without quiet: whoever is in +0x1208, when a player controls him, scores statistic 0xb with half the parts counted (at most 10), and +0x1208 is cleared; a heat-wave effect (0x0019c1d0) 0.7 m above the car; the car is updated every tick; it sends itself message 0x19 (below); an AI event 0x17 to 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).
  4. 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 +0x5c and 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):

  1. 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.
  2. 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).
  3. 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 (mask 0x2a80c0), + 1.0 m for the bonnet or boot (0x30), else at the feet. So a window target sits 1.5 m up, and Player_ObjectAttack plays 662 for it (Breakables).
  4. 0x0038b520 takes 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:

  1. Ignores the hit while the car is exploding (+0x12d5, +0x1200); a car whose s16 +0x12d8 is not 0x19 takes hits only from members of the gang with that id (gang +0x2c).
  2. Picks the parts: for a plain human hit (record +0x08 without 0x400000), 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 with 0x400000 (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).
  3. 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.
  4. Damages each part (0.115 a plain human hit, 0.51 a 0x400000 hit, 0.34 an object; the windows break at once), broadcasts message 0x19 per 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 calls Strike_Contact (0x0021b290), which sees a car (Car_FromObject, type word 0x100000) and calls Car_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 plain S1 (clip 12) broke window 21 five updates after it started, and a second S1 reached 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 +0x86 is 8) damages nothing. It makes the 0x0057e350 particle system at the car (direction (0, 0, 1)), sets +0x1200 = 120, prepares vags/fire/car_explodes<n> (n 1-3) at the car into +0x12fc, sends message 0x19 (car, thrower, -1, 0), and for a player thrower keeps his handle in +0x1208 and counts crime 11 (Stats_AddCrime(…, 0xb, 1)); then the 30 m noise and message 6. The car's update counts +0x1200 down once per update and calls Car_DoExplode at 0 (Explosion).
  • The gang lock: a car whose +0x12d8 is not 0x19 ignores a striker (or holder, or thrower) whose gang (brain +0x20c → +0x2c) differs. CarCanBeDamagedBy(car, gang) (lua_CarCanBeDamagedBy 0x00378790 → 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 +0x90 holds the same point in the world, which the rock, the dent sound and the window-break effect use. Confirmed (code) at 0x0034bbe8 and 0x003453f8 (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 flag 0x10), and the roof coming off ends a police car's part_copcar_lights (message 0x15, then its vtable +0x4c; Car_AddPartDamage 0x0038a4d8). 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) plays Sound_PlayMaterialPair(1, 129 CAR_DAMAGE, 129 CAR_DAMAGE) at the hit point (Sound events). Message 0x19 and the first-hit effect follow as Explosion and Hit effects say. The handler returns the parts reached outside the glass, and Strike_Contact plays the impact sound (9, or 0x1a for 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_Contact counts 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 flag 0x800000000 is 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.

  1. From the car's position towards the hit point (unit d), the point p = position + d with 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) is d × (0, 0, 1), normalised.
  2. 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, and vags/vehicles/shocks_squeak_0<n> (n 1-3) plays at the car (volume 1).
  3. 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 along p − 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); p moves 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 (bytes c1..c4 = 255, 153, 204, 229) and its tint reads 0xff99cce5, 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), bytes c1..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 / 255 on the GS scale (Lighting), alpha 0x80, 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: CarSpawn takes the type index of its name and its CRC-32, car_copcar spawns part_copcar_lights (Particles), CarSetColor packs its four numbers as Colour says and marks the car dirty, CarMakeGoodAsNew clears the removed and open parts, and removing door or window part 14, 16, 18 or 20 also removes the next. CarSpawnRadio puts 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) are CarSpawn, CarSetColor, CarMakeGoodAsNew, CarSpawnRadio and CarPlaceInTrunkOnDetach; CarSpawn returns NilHandle when 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 a dyn_money spawn record holding the boot's dollars; an instant call loses the item, and CarRemovePart releases 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), and Cars::humanHit(): an intact window shields its door, each part takes 0.115. PlayLevelMode offers each car the player can target among the object targets and passes the strike to Cars::humanHit(). The stereo and the boot item sit at the documented offsets.
  • The stereo's model (world_objects::stereoDraws()): the play mode draws dyn_carstereo at 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 by Car_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; without quiet its message 0x19 goes 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 through MessageHandlers::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, 0 message), 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 +0x1200 set 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 to Cars::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. In car_punch Coney matched the original for the two aimed squares (662 broke window 17, 661 reached door 16), but in car_glance its two S1s 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):

  1. 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 calls Cars::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. Keep carTargetable() / 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_glance on level99 checkpoint 4 asserts window 21 breaks on the first square.
  2. Per-type tables. Replace zonesOf()'s stand-in with the six types' boxes, thresholds, masks (with bit 1), part boxes, part flags (the van's 0x18) 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.
  3. 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.
  4. 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's p.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.
  5. Flying pieces. A part with record flag 0x02 coming 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.
  6. 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).
  7. 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 +0x20 is the bounce restitution, and a flying piece lasts 360 ticks before 0x00387f18 removes it; Physics.)
  • What the object a car holds at +0x1204 is (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: 0x002936a8 and 0x00413018 are the 30 m noise and message 6 to the volume boxes; +0x11ec is cleared only by CarMakeGoodAsNew; 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 +0x60 of 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).