# Player collisions, streetlight destruction and shields

## Why a crashed-into streetlight changes model

The observation is supported by the original response path and real bank
collision data: **Delphi type 11 is the only base-state type in banks 30–32 with
a collision primitive that qualifies for building damage from the player handler.**
Other player building impacts still crash the player, but do not advance the
building state through this handler.

The player list header is `6FD0`. Collision dispatch at `6E56` selects a word at
list header +`28`, with result-category bits added to the header address:

| Outcome | Player handler |
|---|---|
| Terrain/surface | `6F84` |
| Map building | `6EEC` |
| Other object | `6ED4` |

`6EEC` first skips a player already carrying expiry flag `20`. Otherwise it calls
`CDB9`, then enters the player crash sequence at `6F4F`. `CDB9` resolves the
current model through `2DE1`, including state-selected alternate/damage links.
For a player definition, category **2** and a nonzero current model damage link
admit mutation at `67C1`. Category 1 is excluded; other categories reach weapon
checks which player definition pointers do not pass.

The category is not the building type ID or the descriptor's collision marker.
It comes from a collision primitive's opcode shifted right by three
(`606C–6073`). Type 11's base collision stream begins at `8080` with opcode `10`,
giving category 2. The native model selection and admitted-hit response were
checked for states `00`, `20`, `40`, `80`, across the four player definitions
25–28. The resulting **5,088 cases** all passed. At base state, the eligible
set is `{11}` for Delphi and empty for Halon and the underground bank.

These tests use actual model headers and collision streams but supply the hit
category from those streams. They do not execute geometric intersection or
prove a collision from every approach. Types marked non-colliding (`FF` in the
bank descriptor) are excluded. Visual effect spawning is intercepted after
native state mutation; the player crash state changes execute normally.

## Crash transition

`6F4F` installs motion callback `6EF7`, marks flags `08` and `20`, and schedules
expiry 1,536 clock ticks after `BF8` (approximately 3.07 seconds at 500 Hz).
It clears speed `+3E`, engine/shield flag `4552`, and other control/camera state.
The crash motion callback changes heading and steers pitch toward `EC00`.
This is a separate lethal path from ordinary damage accumulation.

The terrain handler `6F84` additionally ignores contact while object flag `10`
is set; this is consistent with a special landing/docking state, but not yet a
complete explanation of all terrain contacts. The object-collision handler
`6ED4` damages the other object with strength `5C`, then applies randomized
player damage with its high bit set. Neither path is included in the building
census fixture.

## Reproduction

```
PYTHONPATH=/tmp/darker-python python3 tools/verify_player_collisions.py
```

[Native results and collision-category census](../analysis/flight-physics/player-collision-verification.json).

## Geometric confirmation

A second fixture now runs `6DF1`, `6D35`, `6527` and the native collision
primitive tests, rather than supplying an admitted hit. It sweeps the player
through isolated real Delphi models along both horizontal axes and directions,
at six heights, in base and first-damage states. **384 sweeps** produced 256
building hits and 20 building-state changes. The tested types are 1, 11, 12,
30, 75, 76, 77 and 86; every state change was a streetlight.

For a concrete comparison, a horizontal centre crossing at height 100 collides
with both type 11 and type 30. The streetlight advances from state 0 to 32;
the house stays at state 0; both crash the player. The same streetlight crossing
at height 1000 clears the geometry. Impact coordinates are recorded too.

This closes the geometry-to-response link for controlled trajectories. The old
and new positions are supplied, not produced by a flight callback; other objects
and effects are excluded. It is not a captured gameplay run.

```
PYTHONPATH=/tmp/darker-python python3 tools/verify_player_collision_geometry.py
```

[Geometric sweep results](../analysis/flight-physics/player-collision-geometry-verification.json).

## Player damage and the invulnerability cheat

A further **20,480 native cases** execute `84D0` (including its randomized angular
kick) and the health/crash gate `6F45`. They cover every incoming damage byte,
Caero and both Skimmas, shields enabled/disabled, several reserve levels and
initial Caero damage levels, with and without “Who gives true life?”. Visual
effect spawning alone is intercepted.

The Caero damage word at `8509` has a peripheral amount in its low byte and a
major-damage count in its high byte. Every 68 peripheral units become one major
unit. An incoming byte with bit 7 set contributes two major units plus its lower
seven bits. A high-byte count of at least four causes the crash gate to fire.
This agrees with the manual's outer/inner Damage Report System display, rather
than treating its two indicators as unrelated quantities.

For Skimmas, an enabled shield subtracts the incoming byte from reserve `810A`.
**Depleting it exactly is lethal**, as is exceeding it: status high byte becomes
4, which the subsequent gate turns into a crash. With shields disabled, any call
through this damage path sets that lethal status. This includes a zero incoming
value; whether such an event occurs naturally depends on the caller.

The cheat zeroes incoming AL after the angular kick. It prevents new Caero
damage here and preserves a positive enabled Skimma shield reserve. It does
**not** prevent the unshielded Skimma branch or the zero-reserve branch from
marking lethal damage, remove pre-existing lethal Caero damage, or bypass the
separate building-crash handler. Nor does it suppress the preceding impact kick.
Thus it is not universal invulnerability.

```
PYTHONPATH=/tmp/darker-python python3 tools/verify_player_damage.py
```

[Player damage results](../analysis/flight-physics/player-damage-verification.json).

## Flight callback integration

The [interactive native trace viewer](../analysis/flight-physics/index.html)
replays 48 controlled runs. Each step executes the entire normal Caero `7EB6`
or Skimma `8108` callback, then native collision pass `6F0F`. Craft definition
motion parameters and the vertical displacement patch come from the original
records; the sine-table startup extension is included. Both engine/shield
states and cheat states are covered. Twenty-eight runs enter the crash callback;
the rest reach the 512-tick limit, which does not imply they are indefinitely safe.

The cheat leaves each paired scenery trace identical. All building-state changes
are streetlights. These runs use fixed 8-tick steps, zero processed input, an
isolated building and a supplied airborne initial state; no claim is made of
complete game-loop or independent C++/Python flight-model equivalence. Full
flight instructions execute, but input production, other actors, drawing and
effect spawning are excluded. Upgraded Skimma starts after its initial docking
transition. Reproduce with `tools/trace_flight_collision.py`.
