7.4 KiB
Total Annihilation — art spec
Every sheet is optional. data/totalannihilation-artwork.json ships with path: null on
all of them, and TAArt.js paints a procedural stand-in at runtime, so the game is fully
playable with zero PNG files. Drop a sheet in and it replaces the stand-in with no code change.
To install a sheet: save the PNG under assets/images/totalannihilation/, then set that
sheet's path in data/totalannihilation-artwork.json. Nothing else needs editing — the
asset manifest resolves every entry in the sheets map automatically
(src/data/assetManifest.js), so new sheets never need a manifest change either.
The two rules that make the art cheap
- Units are drawn once, facing right.
0 radis Phaser's+xaxis, and the renderer callssetRotation(heading)— so a unit needs exactly one frame, not eight facings. Draw it pointing right, dead centre in its cell. - Units are drawn in neutral grey. The renderer applies
setTint(armyColour)per army. Paint in greys with the detail carried by value (light/dark), not hue; any hue you paint is multiplied by the team colour and will read as muddy. Keep the brightest highlight near white so the tint stays vivid.
Terrain is exempt from both: tiles are drawn in full colour and are never rotated or tinted.
Sheets
| Sheet name (JSON key) | Texture key | Cell | Cols | Contents |
|---|---|---|---|---|
arm-units |
ta-arm-units |
64×64 | 8 | ARM mobile units |
core-units |
ta-core-units |
64×64 | 8 | CORE mobile units |
arm-structures |
ta-arm-structures |
128×128 | 6 | ARM buildings |
core-structures |
ta-core-structures |
128×128 | 6 | CORE buildings |
terrain-grasslands |
ta-terrain-grasslands |
64×64 | 9 | Grassland tiles |
terrain-snowfields |
ta-terrain-snowfields |
64×64 | 9 | Snow tiles |
terrain-tropics |
ta-terrain-tropics |
64×64 | 9 | Jungle tiles |
icons |
ta-icons |
44×44 | 10 | Command/build glyphs |
Frames are numbered left to right, then top to bottom, starting at 0.
Unit sheets (64×64 cells, 8 per row)
Both army sheets use the identical frame layout — that is what lets one unit definition serve
every army. spritePx is the on-screen size the frame is scaled to; draw to fill the cell and
let the scale do the work.
| Frame | Unit | Drawn as | spritePx |
|---|---|---|---|
| 0 | Commander | hull / legs, no weapon | 56 |
| 1 | Commander turret | upper body + D-Gun arm | 56 |
| 2 | Infantry | whole figure, top-down | 30 |
| 3 | Sniper | whole figure, long rifle | 30 |
| 4 | Jeep | chassis + wheels | 40 |
| 5 | Jeep turret | pintle gun | 40 |
| 6 | Tank | hull + tracks | 54 |
| 7 | Tank turret | turret + barrel | 54 |
| 8 | Rocket Tank | hull + tracks | 56 |
| 9 | Rocket Tank turret | rocket pod | 56 |
Turret frames are drawn as a separate image stacked on the hull and rotated independently, so the unit aims while it drives. Rules:
- The turret's pivot is the centre of the cell — draw the turret so its rotation point sits at (32, 32), with the barrel extending to the right.
- Leave the rest of the turret cell transparent. The hull shows through it.
- A unit with no turret (infantry, sniper) simply has no turret frame; its whole sprite rotates to face its target.
Frames 10–63 are free. Add a unit by appending a definition to units[] in
data/totalannihilation-rules.json with its frame (and optional turretFrame) — no code.
Structure sheets (128×128 cells, 6 per row)
Buildings do not rotate. They are scaled to footprint × 64px, so a 2×2 building renders
at 128px and a 3×3 at 192px — draw the 3×3 buildings to fill the cell and accept that they are
scaled up 1.5×, or supply a larger cell size in the artwork JSON.
Each building has two frames: the finished structure, and a build frame shown while it is under construction (the renderer also fades it in by build progress). Draw the build frame as a skeleton/scaffold version — girders, no panels, no lights.
| Frame | Building | Footprint | Notes |
|---|---|---|---|
| 0 | Energy Generator | 2×2 | radiating fins / vents |
| 1 | Energy Generator — under construction | 2×2 | |
| 2 | Mass Generator | 2×2 | drill ring; sits on metal patches |
| 3 | Mass Generator — under construction | 2×2 | |
| 4 | Barracks | 3×3 | doorway on the bottom edge (units exit downward) |
| 5 | Barracks — under construction | 3×3 | |
| 6 | Vehicle Plant | 3×3 | wide bay door on the bottom edge |
| 7 | Vehicle Plant — under construction | 3×3 |
Frames 8–35 are free.
Terrain sheets (64×64 cells, 9 per row)
Every kind: "terrain" sheet must follow this exact frame order — it is fixed in
terrainFrames in the artwork JSON, which is what lets a new theme be a pure drop-in:
| Frame | Tile | Notes |
|---|---|---|
| 0 | ground | the base tile — most of the map |
| 1 | groundAlt1 | variation, scattered to break up tiling |
| 2 | groundAlt2 | second variation |
| 3 | rough | slows wheeled units; still buildable |
| 4 | cliff | impassable, not buildable |
| 5 | water | impassable to current units |
| 6 | waterDeep | impassable; used for the interior of large bodies |
| 7 | metal | metal patch — doubles a Mass Generator's yield |
| 8 | road | fast movement |
Tiles must be seamlessly tileable on all four edges, since the terrain is stamped into chunked RenderTextures with no blending. Keep them low-contrast: units and health bars are drawn on top, and busy terrain makes a battle unreadable.
Metal (frame 7) should read as obviously valuable at a glance — it is the one tile the player hunts for.
Adding a new theme
- Draw a 9-frame terrain sheet in the order above.
- Add it to
sheetsindata/totalannihilation-artwork.jsonwith"kind": "terrain". - Add an entry to
themesnaming that sheet, plus apalettegiving a colour per terrain id (ground,rough,cliff,water,metal,road, …). The palette drives the minimap and the procedural fallback, so it is required even when a real sheet exists.
The theme then appears in the skirmish setup screen automatically.
Icons (44×44 cells, 10 per row)
Command glyphs for the HUD: move, attack, attack-move, stop, hold, patrol, guard, build, repair, rally. Purely decorative today — the build menu labels itself from unit names — so this sheet is the lowest priority of the eight.
Adding a third army (no code)
Append two sheets (kind: "unit" and kind: "structure") to the artwork JSON, then add a row
to armies[] in the rules JSON naming them plus a colour:
{ "id": "hive", "name": "HIVE", "color": "#8ad46a",
"unitSheet": "hive-units", "structureSheet": "hive-structures" }
Every existing unit and building definition works for the new army unchanged, because each
definition references a sheet slot (unitSheet / structureSheet) rather than a sheet
name. Add commanders for it in commanders[], pointing opponentId at an entry in
data/opponents.json for the portrait.
Checking your work
node tools/verifyTotalAnnihilation.js --quick
Section 2 asserts that every declared sheet has a key and frame size, that each army's sheets
exist, that every theme has a palette entry for every terrain type, and that no unit or
building references a frame index beyond its sheet's cols × rows capacity — so a
mis-numbered frame fails the build instead of rendering the wrong tank in a battle.