/** * MGT2 Commerce – Parseur UWP (Universal World Profile) * * Format standard : SAAHPGL-T * Exemples : A788899-C B200400-A * * S = Astroport (A B C D E X) * A = Taille (0–A hex, 0–10) * A = Atmosphère (0–F hex, 0–15) * H = Hydrographie (0–A hex, 0–10) * P = Population (0–C hex, 0–12) * G = Gouvernement (0–F hex, 0–15) * L = Niveau de loi(0–J hex, 0–19) * T = Niveau tech (0–F hex, 0–15) — après le tiret */ import { TRADE_CODES } from './data/tradeTables.js'; /** * Convertit un caractère de code UWP en valeur numérique. * Utilise la base 16 pour les chiffres 0–9 et lettres A–F. * Pour le Niveau de loi : G=16, H=17, J=18 (pas de I). */ function uwpCharToInt(char) { if (!char) return 0; const c = char.toUpperCase(); if (c === 'G') return 16; if (c === 'H') return 17; if (c === 'J') return 18; const n = parseInt(c, 16); return isNaN(n) ? 0 : n; } /** * Parse une chaîne UWP et retourne un objet avec tous les attributs. * @param {string} uwp Ex. "A788899-C" * @returns {{ * raw: string, * valid: boolean, * error: string|null, * starport: string, * size: number, * atmosphere: number, * hydrographics: number, * population: number, * government: number, * lawLevel: number, * techLevel: number, * tradeCodes: string[], * zone: string, * }} */ export function parseUWP(uwp, zone = 'normal') { const result = { raw: uwp, valid: false, error: null, starport: '', size: 0, atmosphere: 0, hydrographics: 0, population: 0, government: 0, lawLevel: 0, techLevel: 0, tradeCodes: [], zone, }; if (!uwp || typeof uwp !== 'string') { result.error = 'UWP manquant'; return result; } // Normalise : retire espaces, met en majuscules const s = uwp.trim().toUpperCase(); // Accepte SAAHPGL-T ou SAAHPGL (sans tech level) const match = s.match(/^([ABCDEX])([0-9A-F])([0-9A-F])([0-9A])([0-9A-C])([0-9A-F])([0-9A-J])(?:-([0-9A-F]))?$/i); if (!match) { result.error = `Format UWP invalide : "${uwp}". Attendu : SAAHPGL-T (ex. A788899-C)`; return result; } result.starport = match[1]; result.size = uwpCharToInt(match[2]); result.atmosphere = uwpCharToInt(match[3]); result.hydrographics = uwpCharToInt(match[4]); result.population = uwpCharToInt(match[5]); result.government = uwpCharToInt(match[6]); result.lawLevel = uwpCharToInt(match[7]); result.techLevel = match[8] ? uwpCharToInt(match[8]) : 0; result.valid = true; result.tradeCodes = deriveTradeCodes(result); return result; } /** * Dérive les codes commerciaux d'un monde à partir de ses attributs UWP. * @param {object} w Objet retourné par parseUWP * @returns {string[]} */ export function deriveTradeCodes(w) { const codes = []; // Ag – Agricole : Atm 4–9, Hyd 4–8, Pop 5–7 if (w.atmosphere >= 4 && w.atmosphere <= 9 && w.hydrographics >= 4 && w.hydrographics <= 8 && w.population >= 5 && w.population <= 7) { codes.push('Ag'); } // As – Astéroïdes : Taille 0, Atm 0, Hyd 0 if (w.size === 0 && w.atmosphere === 0 && w.hydrographics === 0) { codes.push('As'); } // Ba – Stérile : Pop 0, Gov 0, Loi 0 if (w.population === 0 && w.government === 0 && w.lawLevel === 0) { codes.push('Ba'); } // De – Désert : Atm 2+, Hyd 0 if (w.atmosphere >= 2 && w.hydrographics === 0) { codes.push('De'); } // Fl – Océans fluides : Atm A (10)+, Hyd 1+ if (w.atmosphere >= 10 && w.hydrographics >= 1) { codes.push('Fl'); } // Ga – Jardin : Taille 6–8, Atm 5 ou 6 ou 8, Hyd 5–7 if (w.size >= 6 && w.size <= 8 && [5, 6, 8].includes(w.atmosphere) && w.hydrographics >= 5 && w.hydrographics <= 7) { codes.push('Ga'); } // Hi – Pop. élevée : Pop A (10)+ if (w.population >= 10) { codes.push('Hi'); } // Ht – Haute tech : TL C (12)+ if (w.techLevel >= 12) { codes.push('Ht'); } // IC – Calotte glaciaire : Atm 0–1, Hyd 1+ if (w.atmosphere <= 1 && w.hydrographics >= 1) { codes.push('IC'); } // In – Industriel : Atm ∈ {0,1,2,4,7,9}, Pop 9+ if ([0, 1, 2, 4, 7, 9].includes(w.atmosphere) && w.population >= 9) { codes.push('In'); } // Lo – Pop. basse : Pop 1–3 if (w.population >= 1 && w.population <= 3) { codes.push('Lo'); } // Lt – Basse tech : TL ≤ 5 if (w.techLevel <= 5) { codes.push('Lt'); } // Na – Non-Agricole : Atm 0–3, Hyd 0–3, Pop 6+ if (w.atmosphere <= 3 && w.hydrographics <= 3 && w.population >= 6) { codes.push('Na'); } // Ni – Non-Industriel : Pop 4–6 if (w.population >= 4 && w.population <= 6) { codes.push('Ni'); } // Po – Pauvre : Atm 2–5, Hyd 0–3 if (w.atmosphere >= 2 && w.atmosphere <= 5 && w.hydrographics <= 3) { codes.push('Po'); } // Ri – Riche : Gov ∈ {4,5,6,10}, Pop 6–8, Atm ∈ {6,8} if ([4, 5, 6, 10].includes(w.government) && w.population >= 6 && w.population <= 8 && [6, 8].includes(w.atmosphere)) { codes.push('Ri'); } // Wa – Monde aquatique : Hyd A (10) if (w.hydrographics === 10) { codes.push('Wa'); } // Va – Vide : Atm 0 if (w.atmosphere === 0) { codes.push('Va'); } return codes; } /** * Retourne le modificateur d'astroport pour les tables de trafic. * @param {string} starport 'A'|'B'|'C'|'D'|'E'|'X' * @param {string} table 'passenger'|'cargo'|'supplier' * @returns {number} */ export function starportModifier(starport, table = 'passenger') { if (table === 'supplier') { return { A: 6, B: 4, C: 2, D: 0, E: 0, X: 0 }[starport] ?? 0; } // passenger & cargo return { A: 2, B: 1, C: 0, D: 0, E: -1, X: -3 }[starport] ?? 0; } /** * Retourne le modificateur de population pour les tables de trafic. * @param {number} pop Valeur numérique de la population * @param {string} table 'passenger'|'cargo' * @returns {number} */ export function populationModifier(pop, table = 'passenger') { if (table === 'cargo') { if (pop <= 1) return -4; if (pop >= 6 && pop <= 7) return 2; if (pop >= 8) return 4; return 0; } // passenger if (pop <= 1) return -4; if (pop >= 6 && pop <= 7) return 1; if (pop >= 8) return 3; return 0; } /** * Retourne le modificateur de zone (Ambre/Rouge/Normal) pour les tables de trafic. * @param {string} zone 'normal'|'amber'|'red' * @param {string} table 'passenger'|'cargo' * @returns {number} */ export function zoneModifier(zone, table = 'passenger') { if (table === 'cargo') { return { normal: 0, amber: -2, red: -6 }[zone] ?? 0; } return { normal: 0, amber: 1, red: -4 }[zone] ?? 0; }