# Weapons and carried-store dynamics

The [weapon viewer](weapons/index.html) exposes all **27 native weapon records**, their carried/fired definitions, mass, drag, launch-speed addend and raw range operand. Linked model thumbnails show the carried, fired and secondary shapes for the selected weapon; definitions sharing a shape share a preview. It also shows every timed projectile stage and links its callback to the native code browser.

## Timed projectile programs

Each 48-byte weapon record begins with a pointer to six-byte stage records: a little-endian 16-bit duration and a 32-bit callback. There are **92 nonterminal stages**. A zero-duration, zero-callback entry terminates each program. Native selector [0xa2cf4](code/index.html#a2cf4) copies the next callback and duration into the projectile and advances its stage index. All **119 entries**, including the 27 terminators, have been executed and compared in [native-checks.json](weapons/native-checks.json).

Durations are timer ticks, not model-animation frames. The launcher's verified PIT divisor `0x5555` gives a nominal `1193182 / 21845`, approximately 54.62 ticks/second. The viewer's seconds are derived from that clock; elapsed simulation adjustments and frame scheduling can still affect observed timing.

The projectile constructor [0xa2d80](code/index.html#a2d80) establishes the linked-list record. Its fields include current and previous fixed-point positions, orientation, SSD handle, guidance reference, callback and remaining stage ticks. The [field table](weapons/data.json) retains exact byte offsets, including unaligned fields. Previous XYZ is kept separately for swept collision checks, as described in [the game-logic notes](game-logic.md#projectiles-damage-and-collision).

## Mass, drag and moments

These meanings are supported by both display and physics code. Record offset `+31` is a signed 16-bit mass in kilograms: the loadout display multiplies it by 2.2 for pounds. Offset `+44` is a floating-point drag coefficient: the display scales it by 10,000 for its DRAG readout. The range operand and speed addend are preserved numerically without assigning unverified physical units.

The native store accumulator [0x95dc8](code/index.html#95dc8) walks ten mounted-store slots. For each occupied slot it adds mass and drag, plus two load-dependent moments. With the three native mount-coordinate arrays called `a`, `b` and `c`:

```text
store mass   = sum(mass)
store drag   = sum(drag)
store moment A = sum(mass * (a*a + b*b))
store moment B = sum(mass * (a*a + c*c))
```

Moment A is added to the base value at data `0x142c8`; moment B to `0x14258`. Coordinate arrays are at `0x13bd0`, `0x13c20` and `0x13c70`. Axis names are deliberately withheld until the full flight-coordinate convention is established. These are actual native calculations rather than estimates from weapon names.

The same helper copies base fuel capacity `0x14850` into `0x14770`. In the tested flight-model setting (`0x11b2a == 0`), a class-6 external tank multiplies capacity by **2.0** and sets the tank-present flag. Current fuel is a separate field at `0x14858`.

[decode_weapons.py](../tools/decode_weapons.py) executes **29 loadouts**: empty, each weapon individually at declared synthetic mount coordinates, and a mixed load. Mass, drag, both moments, resulting totals and fuel capacity match the original x87 calculations. These tests establish the store contribution; they do not yet reproduce all aerodynamic forces, guidance laws, target health or damage outcomes.

## Carried-model bindings

Loadout setter `0x8b3d4` writes both the occupied weapon slot and its carried-model handle. **Thirty native cases** verify all ten slots for empty and occupied settings. [Model attachment checks](models/submodel-checks.json) additionally execute opcode 81's anchor/condition handling. The [model viewer](models/index.html#efa) can now inspect a supplied loadout, independently of the weapon physics table.
