COB-Exact Unit Animation
Every unit animation on TA Archive is a 1:1 replay of the unit's original scripts/*.COB bytecode — timings, piece motions and shot cadences taken from the disassembly, nothing invented. This page collects the verified number formats, the transform conventions and the pitfalls we proved against the game, with the hard measurements behind each claim.
The principle
A unit's COB script is the same bytecode Total Annihilation executes: AimPrimary, FirePrimary, activatescr/RequestState, MotionControl. We disassemble it (_cob_dis.py), then replay its turn/move/spin/sleep statements on the original .3do mesh with a software renderer (_cob_anim.py). If a number looks odd — Big Bertha's 2°/s gun elevation, a 10-second ready hold, a 3.2 elmo foot snap — it is odd in the game.
Verified number formats
| Value | Format | Evidence |
|---|---|---|
| Angles | BAM int32, 65536 = 360° (so 16450 = 90.363°) | ARMAMB lids fold exactly 90.363°; annotated constants match human-round degrees (5461 = 30°, 1820 = 10°) |
| Paths / offsets | 3DO units 1:1 (1 elmo = 163840) | With 1:1 scaling, six independent ARMAMB parts land exactly at ground level in the folded pose (hinge deviations < 2%). The old “divide by 5” calibration was wrong |
| Turn/move speeds | BAM/s and elmo/s respectively | Durations computed as Δ/speed match the script's own sleep values (e.g. Rocko's 3 elmo return at 3 elmo/s = the 800 ms the script waits) |
Transform and sign conventions
The model chain is parent · T(offset + move) · R(turn), with moves in parent space. COB coordinates reach our camera frame through a 180° rotation about y (x and z flip, y is kept) — turns and moves about/along x and z therefore flip sign, y does not. That rule won every A/B test it was put into — the EMP covers sink through the terrain without it — with one hard exception:
ltong/rtong y, ±119°) had to be negated against the raw COB values to make the prongs swing forward like the game. Measurement: prong-tip z is +181k (backward) with raw signs and −218k/−407k (forward) with negated ones. Lesson: the sense of rotations about y is not settled by the sign rule. Turret yaw direction was never verified against a reference — verify every y-rotation direction by a forward/backward measurement (piece-tip z) or a game reference before trusting it.Effects with no geometry (honest gaps)
- Muzzle flares can be empty SFX anchors (1 vertex, 0 primitives — Goliath's
flare, Flash'sflare1/2). The flash is an engine effect at that anchor and cannot be rendered. Real flare pieces carry 3 primitives (Stumpy'sflare, Peewee'srfire). Check the primitive count before wiring flash windows. - Ship wakes (
wake1/wake2) and nano beams are SFX emitters re-armed every 300 ms — no mesh, not renderable. Missiles, bombs and tracers are engine-spawned the same way. - Flare colours follow the unit's palette. CORE flashes come out red/orange; a yellow pixel test misses them. Verify flash visibility by triangle count (a flare piece adds exactly 3), never by colour.
Timing gotchas
- Weapon TDF
reloadtimeis the engine's shot cadence (EMG 0.4 s, Heavy Cannon 1.5 s, Rockets 4 s). COBReload/SetMaxReloadTimegates aiming — a different clock (the Crusader's restore delay is literally2 × the longest reload). - KBot gaits are stop-motion.
MotionControlcarries 12–20turn-nowposes held 30–150 ms (Peewee 740 ms/12 poses, Eraser 1040 ms/16) — we replay them as hold segments, which is exactly how the original interpreter flashes through them. - Alternating barrels are a
static-variable toggle inFirePrimary(Bulldog starts with barrel1, Crusader with barrel2 —static0=1at Create). The pattern is only visible in the bytecode. - Opcode
0x10055000is a comparison (==), not bitwise and — under an “and” reading the Crusader's alternating fire would be dead code. - Engine hooks stay out of the timeline:
RockUnit/HitByWeapon(hull rock),MoveRate2(bank),StartMovingwake loops — they take their arguments from the engine, so any values would be invented.
Why a walk can look “backwards”
In-place gait loops make the planted foot creep ~8 elmo per cycle backwards while the body does not advance — a moonwalk-reading the eye happily produces. Measured on the Rocko: the swing foot travels fast towards the weapon side (forward, −z), the stance foot slides back slowly — textbook forward walking. Numbers beat the eye here; we keep the faithful in-place loop.
Pipeline
_cob_dis.py (verified opcode table) → _cob_anim.py (channels, terrain clip, fixed camera envelope, walk_keys gait parser) → <uid>_anim.py (the unit's timeline, straight from its disassembly) → GIF + numbered frames → per-unit control page with the phase list and data/units_timelines.json for the unit dialog. 18 units are live this way: ARMAMB, Annihilator, ARMEMP, Big Bertha, Bulldog, Conqueror, Crusader, Eraser, Peewee, Thud, Rocko, A.K., Stumpy, Goliath, Flash, Thunder, Freedom Fighter and the Construction Vehicle.