# 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 1. **Units are drawn once, facing right.** `0 rad` is Phaser's `+x` axis, and the renderer calls `setRotation(heading)` — so a unit needs exactly **one** frame, not eight facings. Draw it pointing **right**, dead centre in its cell. 2. **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 1. Draw a 9-frame terrain sheet in the order above. 2. Add it to `sheets` in `data/totalannihilation-artwork.json` with `"kind": "terrain"`. 3. Add an entry to `themes` naming that sheet, plus a `palette` giving 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: ```json { "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.