feat(totalannihilation): add Megatank unit with multi-barrel weapons and rebalance combat
- Introduce Megatank: medium commander with Twin 150mm Cannon and Side Rocket Pods - Implement multi-barrel weapon system (barrels + barrelSpacing) in TALogic and TARules - Lower DEFENCE_MIN_SKILL from 4 to 3; make first Laser Tower proactive, not reactive - Rebalance unit triangle: Tank now counters Rocket Tank; rockets weak vs vehicle armour - Re-seed campaign missions m05 (67890) and m06 (80808) with updated maps and briefings - Skirmish default theme uses rules default (tropics) instead of hardcoded grasslands - Add intro video before main menu - Update sprite docs and verification tests for new balance
|
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 287 KiB After Width: | Height: | Size: 318 KiB |
|
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 2.1 MiB |
|
Before Width: | Height: | Size: 258 KiB After Width: | Height: | Size: 288 KiB |
|
|
@ -67,7 +67,7 @@
|
||||||
},
|
},
|
||||||
"icons": {
|
"icons": {
|
||||||
"key": "ta-icons", "path": null, "kind": "icon",
|
"key": "ta-icons", "path": null, "kind": "icon",
|
||||||
"frameWidth": 44, "frameHeight": 44, "cols": 10,
|
"frameWidth": 64, "frameHeight": 64, "cols": 10,
|
||||||
"rows": 4
|
"rows": 4
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -273,6 +273,9 @@ export default class TAHud {
|
||||||
this._clearGrid();
|
this._clearGrid();
|
||||||
const s = this.scene;
|
const s = this.scene;
|
||||||
const army = this.state.armies[this.playerArmy];
|
const army = this.state.armies[this.playerArmy];
|
||||||
|
// Real art or the procedural fallback (TAArt.js always supplies one or the other), so this
|
||||||
|
// is only null for a def that predates the `icon` field — see the fallback layout below.
|
||||||
|
const iconKey = this.view.sheetKeys?.icons;
|
||||||
options.forEach((def, i) => {
|
options.forEach((def, i) => {
|
||||||
const col = i % GRID_COLS, row = Math.floor(i / GRID_COLS);
|
const col = i % GRID_COLS, row = Math.floor(i / GRID_COLS);
|
||||||
// The container is placed at the button's CENTRE, not its top-left — see below.
|
// The container is placed at the button's CENTRE, not its top-left — see below.
|
||||||
|
|
@ -286,13 +289,23 @@ export default class TAHud {
|
||||||
// child here is centred on (0,0) so the visual and the hit area coincide.
|
// child here is centred on (0,0) so the visual and the hit area coincide.
|
||||||
const box = s.add.rectangle(0, 0, BTN_W, BTN_H, affordable ? 0x22304a : 0x1a2130, 1)
|
const box = s.add.rectangle(0, 0, BTN_W, BTN_H, affordable ? 0x22304a : 0x1a2130, 1)
|
||||||
.setStrokeStyle(2, affordable ? EDGE : 0x2a3244);
|
.setStrokeStyle(2, affordable ? EDGE : 0x2a3244);
|
||||||
const label = s.add.text(0, -BTN_H / 2 + 12, shortName(def.name), {
|
const children = [box];
|
||||||
fontFamily: FONT, fontSize: '20px', color: affordable ? TEXT : '#5d6b80',
|
const hasIcon = iconKey != null && def.icon != null;
|
||||||
|
// The icon carries the button's identity at a glance once one exists; name and cost drop
|
||||||
|
// underneath it rather than owning the whole button the way they did before icons.
|
||||||
|
if (hasIcon) {
|
||||||
|
const icon = s.add.image(0, -18, iconKey, def.icon).setDisplaySize(36, 36);
|
||||||
|
if (!affordable) icon.setAlpha(0.5);
|
||||||
|
children.push(icon);
|
||||||
|
}
|
||||||
|
const label = s.add.text(0, hasIcon ? 2 : -BTN_H / 2 + 12, shortName(def.name), {
|
||||||
|
fontFamily: FONT, fontSize: hasIcon ? '14px' : '20px', color: affordable ? TEXT : '#5d6b80',
|
||||||
}).setOrigin(0.5, 0);
|
}).setOrigin(0.5, 0);
|
||||||
const cost = s.add.text(0, -BTN_H / 2 + 44, `${def.cost.mass}m ${def.cost.energy}e`, {
|
const cost = s.add.text(0, hasIcon ? 18 : -BTN_H / 2 + 44, `${def.cost.mass}m ${def.cost.energy}e`, {
|
||||||
fontFamily: FONT, fontSize: '15px', color: affordable ? DIM : '#4e5a6d',
|
fontFamily: FONT, fontSize: hasIcon ? '12px' : '15px', color: affordable ? DIM : '#4e5a6d',
|
||||||
}).setOrigin(0.5, 0);
|
}).setOrigin(0.5, 0);
|
||||||
c.add([box, label, cost]);
|
children.push(label, cost);
|
||||||
|
c.add(children);
|
||||||
c.setSize(BTN_W, BTN_H);
|
c.setSize(BTN_W, BTN_H);
|
||||||
c.setInteractive({ useHandCursor: true });
|
c.setInteractive({ useHandCursor: true });
|
||||||
c.on('pointerover', () => box.setFillStyle(affordable ? 0x2e3f5f : 0x1a2130, 1));
|
c.on('pointerover', () => box.setFillStyle(affordable ? 0x2e3f5f : 0x1a2130, 1));
|
||||||
|
|
|
||||||
|
|
@ -30,14 +30,14 @@ capacity the verify script holds every `frame` index to, and it is measured off
|
||||||
|
|
||||||
| Sheet name (JSON key) | Texture key | Cell | Cols | Contents |
|
| Sheet name (JSON key) | Texture key | Cell | Cols | Contents |
|
||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
| `arm-units` | `ta-arm-units` | 64×64 | 8 | ARM mobile units |
|
| `arm-units` | `ta-arm-units` | 128×128 | 8 | ARM mobile units |
|
||||||
| `core-units` | `ta-core-units` | 64×64 | 8 | CORE mobile units |
|
| `core-units` | `ta-core-units` | 128×128 | 8 | CORE mobile units |
|
||||||
| `arm-structures` | `ta-arm-structures` | 192×192 | 6×4 | ARM buildings |
|
| `arm-structures` | `ta-arm-structures` | 192×192 | 6×4 | ARM buildings |
|
||||||
| `core-structures` | `ta-core-structures` | 192×192 | 6×4 | CORE buildings |
|
| `core-structures` | `ta-core-structures` | 192×192 | 6×4 | CORE buildings |
|
||||||
| `terrain-grasslands` | `ta-terrain-grasslands` | 64×64 | 9 | Grassland tiles |
|
| `terrain-grasslands` | `ta-terrain-grasslands` | 64×64 | 9 | Grassland tiles |
|
||||||
| `terrain-snowfields` | `ta-terrain-snowfields` | 64×64 | 9 | Snow tiles |
|
| `terrain-snowfields` | `ta-terrain-snowfields` | 64×64 | 9 | Snow tiles |
|
||||||
| `terrain-tropics` | `ta-terrain-tropics` | 64×64 | 9 | Jungle tiles |
|
| `terrain-tropics` | `ta-terrain-tropics` | 64×64 | 9 | Jungle tiles |
|
||||||
| `icons` | `ta-icons` | 44×44 | 10 | Command/build glyphs |
|
| `icons` | `ta-icons` | 64×64 | 10 | Command/build glyphs — shared, NOT per-army |
|
||||||
|
|
||||||
Frames are numbered **left to right, then top to bottom**, starting at 0.
|
Frames are numbered **left to right, then top to bottom**, starting at 0.
|
||||||
|
|
||||||
|
|
@ -228,24 +228,69 @@ The theme then appears in the skirmish setup screen automatically.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Icons (44×44 cells, 10 per row)
|
## Icons (64×64 cells, 10 per row)
|
||||||
|
|
||||||
Build-menu and command glyphs. Frame numbers come from each def's `icon` in the rules JSON
|
Build-menu and command glyphs. Frame numbers come from each def's `icon` in the rules JSON
|
||||||
and from `commandIcons`, so this table is a reflection of that file, not a second source of
|
and from `commandIcons`, so this table is a reflection of that file, not a second source of
|
||||||
truth. Purely decorative today — the build menu labels itself from unit names — so this sheet
|
truth.
|
||||||
is the lowest priority of the eight.
|
|
||||||
|
**Shared across both armies — this is the one sheet that does NOT get the "paint every army's
|
||||||
|
sheet in its own finished colours" treatment** from the top of this doc. There's a single
|
||||||
|
`icons` entry in the artwork JSON, not an `arm-icons`/`core-icons` pair, and the same frame
|
||||||
|
renders in either player's build menu. Paint it in a neutral palette (dark plate + light
|
||||||
|
linework is what the procedural fallback already does) rather than ARM blue or CORE red/orange.
|
||||||
|
|
||||||
|
**Wired into the build menu** (`TAHud._drawGrid`, since 2026-08-16): every button in the
|
||||||
|
build/production grid — buildings AND units — now draws this sheet's `def.icon` frame above the
|
||||||
|
shortened name and cost, at a **36×36 display size**. The command bar (Move/Attack/Patrol/…)
|
||||||
|
still renders text-only for now; its glyphs (frames 20–29) only exist for the procedural
|
||||||
|
fallback painter today.
|
||||||
|
|
||||||
|
**The 64×64 source is intentionally bigger than that 36×36 display size.** Painting at exactly
|
||||||
|
the display size (the old 44×44 spec was already close to that trap) means every icon gets
|
||||||
|
upscaled a little and softens; painting bigger and letting Phaser scale down is what keeps it
|
||||||
|
crisp. Draw at the full 64×64 canvas — don't pre-shrink your art to "look right" at a smaller
|
||||||
|
preview size.
|
||||||
|
|
||||||
| Frames | Contents |
|
| Frames | Contents |
|
||||||
|---|---|
|
|---|---|
|
||||||
| 0–9 | Units: commander, infantry, sniper, jeep, tank, rocket tank, rocket trooper, construction vehicle, fighter, bomber |
|
| 0–9 | Units: commander, infantry, sniper, jeep, tank, rocket tank, rocket trooper, construction vehicle, fighter, bomber |
|
||||||
| 10–16 | Buildings: energy gen, mass gen, barracks, vehicle plant, laser tower, missile launcher, advanced vehicle plant |
|
| 10–16 | Buildings: energy gen, mass gen, barracks, vehicle plant, laser tower, missile launcher, advanced vehicle plant |
|
||||||
| 17–19 | Overflow: hover constructor (unit), airfield, radar tower |
|
| 17–19 | Overflow: hover constructor (unit), airfield, radar tower |
|
||||||
| 30–31 | Advanced radar, radar jammer |
|
|
||||||
| 20–29 | Commands: move, attack, attack-move, stop, hold, patrol, guard, assist, repair, rally |
|
| 20–29 | Commands: move, attack, attack-move, stop, hold, patrol, guard, assist, repair, rally |
|
||||||
|
| 30–31 | Advanced radar, radar jammer |
|
||||||
|
| 32 | Megatank (unit) |
|
||||||
|
|
||||||
Row 0 filled up before the Airfield units were added, which is why the hover constructor sits
|
Row 0 filled up before the Airfield units were added, which is why the hover constructor sits
|
||||||
next to the buildings at 17 rather than with the other units. Nothing reads a row as a
|
next to the buildings at 17 rather than with the other units. Nothing reads a row as a
|
||||||
category — only the `icon` numbers matter.
|
category — only the `icon` numbers matter. Frames 33–39 are free.
|
||||||
|
|
||||||
|
### Painting priority: buildings the Commander and Construction Vehicle put up
|
||||||
|
|
||||||
|
This is the set that actually appears in the build-menu grid during a normal game — the union
|
||||||
|
of everything `commander`, `constructor` and `hoverconstructor` can build — in a sensible
|
||||||
|
painting order (the 8 the Commander itself can queue from turn one, then the 3 that need a
|
||||||
|
Construction Vehicle or Hover Constructor first):
|
||||||
|
|
||||||
|
| Frame | Building | Also needs a Constructor? | Footprint |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 10 | Energy Generator | no — Commander builds it too | 2×2 |
|
||||||
|
| 11 | Mass Generator | no | 2×2 |
|
||||||
|
| 12 | Barracks | no | 3×3 |
|
||||||
|
| 13 | Vehicle Plant | no | 3×3 |
|
||||||
|
| 14 | Laser Tower | no | 1×1 |
|
||||||
|
| 15 | Missile Launcher | no | 2×2 |
|
||||||
|
| 18 | Airfield | no | 3×3 |
|
||||||
|
| 19 | Radar Tower | no | 2×2 |
|
||||||
|
| 16 | Advanced Vehicle Plant | **yes** | 4×4 |
|
||||||
|
| 30 | Advanced Radar | **yes** | 2×2 |
|
||||||
|
| 31 | Radar Jammer | **yes** | 2×2 |
|
||||||
|
|
||||||
|
Draw each as a simplified, high-contrast read of its full structure-sheet art (see Structure
|
||||||
|
sheets below), not a literal crop of it — a 128–192px building shrunk straight down to 64px
|
||||||
|
loses its silhouette to surface detail. Keep the same silhouette and colour language (Laser
|
||||||
|
Tower's plinth-and-emitter, the Radar family's dish/mast/broken-rings distinction, etc.) so an
|
||||||
|
icon and its full-size building are still recognisably the same thing at a glance.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||