At a glance

Where
Battle overlay (PROT entry 0898) data tail, file offset 0x265F8; runtime address 0x801F4E10
Shape
20 rows × 2 signed little-endian halfwords, 0x50 bytes
Addressing
value(row, column) = base[row × 4 + column × 2]
Column select
ctx[+0x26D], written only as rand() % 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.

RowsFolded into
0pitch
1, 2, 4..8, 10..14, 17, 18yaw
9yaw, subtracted (summed with a ramp accumulator first)
3eye-space Z
15, 16eye-space X
19eye-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.

CharacterBoundJump tableLive arms
Vahn0x110x801CEA880x1A, 0x1C, 0x1D, 0x1E / 0x2A
Noa0x140x801CEAD00x1A, 0x1D / 0x2C, 0x1E / 0x2D, 0x1F, 0x20
Gala0x110x801CEB200x1A, 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.