Battle attack-camera track table Confirmed
When a party member swings a Tactical Art, the camera cuts in close and from an angle that differs from art to art - and from turn to turn. The angles are a small table in the battle overlay: twenty rows of two signed halfwords. The two columns are not two phases of a swing; they are two alternative offsets, and the game flips a coin between them.
At a glance
- Where
- Battle overlay (PROT entry 0898) data tail, file offset
0x265F8; runtime address0x801F4E10 - Shape
- 20 rows × 2 signed little-endian halfwords,
0x50bytes - Addressing
value(row, column) = base[row × 4 + column × 2]- Column select
ctx[+0x26D], written only asrand() % 2- Reader
- The per-art attack camera
FUN_801D71B8 - Parser / engine
legaia_game_tables::battle_attack_camera_table,engine-battle-vm::battle_attack_camera
The column is a coin flip
The byte that selects the column has exactly one writer on the disc, a routine in the executable that stores rand() % 2 on every call. It is called from the animation commit whenever the committed clip's header marks it as a camera-relevant clip. So a row holds two alternatives and retail picks one at each such commit - which is why the same swing is framed from two visibly different angles on successive turns. One character is the exception: for Gala the column is forced to zero.
One of twenty rows. An arm forms base + column × 2 once, then reads fixed 4-byte steps off it.
Row map
Each arm of the camera routine picks its own rows and its own destination, so the table is not a column-typed record. Most rows fold into the yaw; a few into pitch or an eye-space axis.
| Rows | Folded into |
|---|---|
| 0 | pitch |
| 1, 2, 4..8, 10..14, 17, 18 | yaw |
| 9 | yaw, subtracted (summed with a ramp accumulator first) |
| 3 | eye-space Z |
| 15, 16 | eye-space X |
| 19 | eye-space Z, subtracted |
Two dispatched arms read no row at all and are built from literals and multiples of a ramp counter. One read escapes the coin flip entirely: one of Gala's arms reads row 18, column 0, at a fixed address.
The extent is measured two ways that agree on twenty rows: sweeping every base computation in the reader yields a dense set of displacements with nothing past row 19, and the halfword pair after the last row is zero and addressed by no arm.
Who reads which rows: three jump tables
The reader dispatches on the acting character and then on the latched battle-animation id minus 0x1A, through a table that is per character. The latched id is the clip that is playing, not a Tactical-Arts constant: the arm that runs is chosen by the animation, and the band 0x1A..=0x2D is art-animation bank records 0x0A..=0x1D.
| Character | Bound | Jump table | Live arms |
|---|---|---|---|
| Vahn | 0x11 | 0x801CEA88 | 0x1A, 0x1C, 0x1D, 0x1E / 0x2A |
| Noa | 0x14 | 0x801CEAD0 | 0x1A, 0x1D / 0x2C, 0x1E / 0x2D, 0x1F, 0x20 |
| Gala | 0x11 | 0x801CEB20 | 0x1A, 0x1C, 0x1D, 0x1E / 0x2A |
Most slots in every table are the shared epilogue: an art with no arm keeps the ordinary framing. Thirteen distinct arm bodies exist and they cover the live slots exactly - no arm is reached from two different characters' tables, and there is no spare arm. “Seventeen per-art arms” is one character's table bound, not a property of all three.
Because the dispatch byte only ever falls in this band when an art constant was staged, an action made of plain direction swings never reaches the table - see the action queue. How the override sits over the ordinary framings is on the battle camera page.
See also
Source of record, with every arm's entry address and fold site: docs/formats/battle-attack-camera-table.md.