At a glance

Loader
FUN_800513F0 registers backdrop, party blobs and monster meshes into DAT_8007C018[] and seeds the animation rate
Party meshes
Assembled from the player battle files; see battle data pack and character mesh
Monster meshes
Archive record +0x04 mesh and +0x08 texture pool; poses in monster animation
Clip channel
Staged actor[+0x1DA], playing +0x1D9, camera key +0x1DB, commit flags +0x1DC
Rate
actor[+0x21D], normal 8
Colour
Tint lanes +0x04, blend weight +0x0C, mode word +0x08 (top byte = semi-transparency)
Port
actor-formats (assembly), engine-core::world::battle::clip_ladder, engine-battle-vm::battle_actor_tint, battle_trail; hosts engine-shell and web-play::play_battle_render

The draw pipeline

Player battle files PROT 863..865 Party mesh assembly equipment sections Monster archive record +0x04 mesh, +0x08 pool Monster mesh pose stream per action Relocate + upload per-slot TSB / CBA Registered actor set DAT_8007C018 Clip producers SM, damage arm, cast module Staged byte +0x1DA last writer wins Commit FUN_8004AD80 tag ladder, rate +0x21D Anim tick FUN_80047430 cursor, history rings Draw tick FUN_800480D8 once per actor Ghost pass FUN_8004DC68, mode +0x8 Tint pass FUN_8004A908 Trail + after-image 8005112C, 80049348 Draw pass FUN_80048A08 far colour + IR0 Packet wrapper FUN_80043390 mode byte to blend bits
Two independent chains meet at the per-actor draw tick: what is drawn (assembled mesh, relocated textures) and how it is posed (one staged clip byte, committed and ticked). Every colour effect is a pair of words the tint pass hands to the GTE depth cue, so a hit flash is the actor’s own texture pushed toward a colour, never a flat silhouette.

One staged clip byte

Which clip an actor plays is a single byte, actor[+0x1DA]. Every producer writes it and the last writer wins; the commit FUN_8004AD80 snaps the playing id to it. A hit reaction is not a mode the actor is in - it is only the current value of the staged byte, and the next thing staged replaces it.

ProducerWhat it stages
Action SM, approachliteral 1, the walk entry
Action SM, strike loopthe queue byte: 0x0C..0x0F swings, art ids
Damage arm, flinchactor[+0x1EF], the tag-2 entry
Damage arm, knockdownactor[+0x1F1], the tag-4 entry
Cast modulecaster stage literals; the victim’s knockdown

Flinch or knockdown is decided in the damage arm: flinch only when the actor has no get-up entry and the damage is survivable. Every player battle file carries a get-up entry, so a party member takes the knockdown arm on any hit.

When a staged clip commits. Mid-clip, commit-flag bit 0 commits at once and bit 1 commits once the cursor is past the entry’s gate frame. Otherwise the staged byte waits for the natural end. A clip left staged behind itself re-commits from frame one, which is how a loop loops.

The clip-tag ladder

Hit damage arm Knockdown tag 4, from +0x1F1 Get-up tag 5, from +0x1F2 Idle natural end Entry 7 party seat at HP 0 Entry 8, latch 0x8 re-commits at each end HP not 0 HP 0
The commit tests the new clip’s tag byte once and stages whatever comes next. The downed kneel is entry 8 re-committing itself; the latch that stops its root motion is raised by entry 8’s commit, not by the knockdown.

A monster’s death runs through an earlier arm of the same routine: the knockdown is held on its last frame, the spoils run, and the actor takes the capture / defeat fade. With a Seru staged for capture the monster is stood back up for the absorb.

Slow motion and after-images

actor[+0x21D] scales how far the animation cursor moves per frame: 8 normal, 4 half speed, 2 quarter, 0 a freeze. Every animation-driven edge - swing pacing, root motion, the strike loop’s gate - stretches with it. A Stone or Numb body on its idle takes no step at all: the animation tick skips it and the body holds the keyframe it stood on.

Staged idEffect on the rate
any commit while the rate is not 8rate 4
0x1A, the special starterevery slot 0, the acting actor 2
0x1B and up, an artevery slot 2 or 4

The mesh after-images behind an art are two ghost copies drawn from the actor’s pose-history rings (FUN_80049348), additive, only while the ring id marks an art.

Tint and blend

The tint pass packs the actor’s colour lanes into the render node and the draw pass stages them as the GTE far colour and IR0. The prim’s modulation colour becomes

baked + (tint - baked) * blend / 0x1000

and the GPU still multiplies the texel through it. With no blend weight set, the same word is derived from depth instead: the distance fade. A colour word of zero is the summon-hide’s “not drawn” value, not a black tint.

  • The capture / defeat fade ORs additive blending into the node’s mode word, so the fading body is never an opaque black silhouette. It runs after the near-camera ghost pass, which clears the same bits, so the fade’s OR is what the draw reads.
  • The near-camera ghost pass (FUN_8004DC68) makes a body that crowds the lens translucent.
  • Shading itself is on shading and palettes.

Trails and streaks

TrailPrimitiveDriven byRoutine
Weapon trailuntextured gouraud band, semi-transparentthe committed clip’s identity byte +0x77FUN_8005112C
Move-FX afterimageone textured billboardstreak counter high, partyFUN_801E1AB0
Move-FX ribbonchained textured stripcounter low, or any monsterFUN_801E1D98
Mesh after-imagethe actor mesh, additivehistory ring id marks an artFUN_80049348

Where the weapon trail lands. The sweep is sampled off the pose-history ring: step k is the pose 2k frames ago, retail’s rewind at a constant rate. Each control point is turned by the body’s facing and set on its slot’s ground base with retail’s integer arithmetic (products round toward zero, no base height), then placed the way the hosts place a battle body - the frame flip and the 4x world scale - and projected through the battle view-projection.

Both hosts call one kernel for that, engine-world-views::screen_passes::weapon_trail_prims, and hand it nothing but their battle view-projection, so a trail cannot land in one place on the page and another in the window. Tests pin the native composition to the page’s matrix and a trail point onto the body’s own drawn point under both. PROT 0904’s Theeder beam projects through the same point kernel (render-kernels::battle_trail::project_battle_point). The bands composite over the scene; retail’s ordering-table interleave with scene depth is not reproduced.

See also

The assembly and relocation detail, the monster texture pool, every rate writer with its address, the distance fade and both hosts’ render bridges are in docs/subsystems/battle-actor-rendering.md.