# Vehicle routes and map-state events

**Known unresolved discrepancy:** the shared convoy prefix in `04_009 / 7` produces spreading and off-map paths. See the [convoy audit](route-convoy-discrepancy.md). These are isolated native-execution predictions, not verified full-game trajectories; the viewer now marks affected routes.

The [scenario viewer](../analysis/missions/index.html) now plots the compact route programs: **172 routed objects in 27 stored scenarios**, with **50 scripted map-damage events** in the sampled passes. Choose a vehicle under **Vehicle routes and events** to isolate its path. Green lines and arrows show movement; red cells are damage sites. Click a red cell or select an event to see the before/after model and script waits linked to that cell. The event dropdown distinguishes multiple stages at the same location.

[vehicle-routes.json](../analysis/missions/vehicle-routes.json) preserves samples, route instruction offsets, repeat quantities, event locations, map states and local script reactions. Routes include initially active, reserve and free-list templates; plotting one does not mean it is active at mission start.

## Wrecker: 04_002 / 0, map 70

Object **4**, definition slot **29**, is the Wrecker. It starts from source cell (53.5,56.5), heading C000h, and uses the route at **0127h**. It visits three section-door cells:

| Cell | Map type | First damage / second damage script offsets | Main-script response to state 40h |
| --- | --- | --- | --- |
| (61,56) | 112 | 0129 / 012B | Wait 00EC releases six queued air reserves at 00F0 |
| (65,49) | 111 | 0130 / 0132 | Wait 00F3 releases five at 00F7 |
| (62,41) | 111 | 013A / 013C | Wait 00FA releases two at 00FE |

These are **city-map models**, not additional moving-object templates. Types 111 and 112 have three linked geometries: 35 faces at state 00h, 47 at 20h, and 95 at 40h (96 selects the same final geometry). Their two orientations and progressively broken openings match the section-door role. Before/after thumbnails and model-browser links are shown for each event.

At each door the route uses effects, a first damage action, more effects, and a second damage action while lowering. Native `67BF` **adds 20h** to the map state byte; this is not simply an OR of a destroyed bit. Starting from 00h, the transitions are **00 → 20 → 40**. Opcode 1D waits use mask 40h: the first transition does not release them; the second does. Other object scripts wait on these same cells before changing target cells; the viewer lists their local continuations too. This is an explicit connection between route progress, changing door geometry and encounter progression.

After the last door the Wrecker travels a little farther and reaches the stationary loop at **013E**, with `FF FE` at 013F branching back. The plotted endpoint is approximately (62.496,39.996). The fractional offsets are the original fixed-point placement results, not rounded cell centres.

The sampled first and second damage ticks are 57,344/73,728, 155,648/172,032 and 253,952/270,336 after the isolated activation start. These are route-clock values, not wall-clock promises for a playthrough with pauses, interference or a different activation time.

## Flatbed: 04_000 / 3, map 68

Object **2**, slot **31**, is the flatbed/equipment carrier. It has an explicit route at **18F8h**, so it is not merely a stationary mission prop:

- Wait six route units (24,576 ticks).
- Drive six units, then make successive turns and straight sections.
- Wait another two units near the end.
- Drive two more units and execute **action 8 at 1906h**, calling the removal path.

There are no map-damage operations on this route. It runs from source position (57.5,80.5) to a sampled endpoint approximately (66.496,95.996), with removal at tick 155,648 in the isolated run. The route supports the observation that it is a moving truck; its exact narrative association with the downed partner remains an interpretation of the briefing, not a model-definition label.

## Compact interpreter

The original setup at `BE1B` installs callback `8F3B`, the route pointer in object +58, and initial time field +5E=F000h. It uses the stored coordinates directly; this compact-object path does **not** call the underground placement helper BEE7 used by ordinary moving objects.

`8F3B` reads the low nibble as an action and the high nibble as a repeat count. A byte occupies **high nibble + 1 units**, each 1000h (4,096) clock ticks. FF is a signed relative branch with the original pointer convention. The route can remain alive indefinitely in a loop.

| Action | Handler | Recovered behaviour |
| --- | --- | --- |
| 0 | 90D2 | Stationary; clear movement bit and speed; optional combat helper afterwards |
| 1 | 90DB | Advance along heading, with optional combat helper |
| 2 | 9102 | Curved turn with decreasing heading |
| 3 | 912D | Curved turn with increasing heading |
| 4 | 9056 | Raised/climbing traversal with height/pitch profile |
| 5 | 904B | Damage current cell once on entry, then descending profile |
| 6 | 8FDD | Damage current cell once on entry, then stationary effects |
| 7 | 8FE0 | Stationary effects |
| 8 | 90C5 | Call object-removal routine 6EC7 unless flag 20h suppresses it |
| 9 | 909E | Set timed object flags; complete gameplay meaning not yet established |

Cardinal heading directions in map coordinates are 0000h north, 4000h west, 8000h south and C000h east. Turns use the original interpolation tables and sub-cell arithmetic. Stop/resume actions affect the cell-step flag, so simply drawing one straight segment per high nibble would produce incorrect routes.

## Execution and verification scope

The exporter executes original `8F3B` and its movement/turn handlers in Unicorn, sampling every 512 ticks. It executes the actual map-state addition and counter carry update inside `67BF`, then intercepts the later debris/visibility path. It omits particle creation and the optional combat helper 9185, intercepts removal, and uses a synthetic zero model height. Each vehicle runs independently against the map plus the scenario's initial cell-list writes. Cross-vehicle damage and player interference are not simulated.

The first repeated route instruction ends the preview: this shows one pass through a loop, not every future lap. All 172 programs ended in a repeated instruction (136) or removal (36); none hit the sampling budget. This is separate from the main mission scheduler, whose activation and other prerequisites remain relevant.

[Verification](../analysis/missions/route-verification.json) checks independent one-cell movement expectations in all four cardinal directions; compares operation times, damage events and final positions at 256 versus 512 tick sampling for both requested examples; executes the original world-flag wait with states 00/20/40/60; and checks the linked reinforcement counts 6/5/2. Every exported damage event asserts the original instruction's resulting state byte. Browser checks exercised all 124 records and selected the Wrecker's door models and reinforcement reaction.

```sh
PYTHONPATH=/tmp/darker-python python3 tools/extract_vehicle_routes.py
PYTHONPATH=/tmp/darker-python python3 tools/verify_vehicle_routes.py
python3 tools/mission_browser.py
```
