Files
helldivers/scripts/download-icons.js
T

161 lines
9.6 KiB
JavaScript
Raw Normal View History

/**
* Download Helldivers 2 stratagem SVG icons from:
* github.com/nvigneux/Helldivers-2-Stratagems-icons-svg
*
* Run once: node scripts/download-icons.js
*/
import https from 'https';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const ICONS_DIR = path.join(__dirname, '..', 'public', 'icons');
const BASE_URL = 'https://raw.githubusercontent.com/nvigneux/Helldivers-2-Stratagems-icons-svg/master';
// Map: our stratagem name → { repo category folder, repo filename (without .svg) }
const ICON_MAP = [
// ── General / PAC ────────────────────────────────────────────────────────
['Reinforce', 'General Stratagems', 'Reinforce'],
['Resupply', 'General Stratagems', 'Resupply'],
['SOS Beacon', 'General Stratagems', 'SOS Beacon'],
['Hellbomb', 'General Stratagems', 'Hellbomb'],
['SEAF Artillery', 'General Stratagems', 'SEAF Artillery'],
['Upload Data', 'General Stratagems', 'Upload Data'],
['Prospecting Drill', 'General Stratagems', 'Prospecting Drill'],
['Orbital Illumination Flare', 'General Stratagems', 'Orbital Illumination Flare'],
// ── Orbital Cannons ───────────────────────────────────────────────────────
['Orbital Gatling Barrage', 'Orbital Cannons', 'Orbital Gatling Barrage'],
['Orbital Airburst Strike', 'Orbital Cannons', 'Orbital Airburst Strike'],
['Orbital 120MM HE Barrage', 'Orbital Cannons', 'Orbital 120MM HE Barrage'],
['Orbital 380MM HE Barrage', 'Orbital Cannons', 'Orbital 380MM HE Barrage'],
['Orbital Walking Barrage', 'Orbital Cannons', 'Orbital Walking Barrage'],
['Orbital Laser', 'Orbital Cannons', 'Orbital Laser'],
['Orbital Railcannon Strike', 'Orbital Cannons', 'Orbital Railcannon Strike'],
// ── Bridge ────────────────────────────────────────────────────────────────
['Orbital Precision Strike', 'Bridge', 'Orbital Precision Strike'],
['Orbital Gas Strike', 'Bridge', 'Orbital Gas Strike'],
['Orbital EMS Strike', 'Bridge', 'Orbital EMS Strike'],
['Orbital Smoke Strike', 'Bridge', 'Orbital Smoke Strike'],
['Tesla Tower', 'Bridge', 'Tesla Tower'],
['Shield Generator Relay', 'Bridge', 'Shield Generator Relay'],
['HMG Emplacement', 'Bridge', 'HMG Emplacement'],
// ── Hangar ────────────────────────────────────────────────────────────────
['Eagle Strafing Run', 'Hangar', 'Eagle Strafing Run'],
['Eagle Airstrike', 'Hangar', 'Eagle Airstrike'],
['Eagle Cluster Bomb', 'Hangar', 'Eagle Cluster Bomb'],
['Eagle Napalm Airstrike', 'Hangar', 'Eagle Napalm Airstrike'],
['LIFT-850 Jump Pack', 'Hangar', 'Jump Pack'],
['Eagle Smoke Strike', 'Hangar', 'Eagle Smoke Strike'],
['Eagle 110MM Rocket Pods', 'Hangar', 'Eagle 110MM Rocket Pods'],
['Eagle 500KG Bomb', 'Hangar', 'Eagle 500KG Bomb'],
['Eagle Rearm', 'Hangar', 'Eagle Rearm'],
// ── PAC Support Weapons ─────────────────────────────────────────────────
['Machine Gun', 'Patriotic Administration Center', 'Machine Gun'],
['Anti-Materiel Rifle', 'Patriotic Administration Center', 'Anti-Materiel Rifle'],
['Stalwart', 'Patriotic Administration Center', 'Stalwart'],
['Expendable Anti-Tank', 'Patriotic Administration Center', 'Expendable Anti-Tank'],
['Recoilless Rifle', 'Patriotic Administration Center', 'Recoilless Rifle'],
['Flamethrower', 'Patriotic Administration Center', 'Flamethrower'],
['Autocannon', 'Patriotic Administration Center', 'Autocannon'],
['Heavy Machine Gun', 'Patriotic Administration Center', 'Heavy Machine Gun'],
['Airburst Rocket Launcher', 'Patriotic Administration Center', 'Airburst Rocket Launcher'],
['Commando', 'Patriotic Administration Center', 'Commando'],
['Railgun', 'Patriotic Administration Center', 'Railgun'],
['Spear', 'Patriotic Administration Center', 'Spear'],
// ── Engineering Bay ───────────────────────────────────────────────────────
['Quasar Cannon', 'Engineering Bay', 'Quasar Cannon'],
['Arc Thrower', 'Engineering Bay', 'Arc Thrower'],
['Laser Cannon', 'Engineering Bay', 'Laser Cannon'],
['Grenade Launcher', 'Engineering Bay', 'Grenade Launcher'],
['Supply Pack', 'Engineering Bay', 'Supply Pack'],
['Guard Dog Rover', 'Engineering Bay', 'Guard Dog Rover'],
['Ballistic Shield Backpack', 'Engineering Bay', 'Ballistic Shield Backpack'],
['Shield Generator Pack', 'Engineering Bay', 'Shield Generator Pack'],
['Anti-Personnel Minefield', 'Engineering Bay', 'Anti-Personnel Minefield'],
['Incendiary Mines', 'Engineering Bay', 'Incendiary Mines'],
['Anti-Tank Mines', 'Engineering Bay', 'Anti-Tank Mines'],
// ── Robotics Workshop ─────────────────────────────────────────────────────
['Machine Gun Sentry', 'Robotics Workshop', 'Machine Gun Sentry'],
['Gatling Sentry', 'Robotics Workshop', 'Gatling Sentry'],
['Mortar Sentry', 'Robotics Workshop', 'Mortar Sentry'],
['Guard Dog', 'Robotics Workshop', 'Guard Dog'],
['Autocannon Sentry', 'Robotics Workshop', 'Autocannon Sentry'],
['Rocket Sentry', 'Robotics Workshop', 'Rocket Sentry'],
['EMS Mortar Sentry', 'Robotics Workshop', 'EMS Mortar Sentry'],
['Patriot Exosuit', 'Robotics Workshop', 'Patriot Exosuit'],
['Emancipator Exosuit', 'Robotics Workshop', 'Emancipator Exosuit'],
// ── Urban Legends / Defensive ─────────────────────────────────────────────
['Directional Shield', 'Urban Legends', 'Directional Shield'],
['Anti-Tank Emplacement', 'Urban Legends', 'Anti-Tank Emplacement'],
];
function fetchURL(url) {
return new Promise((resolve, reject) => {
const req = https.get(url, (res) => {
if (res.statusCode === 301 || res.statusCode === 302) {
return fetchURL(res.headers.location).then(resolve).catch(reject);
}
if (res.statusCode !== 200) {
reject(new Error(`HTTP ${res.statusCode} for ${url}`));
res.resume();
return;
}
const chunks = [];
res.on('data', d => chunks.push(d));
res.on('end', () => resolve(Buffer.concat(chunks)));
});
req.on('error', reject);
req.setTimeout(10000, () => { req.destroy(); reject(new Error('Timeout: ' + url)); });
});
}
async function downloadAll() {
let ok = 0, fail = 0;
const failed = [];
for (const [name, folder, file] of ICON_MAP) {
// Local output: public/icons/<slug>.svg (flat directory, slug = name)
const slug = name.replace(/[^a-z0-9]/gi, '_').toLowerCase();
const outPath = path.join(ICONS_DIR, slug + '.svg');
// Build GitHub raw URL (spaces → %20)
const encoded = encodeURIComponent(folder) + '/' + encodeURIComponent(file + '.svg');
const url = `${BASE_URL}/${encoded}`;
try {
const buf = await fetchURL(url);
fs.writeFileSync(outPath, buf);
console.log(` ↓ ok ${name}`);
ok++;
} catch (err) {
console.log(` ✗ FAIL ${name} (${err.message})`);
fail++;
failed.push({ name, url });
}
// Be polite to GitHub CDN
await new Promise(r => setTimeout(r, 80));
}
console.log(`\nDone: ${ok} ok, ${fail} failed`);
if (failed.length) {
console.log('Failed:');
failed.forEach(f => console.log(` ${f.name}${f.url}`));
}
// Output slug map for server.js
const slugMap = {};
for (const [name] of ICON_MAP) {
const slug = name.replace(/[^a-z0-9]/gi, '_').toLowerCase();
if (fs.existsSync(path.join(ICONS_DIR, slug + '.svg'))) {
slugMap[name] = '/icons/' + slug + '.svg';
}
}
const mapPath = path.join(ICONS_DIR, '_map.json');
fs.writeFileSync(mapPath, JSON.stringify(slugMap, null, 2));
console.log(`\nIcon map written to ${mapPath}`);
}
downloadAll().catch(console.error);