feat: stratagem icons, session summary, queue preview, UX polish

- Download 65 SVG icons from community repo (scripts/download-icons.js)
- Gold CSS filter on all icons to match game theme
- Session summary modal with score/accuracy/top stratagems
- Queue preview strip (next 3 stratagems with icons)
- Score popup animation, icon shake on wrong input
- Icons in history, leaderboard, and best-per-stratagem tables
- server.js: icon fields on all stratagems, ELO in lobby-update WS events
This commit is contained in:
Jeremy Brandenburger
2026-03-31 08:48:56 +02:00
parent 0d971745a6
commit 2d27d9fe4d
72 changed files with 2280 additions and 372 deletions
+75 -74
View File
@@ -18,81 +18,82 @@ const DATA_DIR = path.join(__dirname, 'data');
if (!fs.existsSync(DATA_DIR)) fs.mkdirSync(DATA_DIR, { recursive: true });
// ── Stratagems (mirrored in public/stratagems.js) ─────────────────────────────
const ICON = (slug) => '/icons/' + slug + '.svg';
const STRATAGEMS = [
// Patriotic Administration Center
{ name: 'Reinforce', category: 'Patriotic Administration Center', sequence: ['up','down','right','left','up'] },
{ name: 'Resupply', category: 'Patriotic Administration Center', sequence: ['down','down','up','right'] },
{ name: 'SOS Beacon', category: 'Patriotic Administration Center', sequence: ['up','down','right','up'] },
{ name: 'Hellbomb', category: 'Patriotic Administration Center', sequence: ['down','up','left','down','up','right','down','up'] },
{ name: 'SEAF Artillery', category: 'Patriotic Administration Center', sequence: ['right','up','up','down'] },
{ name: 'Upload Data', category: 'Patriotic Administration Center', sequence: ['right','right','left','up','up'] },
{ name: 'Eagle Rearm', category: 'Patriotic Administration Center', sequence: ['up','up','left','up','right'] },
{ name: 'Prospecting Drill', category: 'Patriotic Administration Center', sequence: ['down','down','left','right','down'] },
// Orbital Cannons
{ name: 'Orbital Gatling Barrage', category: 'Orbital Cannons', sequence: ['right','down','left','up','up'] },
{ name: 'Orbital Airburst Strike', category: 'Orbital Cannons', sequence: ['right','right','right'] },
{ name: 'Orbital 120MM HE Barrage', category: 'Orbital Cannons', sequence: ['right','right','down','left','right','down'] },
{ name: 'Orbital 380MM HE Barrage', category: 'Orbital Cannons', sequence: ['right','down','up','up','left','down','down'] },
{ name: 'Orbital Walking Barrage', category: 'Orbital Cannons', sequence: ['right','down','right','down','right','down'] },
{ name: 'Orbital Laser', category: 'Orbital Cannons', sequence: ['right','down','up','right','down'] },
{ name: 'Orbital Railcannon Strike', category: 'Orbital Cannons', sequence: ['right','up','down','down','right'] },
{ name: 'Orbital Precision Strike', category: 'Orbital Cannons', sequence: ['right','right','up'] },
{ name: 'Orbital Gas Strike', category: 'Orbital Cannons', sequence: ['right','right','down','right'] },
{ name: 'Orbital EMS Strike', category: 'Orbital Cannons', sequence: ['right','right','left','down'] },
{ name: 'Orbital Smoke Strike', category: 'Orbital Cannons', sequence: ['right','right','down','up'] },
{ name: 'Orbital Illumination Flare', category: 'Orbital Cannons', sequence: ['right','right','left','left'] },
// Hangar
{ name: 'Eagle Strafing Run', category: 'Hangar', sequence: ['up','right','right'] },
{ name: 'Eagle Airstrike', category: 'Hangar', sequence: ['up','right','down','right'] },
{ name: 'Eagle Cluster Bomb', category: 'Hangar', sequence: ['up','right','down','down','right'] },
{ name: 'Eagle Napalm Airstrike', category: 'Hangar', sequence: ['up','right','down','up'] },
{ name: 'LIFT-850 Jump Pack', category: 'Hangar', sequence: ['down','up','up','down','up'] },
{ name: 'Eagle Smoke Strike', category: 'Hangar', sequence: ['up','right','up','down'] },
{ name: 'Eagle 110MM Rocket Pods', category: 'Hangar', sequence: ['up','right','up','left'] },
{ name: 'Eagle 500KG Bomb', category: 'Hangar', sequence: ['up','right','down','down','down'] },
// Bridge
{ name: 'Patriot Exosuit', category: 'Bridge', sequence: ['left','down','right','up','left','down','right'] },
{ name: 'Emancipator Exosuit', category: 'Bridge', sequence: ['left','down','right','up','left','down','down'] },
// Engineering Bay Support Weapons
{ name: 'Machine Gun', category: 'Engineering Bay', sequence: ['down','left','down','up','right'] },
{ name: 'Anti-Materiel Rifle', category: 'Engineering Bay', sequence: ['down','left','right','up','down'] },
{ name: 'Stalwart', category: 'Engineering Bay', sequence: ['down','left','down','up','up','left'] },
{ name: 'Expendable Anti-Tank', category: 'Engineering Bay', sequence: ['down','down','left','up'] },
{ name: 'Recoilless Rifle', category: 'Engineering Bay', sequence: ['down','left','right','right','left'] },
{ name: 'Flamethrower', category: 'Engineering Bay', sequence: ['down','left','up','down','up'] },
{ name: 'Autocannon', category: 'Engineering Bay', sequence: ['down','left','down','up','up','right'] },
{ name: 'Heavy Machine Gun', category: 'Engineering Bay', sequence: ['down','left','up','down','down'] },
{ name: 'Airburst Rocket Launcher', category: 'Engineering Bay', sequence: ['down','up','up','left','right'] },
{ name: 'Commando', category: 'Engineering Bay', sequence: ['down','left','up','down','right'] },
{ name: 'Railgun', category: 'Engineering Bay', sequence: ['down','right','down','up','left','right'] },
{ name: 'Spear', category: 'Engineering Bay', sequence: ['down','down','up','down','down'] },
{ name: 'Quasar Cannon', category: 'Engineering Bay', sequence: ['down','down','up','left','right'] },
{ name: 'Arc Thrower', category: 'Engineering Bay', sequence: ['down','right','down','up','left','left'] },
{ name: 'Laser Cannon', category: 'Engineering Bay', sequence: ['down','left','down','up','left'] },
{ name: 'Grenade Launcher', category: 'Engineering Bay', sequence: ['down','left','up','left','down'] },
// Engineering Bay Equipment
{ name: 'Supply Pack', category: 'Engineering Bay', sequence: ['down','left','down','up','up','down'] },
{ name: 'Guard Dog Rover', category: 'Engineering Bay', sequence: ['down','up','left','up','right','right'] },
{ name: 'Guard Dog', category: 'Engineering Bay', sequence: ['down','up','left','up','right','down'] },
{ name: 'Ballistic Shield Backpack', category: 'Engineering Bay', sequence: ['down','left','down','down','up','left'] },
{ name: 'Shield Generator Pack', category: 'Engineering Bay', sequence: ['down','up','left','right','left','right'] },
{ name: 'Directional Shield', category: 'Engineering Bay', sequence: ['down','left','up','up','right'] },
// Engineering Bay Mines
{ name: 'Anti-Personnel Minefield', category: 'Engineering Bay', sequence: ['down','left','up','right'] },
{ name: 'Incendiary Mines', category: 'Engineering Bay', sequence: ['down','left','left','down'] },
{ name: 'Anti-Tank Mines', category: 'Engineering Bay', sequence: ['down','down','left','left'] },
// Robotics Workshop
{ name: 'Machine Gun Sentry', category: 'Robotics Workshop', sequence: ['down','up','right','right','up'] },
{ name: 'Gatling Sentry', category: 'Robotics Workshop', sequence: ['down','up','right','left'] },
{ name: 'Mortar Sentry', category: 'Robotics Workshop', sequence: ['down','up','right','right','down'] },
{ name: 'Autocannon Sentry', category: 'Robotics Workshop', sequence: ['down','up','right','up','left','up'] },
{ name: 'Rocket Sentry', category: 'Robotics Workshop', sequence: ['down','up','right','right','left'] },
{ name: 'EMS Mortar Sentry', category: 'Robotics Workshop', sequence: ['down','up','right','down','right'] },
{ name: 'Tesla Tower', category: 'Robotics Workshop', sequence: ['down','up','right','up','left','up','up'] },
// Defensive
{ name: 'Shield Generator Relay', category: 'Defensive', sequence: ['down','up','left','right','left','down'] },
{ name: 'Anti-Tank Emplacement', category: 'Defensive', sequence: ['down','right','right','up','left'] },
{ name: 'Orbital Shield Generator', category: 'Defensive', sequence: ['right','right','left','down','left','down'] },
// ── Patriotic Administration Center ──────────────────────────────────────
{ name: 'Reinforce', category: 'Patriotic Administration Center', icon: ICON('reinforce'), sequence: ['up','down','right','left','up'] },
{ name: 'Resupply', category: 'Patriotic Administration Center', icon: ICON('resupply'), sequence: ['down','down','up','right'] },
{ name: 'SOS Beacon', category: 'Patriotic Administration Center', icon: ICON('sos_beacon'), sequence: ['up','down','right','up'] },
{ name: 'Hellbomb', category: 'Patriotic Administration Center', icon: ICON('hellbomb'), sequence: ['down','up','left','down','up','right','down','up'] },
{ name: 'SEAF Artillery', category: 'Patriotic Administration Center', icon: ICON('seaf_artillery'), sequence: ['right','up','up','down'] },
{ name: 'Upload Data', category: 'Patriotic Administration Center', icon: ICON('upload_data'), sequence: ['right','right','left','up','up'] },
{ name: 'Eagle Rearm', category: 'Patriotic Administration Center', icon: ICON('eagle_rearm'), sequence: ['up','up','left','up','right'] },
{ name: 'Prospecting Drill', category: 'Patriotic Administration Center', icon: ICON('prospecting_drill'), sequence: ['down','down','left','right','down'] },
// ── Orbital Cannons ───────────────────────────────────────────────────────
{ name: 'Orbital Gatling Barrage', category: 'Orbital Cannons', icon: ICON('orbital_gatling_barrage'), sequence: ['right','down','left','up','up'] },
{ name: 'Orbital Airburst Strike', category: 'Orbital Cannons', icon: ICON('orbital_airburst_strike'), sequence: ['right','right','right'] },
{ name: 'Orbital 120MM HE Barrage', category: 'Orbital Cannons', icon: ICON('orbital_120mm_he_barrage'), sequence: ['right','right','down','left','right','down'] },
{ name: 'Orbital 380MM HE Barrage', category: 'Orbital Cannons', icon: ICON('orbital_380mm_he_barrage'), sequence: ['right','down','up','up','left','down','down'] },
{ name: 'Orbital Walking Barrage', category: 'Orbital Cannons', icon: ICON('orbital_walking_barrage'), sequence: ['right','down','right','down','right','down'] },
{ name: 'Orbital Laser', category: 'Orbital Cannons', icon: ICON('orbital_laser'), sequence: ['right','down','up','right','down'] },
{ name: 'Orbital Railcannon Strike', category: 'Orbital Cannons', icon: ICON('orbital_railcannon_strike'), sequence: ['right','up','down','down','right'] },
{ name: 'Orbital Precision Strike', category: 'Orbital Cannons', icon: ICON('orbital_precision_strike'), sequence: ['right','right','up'] },
{ name: 'Orbital Gas Strike', category: 'Orbital Cannons', icon: ICON('orbital_gas_strike'), sequence: ['right','right','down','right'] },
{ name: 'Orbital EMS Strike', category: 'Orbital Cannons', icon: ICON('orbital_ems_strike'), sequence: ['right','right','left','down'] },
{ name: 'Orbital Smoke Strike', category: 'Orbital Cannons', icon: ICON('orbital_smoke_strike'), sequence: ['right','right','down','up'] },
{ name: 'Orbital Illumination Flare', category: 'Orbital Cannons', icon: ICON('orbital_illumination_flare'), sequence: ['right','right','left','left'] },
// ── Hangar ────────────────────────────────────────────────────────────────
{ name: 'Eagle Strafing Run', category: 'Hangar', icon: ICON('eagle_strafing_run'), sequence: ['up','right','right'] },
{ name: 'Eagle Airstrike', category: 'Hangar', icon: ICON('eagle_airstrike'), sequence: ['up','right','down','right'] },
{ name: 'Eagle Cluster Bomb', category: 'Hangar', icon: ICON('eagle_cluster_bomb'), sequence: ['up','right','down','down','right'] },
{ name: 'Eagle Napalm Airstrike', category: 'Hangar', icon: ICON('eagle_napalm_airstrike'), sequence: ['up','right','down','up'] },
{ name: 'LIFT-850 Jump Pack', category: 'Hangar', icon: ICON('lift_850_jump_pack'), sequence: ['down','up','up','down','up'] },
{ name: 'Eagle Smoke Strike', category: 'Hangar', icon: ICON('eagle_smoke_strike'), sequence: ['up','right','up','down'] },
{ name: 'Eagle 110MM Rocket Pods', category: 'Hangar', icon: ICON('eagle_110mm_rocket_pods'), sequence: ['up','right','up','left'] },
{ name: 'Eagle 500KG Bomb', category: 'Hangar', icon: ICON('eagle_500kg_bomb'), sequence: ['up','right','down','down','down'] },
// ── Bridge ────────────────────────────────────────────────────────────────
{ name: 'Patriot Exosuit', category: 'Bridge', icon: ICON('patriot_exosuit'), sequence: ['left','down','right','up','left','down','right'] },
{ name: 'Emancipator Exosuit', category: 'Bridge', icon: ICON('emancipator_exosuit'), sequence: ['left','down','right','up','left','down','down'] },
{ name: 'Tesla Tower', category: 'Bridge', icon: ICON('tesla_tower'), sequence: ['down','up','right','up','left','up','up'] },
{ name: 'Shield Generator Relay', category: 'Bridge', icon: ICON('shield_generator_relay'), sequence: ['down','up','left','right','left','down'] },
// ── Engineering Bay Support Weapons ────────────────────────────────────
{ name: 'Machine Gun', category: 'Engineering Bay', icon: ICON('machine_gun'), sequence: ['down','left','down','up','right'] },
{ name: 'Anti-Materiel Rifle', category: 'Engineering Bay', icon: ICON('anti_materiel_rifle'), sequence: ['down','left','right','up','down'] },
{ name: 'Stalwart', category: 'Engineering Bay', icon: ICON('stalwart'), sequence: ['down','left','down','up','up','left'] },
{ name: 'Expendable Anti-Tank', category: 'Engineering Bay', icon: ICON('expendable_anti_tank'), sequence: ['down','down','left','up'] },
{ name: 'Recoilless Rifle', category: 'Engineering Bay', icon: ICON('recoilless_rifle'), sequence: ['down','left','right','right','left'] },
{ name: 'Flamethrower', category: 'Engineering Bay', icon: ICON('flamethrower'), sequence: ['down','left','up','down','up'] },
{ name: 'Autocannon', category: 'Engineering Bay', icon: ICON('autocannon'), sequence: ['down','left','down','up','up','right'] },
{ name: 'Heavy Machine Gun', category: 'Engineering Bay', icon: ICON('heavy_machine_gun'), sequence: ['down','left','up','down','down'] },
{ name: 'Airburst Rocket Launcher', category: 'Engineering Bay', icon: ICON('airburst_rocket_launcher'), sequence: ['down','up','up','left','right'] },
{ name: 'Commando', category: 'Engineering Bay', icon: ICON('commando'), sequence: ['down','left','up','down','right'] },
{ name: 'Railgun', category: 'Engineering Bay', icon: ICON('railgun'), sequence: ['down','right','down','up','left','right'] },
{ name: 'Spear', category: 'Engineering Bay', icon: ICON('spear'), sequence: ['down','down','up','down','down'] },
{ name: 'Quasar Cannon', category: 'Engineering Bay', icon: ICON('quasar_cannon'), sequence: ['down','down','up','left','right'] },
{ name: 'Arc Thrower', category: 'Engineering Bay', icon: ICON('arc_thrower'), sequence: ['down','right','down','up','left','left'] },
{ name: 'Laser Cannon', category: 'Engineering Bay', icon: ICON('laser_cannon'), sequence: ['down','left','down','up','left'] },
{ name: 'Grenade Launcher', category: 'Engineering Bay', icon: ICON('grenade_launcher'), sequence: ['down','left','up','left','down'] },
// ── Engineering Bay Equipment ───────────────────────────────────────────
{ name: 'Supply Pack', category: 'Engineering Bay', icon: ICON('supply_pack'), sequence: ['down','left','down','up','up','down'] },
{ name: 'Guard Dog Rover', category: 'Engineering Bay', icon: ICON('guard_dog_rover'), sequence: ['down','up','left','up','right','right'] },
{ name: 'Guard Dog', category: 'Engineering Bay', icon: ICON('guard_dog'), sequence: ['down','up','left','up','right','down'] },
{ name: 'Ballistic Shield Backpack', category: 'Engineering Bay', icon: ICON('ballistic_shield_backpack'), sequence: ['down','left','down','down','up','left'] },
{ name: 'Shield Generator Pack', category: 'Engineering Bay', icon: ICON('shield_generator_pack'), sequence: ['down','up','left','right','left','right'] },
{ name: 'Directional Shield', category: 'Engineering Bay', icon: ICON('directional_shield'), sequence: ['down','left','up','up','right'] },
// ── Engineering Bay Mines ───────────────────────────────────────────────
{ name: 'Anti-Personnel Minefield', category: 'Engineering Bay', icon: ICON('anti_personnel_minefield'), sequence: ['down','left','up','right'] },
{ name: 'Incendiary Mines', category: 'Engineering Bay', icon: ICON('incendiary_mines'), sequence: ['down','left','left','down'] },
{ name: 'Anti-Tank Mines', category: 'Engineering Bay', icon: ICON('anti_tank_mines'), sequence: ['down','down','left','left'] },
// ── Robotics Workshop ─────────────────────────────────────────────────────
{ name: 'Machine Gun Sentry', category: 'Robotics Workshop', icon: ICON('machine_gun_sentry'), sequence: ['down','up','right','right','up'] },
{ name: 'Gatling Sentry', category: 'Robotics Workshop', icon: ICON('gatling_sentry'), sequence: ['down','up','right','left'] },
{ name: 'Mortar Sentry', category: 'Robotics Workshop', icon: ICON('mortar_sentry'), sequence: ['down','up','right','right','down'] },
{ name: 'Autocannon Sentry', category: 'Robotics Workshop', icon: ICON('autocannon_sentry'), sequence: ['down','up','right','up','left','up'] },
{ name: 'Rocket Sentry', category: 'Robotics Workshop', icon: ICON('rocket_sentry'), sequence: ['down','up','right','right','left'] },
{ name: 'EMS Mortar Sentry', category: 'Robotics Workshop', icon: ICON('ems_mortar_sentry'), sequence: ['down','up','right','down','right'] },
// ── Defensive ─────────────────────────────────────────────────────────────
{ name: 'Anti-Tank Emplacement', category: 'Defensive', icon: ICON('anti_tank_emplacement'), sequence: ['down','right','right','up','left'] },
{ name: 'Orbital Shield Generator', category: 'Defensive', icon: null, sequence: ['right','right','left','down','left','down'] },
];
const VALID_NAMES = new Set(STRATAGEMS.map(s => s.name));