/** * Traveller NPC Generator - Données de configuration * Basé sur : https://github.com/carloscasalar/traveller-npc-generator * * Ce fichier contient toutes les données nécessaires pour générer des PNJ * selon les règles du générateur Traveller. */ const MODULE_ID = 'mgt2-compendium-amiral-denisov'; // ============================================================================ // Catégories de citoyens // ============================================================================ export const CITIZEN_CATEGORY = { BELOW_AVERAGE: { key: 'belowAverage', label: 'En dessous de la moyenne', value: 0, characteristicArray: [8, 7, 6, 6, 5, 4], description: 'Citoyen avec des capacités inférieures à la moyenne' }, AVERAGE: { key: 'average', label: 'Moyenne', value: 1, characteristicArray: [9, 8, 7, 7, 6, 5], description: 'Citoyen moyen' }, ABOVE_AVERAGE: { key: 'aboveAverage', label: 'Au-dessus de la moyenne', value: 2, characteristicArray: [10, 9, 8, 8, 7, 6], description: 'Citoyen avec des capacités supérieures à la moyenne' }, EXCEPTIONAL: { key: 'exceptional', label: 'Exceptionnel', value: 3, characteristicArray: [11, 10, 9, 9, 8, 7], description: 'Citoyen exceptionnel' } }; export const CITIZEN_CATEGORY_LIST = [ CITIZEN_CATEGORY.BELOW_AVERAGE, CITIZEN_CATEGORY.AVERAGE, CITIZEN_CATEGORY.ABOVE_AVERAGE, CITIZEN_CATEGORY.EXCEPTIONAL ]; // ============================================================================ // Niveaux d'expérience // ============================================================================ export const EXPERIENCE_LEVEL = { RECRUIT: { key: 'recruit', label: 'Recrue', value: 0, skillDistribution: { level0: 4, level1: 0, level2: 0, level3: 0 }, description: 'Nouveau, sans expérience' }, ROOKIE: { key: 'rookie', label: 'Débutant', value: 1, skillDistribution: { level0: 4, level1: 2, level2: 0, level3: 0 }, description: 'Débutant avec un peu d\'expérience' }, INTERMEDIATE: { key: 'intermediate', label: 'Intermédiaire', value: 2, skillDistribution: { level0: 4, level1: 2, level2: 1, level3: 0 }, description: 'Niveau intermédiaire' }, REGULAR: { key: 'regular', label: 'Régulier', value: 3, skillDistribution: { level0: 5, level1: 2, level2: 2, level3: 0 }, description: 'Expérience régulière' }, VETERAN: { key: 'veteran', label: 'Vétéran', value: 4, skillDistribution: { level0: 5, level1: 2, level2: 3, level3: 0 }, description: 'Vétéran expérimenté' }, ELITE: { key: 'elite', label: 'Élite', value: 5, skillDistribution: { level0: 6, level1: 3, level2: 2, level3: 1 }, description: 'Élite, très expérimenté' } }; export const EXPERIENCE_LEVEL_LIST = [ EXPERIENCE_LEVEL.RECRUIT, EXPERIENCE_LEVEL.ROOKIE, EXPERIENCE_LEVEL.INTERMEDIATE, EXPERIENCE_LEVEL.REGULAR, EXPERIENCE_LEVEL.VETERAN, EXPERIENCE_LEVEL.ELITE ]; // ============================================================================ // Rôles (Crew roles in a starship) // ============================================================================ // Caractéristiques par rôle - définit quelles caractéristiques sont prioritaires // pour chaque rôle (High, Medium, Low) export const CHARACTERISTIC_PRIORITIES = { pilot: { high: ['DEX', 'INT'], medium: ['EDU', 'STR'], low: ['END', 'SOC'] }, navigator: { high: ['INT', 'EDU'], medium: ['DEX', 'SOC'], low: ['STR', 'END'] }, engineer: { high: ['INT', 'EDU'], medium: ['DEX', 'END'], low: ['STR', 'SOC'] }, steward: { high: ['INT', 'SOC'], medium: ['DEX', 'EDU'], low: ['STR', 'END'] }, medic: { high: ['INT', 'EDU'], medium: ['DEX', 'SOC'], low: ['STR', 'END'] }, marine: { high: ['STR', 'END'], medium: ['DEX', 'INT'], low: ['EDU', 'SOC'] }, gunner: { high: ['DEX', 'INT'], medium: ['END', 'EDU'], low: ['STR', 'SOC'] }, scout: { high: ['DEX', 'INT'], medium: ['END', 'EDU'], low: ['STR', 'SOC'] }, technician: { high: ['INT', 'EDU'], medium: ['DEX', 'END'], low: ['STR', 'SOC'] }, leader: { high: ['INT', 'SOC'], medium: ['EDU', 'END'], low: ['DEX', 'STR'] }, diplomat: { high: ['INT', 'SOC'], medium: ['EDU', 'DEX'], low: ['STR', 'END'] }, entertainer: { high: ['DEX', 'SOC'], medium: ['INT', 'EDU'], low: ['STR', 'END'] }, trader: { high: ['INT', 'SOC'], medium: ['EDU', 'DEX'], low: ['STR', 'END'] }, thug: { high: ['STR', 'END'], medium: ['DEX', 'INT'], low: ['EDU', 'SOC'] } }; // Compétences pertinentes pour chaque rôle export const ROLE_SKILLS = { pilot: [ 'Pilot-Spacecraft', 'Astrogation', 'Electronics-Sensors', 'Gunner', 'Mechanic', 'Pilot-Small Craft', 'Leadership', 'Vacc Suit', 'Communications', 'Drive-Grav', 'Survival', 'Recon', 'Flyer' ], navigator: [ 'Astrogation', 'Electronics-Sensors', 'Pilot-Spacecraft', 'Computers', 'Survival', 'Navigation', 'Mechanic', 'Leadership', 'Tactics', 'Engineer', 'Vacc Suit', 'Recon' ], engineer: [ 'Engineer-MDrive', 'Mechanic', 'Engineer-Power', 'Computers', 'Engineer-JDrive', 'Engineer-Life Support', 'Electronics-Sensors', 'Survival', 'Pilot-Small Craft', 'Leadership', 'Vacc Suit', 'Recon', 'Drive' ], steward: [ 'Steward', 'Carouse', 'Persuade', 'Broker', 'Admin', 'Computers', 'Language', 'Advocate', 'Leadership', 'Medic', 'Streetwise', 'Diplomat' ], medic: [ 'Medic', 'Science-Biology', 'Science-Chemistry', 'Deception', 'Investigate', 'Diplomat', 'Computers', 'Persuade', 'Admin', 'Broker', 'Electronics-Sensors', 'Drive', 'Leadership' ], marine: [ 'Gun Combat', 'Survival', 'Athletics-Strength', 'Melee-Unarmed', 'Heavy Weapons', 'Tactics', 'Recon', 'Electronics-Sensors', 'Leadership', 'Medic', 'Drive-Grav', 'Communications', 'Stealth' ], gunner: [ 'Gunner-Turrets', 'Electronics-Sensors', 'Gunner-Screens', 'Tactics', 'Gun Combat', 'Leadership', 'Mechanic', 'Heavy Weapons', 'Explosives', 'Computers', 'Pilot-Small Craft', 'Athletics-Dexterity', 'Melee-Blade' ], scout: [ 'Survival', 'Recon', 'Pilot-Small Craft', 'Astrogation', 'Electronics-Sensors', 'Stealth', 'Gunner', 'Medic', 'Tactics', 'Gun Combat', 'Navigation', 'Leadership' ], technician: [ 'Mechanic', 'Computers', 'Electronics-Sensors', 'Engineer-Power', 'Engineer-MDrive', 'Drive', 'Pilot', 'Vacc Suit', 'Recon', 'Athletics-Dexterity', 'Survival', 'Explosives' ], leader: [ 'Leadership', 'Tactics', 'Admin', 'Diplomat', 'Persuade', 'Advocate', 'Electronics-Sensors', 'Computers', 'Deception', 'Pilot-Spacecraft', 'Engineer', 'Recon', 'Medic' ], diplomat: [ 'Diplomat', 'Persuade', 'Advocate', 'Admin', 'Carouse', 'Steward', 'Streetwise', 'Language', 'Broker', 'Leadership', 'Communications', 'Tactics' ], entertainer: [ 'Carouse', 'Streetwise', 'Art-Instrument', 'Persuade', 'Stealth', 'Deception', 'Diplomat', 'Art-Acting', 'Computers', 'Electronics-Sensors', 'Leadership', 'Broker', 'Melee-Blade', 'Admin' ], trader: [ 'Broker', 'Persuade', 'Admin', 'Advocate', 'Computers', 'Streetwise', 'Gun Combat', 'Diplomat', 'Deception', 'Carouse', 'Communications', 'Mechanic', 'Electronics-Sensors', 'Leadership' ], thug: [ 'Melee-Unarmed', 'Gun Combat', 'Melee-Blade', 'Athletics-Strength', 'Stealth', 'Streetwise', 'Carouse', 'Tactics', 'Stealth', 'Survival', 'Persuade', 'Explosives', 'Computers' ] }; // Liste des rôles avec libellés en français export const ROLE = { PILOT: { key: 'pilot', label: 'Pilote', description: 'Pilote de vaisseau spatial' }, NAVIGATOR: { key: 'navigator', label: 'Navigateur', description: 'Navigateur spatial' }, ENGINEER: { key: 'engineer', label: 'Ingénieur', description: 'Ingénieur de bord' }, STEWARD: { key: 'steward', label: 'Intendant', description: 'Intendant / steward' }, MEDIC: { key: 'medic', label: 'Médecin', description: 'Médecin de bord' }, MARINE: { key: 'marine', label: 'Marine', description: 'Marine / soldat' }, GUNNER: { key: 'gunner', label: 'Artilleur', description: 'Artilleur / canonnier' }, SCOUT: { key: 'scout', label: 'Éclaireur', description: 'Éclaireur' }, TECHNICIAN: { key: 'technician', label: 'Technicien', description: 'Technicien' }, LEADER: { key: 'leader', label: 'Chef', description: 'Chef / leader' }, DIPLOMAT: { key: 'diplomat', label: 'Diplomate', description: 'Diplomate' }, ENTERTAINER: { key: 'entertainer', label: 'Artiste', description: 'Artiste / divertisseur' }, TRADER: { key: 'trader', label: 'Marchand', description: 'Marchand / commerçant' }, THUG: { key: 'thug', label: 'Brute', description: 'Brute / voyou' } }; export const ROLE_LIST = [ ROLE.PILOT, ROLE.NAVIGATOR, ROLE.ENGINEER, ROLE.STEWARD, ROLE.MEDIC, ROLE.MARINE, ROLE.GUNNER, ROLE.SCOUT, ROLE.TECHNICIAN, ROLE.LEADER, ROLE.DIPLOMAT, ROLE.ENTERTAINER, ROLE.TRADER, ROLE.THUG ]; // ============================================================================ // Genre // ============================================================================ export const GENDER = { UNSPECIFIED: { key: 'unspecified', label: 'Non spécifié', value: 0 }, FEMALE: { key: 'female', label: 'Féminin', value: 1 }, MALE: { key: 'male', label: 'Masculin', value: 2 } }; export const GENDER_LIST = [ GENDER.UNSPECIFIED, GENDER.FEMALE, GENDER.MALE ]; // ============================================================================ // Catalogues de noms // ============================================================================ export const NAME_CATALOGS = { surnames: [ 'Anderson', 'Berezovsky', 'Brown', 'Chen', 'Clark', 'Davis', 'Fujita', 'Garcia', 'Gupta', 'Harris', 'Hicks', 'Ito', 'Ivanov', 'Jackson', 'Johnson', 'Jones', 'Kim', 'Kobayashi', 'Kowalski', 'Kumar', 'Kuznetsoff', 'Kuznetsov', 'Kuznetsova', 'Lee', 'Martin', 'Martinez', 'Miller', 'Moore', 'Nakamura', 'Nguyen', 'Nowak', "O'Brien", "O'Callaghan", "O'Connell", "O'Connor", "O'Keefe", "O'Leary", "O'Malley", "O'Neil", "O'Reilly", "O'Sullivan", 'Park', 'Patel', 'Pierzynski', 'Pietrzykowski', 'Pisarski', 'Reshevsky', 'Robinson', 'Rumkowska', 'Saito', 'Singh', 'Smith', 'Tanaka', 'Taylor', 'Thomas', 'Thompson', 'Vasquez', 'Watanabe', 'White', 'Williams', 'Wilson', 'Wong', 'Yamamoto', 'Yang' ], nonGenderedNames: [ 'Arrow', 'Artemis', 'Ash', 'Aster', 'Avery', 'Basil', 'Ever', 'Fig', 'Finch', 'Indigo', 'Jett', 'Juniper', 'Kavi', 'Kaviya', 'Kaviyan', 'Kaviyanan', 'Kay', 'Lark', 'Noah', 'Ocean', 'Phoenix', 'Riley', 'River', 'Rory', 'Rowan', 'Sage', 'Sawyer', 'Shiloh', 'Sparrow', 'Sutton', 'Tavi', 'Uli', 'Veer', 'Vesper', 'Winter', 'Wren', 'Zen', 'Zenith', 'Zephyr', 'Zephyrus' ], femaleNames: [ 'Aarohi', 'Aarushi', 'Abigail', 'Amelia', 'Ananya', 'Anika', 'Anjali', 'Anushka', 'Aria', 'Ava', 'Chloe', 'Devi', 'Elina', 'Elizabeth', 'Emily', 'Emma', 'Esha', 'Evelyn', 'Grace', 'Harper', 'Isabella', 'Ishani', 'Ishika', 'Ishita', 'Layla', 'Lily', 'Madison', 'Margarita', 'Maria', 'Mia', 'Nisha', 'Nora', 'Nosheen', 'Olivia', 'Penelope', 'Priya', 'Qadira', 'Riley', 'Scarlett', 'Sofia', 'Sophia', 'Tala', 'Ulka', 'Uthra', 'Uthraa', 'Victoria', 'Yasmin', 'Zara', 'Zoey', 'Zoya', 'Zulaikha' ], maleNames: [ 'Ahmed', 'Aiden', 'Alexander', 'Benjamin', 'Callum', 'Carter', 'Daniel', 'David', 'Dylan', 'Elias', 'Elijah', 'Ethan', 'Ewan', 'Ezra', 'Gavin', 'Henry', 'Hudson', 'Jackson', 'Jacob', 'Jaden', 'James', 'Jaxon', 'Jayden', 'Jordan', 'Joseph', 'Julian', 'Kian', 'Kianan', 'Kianu', 'Lachlan', 'Leo', 'Levi', 'Liam', 'Lincoln', 'Logan', 'Lucas', 'Mason', 'Mateo', 'Matthew', 'Michael', 'Mohammed', 'Noah', 'Nolan', 'Oliver', 'Pol', 'Samuel', 'Santiago', 'Sebastian', 'Theodore', 'Ulric', 'Wallid', 'William', 'Wyatt' ] }; // ============================================================================ // Caractéristiques // ============================================================================ export const CHARACTERISTIC = { STR: { key: 'STR', label: 'Force', mgt2eKey: 'STR' }, DEX: { key: 'DEX', label: 'Dextérité', mgt2eKey: 'DEX' }, END: { key: 'END', label: 'Endurance', mgt2eKey: 'END' }, INT: { key: 'INT', label: 'Intellect', mgt2eKey: 'INT' }, EDU: { key: 'EDU', label: 'Éducation', mgt2eKey: 'EDU' }, SOC: { key: 'SOC', label: 'Statut Social', mgt2eKey: 'SOC' } }; export const CHARACTERISTIC_LIST = [ CHARACTERISTIC.STR, CHARACTERISTIC.DEX, CHARACTERISTIC.END, CHARACTERISTIC.INT, CHARACTERISTIC.EDU, CHARACTERISTIC.SOC ]; // Ordre des caractéristiques pour l'UPP export const UPP_ORDER = ['STR', 'DEX', 'END', 'INT', 'EDU', 'SOC']; // ============================================================================ // Fonctions utilitaires // ============================================================================ /** * Convertit une valeur de caractéristique en code hexadécimal pour UPP * @param {number} value - Valeur de la caractéristique (0-15) * @returns {string} - Code hexadécimal */ export function toHex(value) { return Math.max(0, Math.min(15, Math.floor(value))).toString(16).toUpperCase(); } /** * Calcule le DM (Damage Modifier) à partir d'une valeur de caractéristique * @param {number} value - Valeur de la caractéristique * @returns {number} - Modificateur de dégâts */ export function calculateDm(value) { return Math.floor((value - 6) / 3); } /** * Génère un nom aléatoire à partir des catalogues * @param {string} genderKey - Clé du genre ('unspecified', 'female', 'male') * @returns {{firstName: string, surname: string, fullName: string}} */ export function generateRandomName(genderKey = 'unspecified') { const genderMap = { unspecified: NAME_CATALOGS.nonGenderedNames, female: NAME_CATALOGS.femaleNames, male: NAME_CATALOGS.maleNames }; const firstNames = genderMap[genderKey] || NAME_CATALOGS.nonGenderedNames; const surnames = NAME_CATALOGS.surnames; const firstName = pickRandomItem(firstNames); const surname = pickRandomItem(surnames); return { firstName, surname, fullName: `${firstName} ${surname}` }; } /** * Sélectionne un élément aléatoire dans un tableau * @template T * @param {T[]} items - Tableau d'éléments * @returns {T} - Élément sélectionné */ export function pickRandomItem(items) { if (!items || items.length === 0) { throw new Error('Cannot pick from empty array'); } const index = Math.floor(Math.random() * items.length); return items[index]; } /** * Mélange un tableau (algorithme de Fisher-Yates) * @template T * @param {T[]} array - Tableau à mélanger * @returns {T[]} - Nouveau tableau mélangé */ export function shuffleArray(array) { const newArray = [...array]; for (let i = newArray.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [newArray[i], newArray[j]] = [newArray[j], newArray[i]]; } return newArray; } /** * Extraire les N premiers éléments d'un tableau et retourner les deux parties * @template T * @param {T[]} array - Tableau source * @param {number} count - Nombre d'éléments à extraire * @returns {[T[], T[]]} - [éléments extraits, éléments restants] */ export function popRandomItems(array, count) { if (!array || array.length === 0 || count <= 0) { return [[], [...array]]; } const shuffled = shuffleArray(array); const taken = shuffled.slice(0, Math.min(count, shuffled.length)); const remaining = shuffled.slice(Math.min(count, shuffled.length)); return [taken, remaining]; } /** * Trouve un rôle par sa clé * @param {string} key - Clé du rôle * @returns {Object|null} - Objet rôle ou null */ export function getRoleByKey(key) { const found = ROLE_LIST.find(r => r.key === key); return found || ROLE.PILOT; } /** * Trouve une catégorie de citoyen par sa clé * @param {string} key - Clé de la catégorie * @returns {Object} - Objet catégorie */ export function getCitizenCategoryByKey(key) { const found = CITIZEN_CATEGORY_LIST.find(c => c.key === key); return found || CITIZEN_CATEGORY.AVERAGE; } /** * Trouve un niveau d'expérience par sa clé * @param {string} key - Clé du niveau * @returns {Object} - Objet niveau d'expérience */ export function getExperienceLevelByKey(key) { const found = EXPERIENCE_LEVEL_LIST.find(e => e.key === key); return found || EXPERIENCE_LEVEL.REGULAR; } /** * Trouve un genre par sa clé * @param {string} key - Clé du genre * @returns {Object} - Objet genre */ export function getGenderByKey(key) { const found = GENDER_LIST.find(g => g.key === key); return found || GENDER.UNSPECIFIED; } /** * Obtient les compétences pour un rôle * @param {string} roleKey - Clé du rôle * @returns {string[]} - Tableau de compétences */ export function getSkillsForRole(roleKey) { return ROLE_SKILLS[roleKey] || ROLE_SKILLS.pilot; } /** * Obtient les priorités de caractéristiques pour un rôle * @param {string} roleKey - Clé du rôle * @returns {Object} - Priorités de caractéristiques */ export function getCharacteristicPrioritiesForRole(roleKey) { return CHARACTERISTIC_PRIORITIES[roleKey] || CHARACTERISTIC_PRIORITIES.pilot; } // ============================================================================ // Données par défaut // ============================================================================ export const DEFAULT_OPTIONS = { citizenCategory: CITIZEN_CATEGORY.AVERAGE.key, experience: EXPERIENCE_LEVEL.REGULAR.key, role: ROLE.PILOT.key, gender: GENDER.UNSPECIFIED.key, createActor: false, actorName: '', openCreatedActor: true };