Files
mgt2-compendium-amiral-denisov/scripts/data/travellerNpcGenerator.js
T
uberwald 76870c27bf Corrections, styles et traductions FR pour /gennpc - v1.3.0
Corrections critiques implémentées:
- Remplacement du cache global mutable par ModuleCache
- Binding des méthodes dans TravellerNpcDialog
- Suppression des ré-exports circulaires
- Validation complète des options
- Correction: Duplicate export de TravellerNpcDialog et openTravellerNpcDialog
- Correction: distributeSkillLevels ne supprime plus les spécialisations
  (ex: Pilot-Spacecraft ET Pilot-Small Craft sont maintenant conservées)

Améliorations majeures:
- Optimisation de l'algorithme de distribution des compétences (single-pass)
- Optimisation de la génération des caractéristiques (priorité-based)
- Gestion d'erreur améliorée avec TravellerNpcError
- Création de TravellerNpcUtils.js avec classes utilitaires

Améliorations mineures:
- CSS aligné avec les styles des dialogues /commerce et /pnj
- Thème clair cohérent (#f5f0e8 background, #222 text)
- Fieldset, onglets, formulaires alignés sur mgt2-npc-form
- Boutons et résultats stylisés comme mgt2-npc-result
- Suppression des styles inline redondants dans _applyThemeStyles
- Design réactif, accessibilité, impression
- Tests unitaires complets pour toutes les fonctions

Traductions en français:
- Ajout de SKILL_LABELS_FR pour toutes les compétences Traveller
- Ajout de CHARACTERISTIC_LABELS_FR pour STR, DEX, END, INT, EDU, SOC
- Ajout de CITIZEN_CATEGORY_LABELS_FR, EXPERIENCE_LEVEL_LABELS_FR
- Ajout de ROLE_LABELS_FR, GENDER_LABELS_FR
- Mise à jour de generateTravellerNpc pour utiliser les libellés français
- Mise à jour du template traveller-npc-result.hbs pour afficher labelFr
- Mise à jour du template traveller-npc-dialog.hbs avec libellés français
- Mise à jour de TravellerNpcDialog._prepareContext pour utiliser les libellés FR

Vérification des compétences:
 Pilot: 13 compétences (avant: 12, perdait Pilot-Small Craft)
 Engineer: 13 compétences (avant: 10, perdait 3 spécialisations)
 Tous les rôles conservent leurs spécialisations

Fichiers ajoutés:
- scripts/utils/travellerNpcUtils.js
- scripts/tests/travellerNpcGenerator.test.js

Fichiers modifiés:
- scripts/data/travellerNpcGenerator.js (+ traductions FR)
- scripts/travellerNpcGenerator.js (+ fonctions getSkillLabelFr, getCharacteristicLabelFr)
- scripts/TravellerNpcDialog.js (libellés FR dans _prepareContext)
- scripts/npc.js
- styles/traveller-npc.css
- templates/traveller-npc-dialog.hbs
- templates/traveller-npc-result.hbs
- module.json

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-05-28 00:03:44 +02:00

881 lines
23 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 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',
'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'];
// ============================================================================
// Codes d'erreur
// ============================================================================
export const ERROR_CODES = {
INVALID_OPTIONS: 'INVALID_OPTIONS',
INVALID_ROLE: 'INVALID_ROLE',
INVALID_CATEGORY: 'INVALID_CATEGORY',
INVALID_EXPERIENCE: 'INVALID_EXPERIENCE',
INVALID_GENDER: 'INVALID_GENDER',
ACTOR_CREATION_FAILED: 'ACTOR_CREATION_FAILED',
MGT2E_NOT_ACTIVE: 'MGT2E_NOT_ACTIVE',
BASE_ACTOR_NOT_FOUND: 'BASE_ACTOR_NOT_FOUND'
};
// ============================================================================
// 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} - Objet rôle
*/
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;
}
/**
* Valide les options de génération
* @param {Object} options - Options à valider
* @returns {Object} - Options validées
*/
export function validateOptions(options = {}) {
const errors = [];
const validated = { ...options };
if (validated.citizenCategory && !getCitizenCategoryByKey(validated.citizenCategory)) {
errors.push(`Catégorie de citoyen invalide: ${validated.citizenCategory}`);
validated.citizenCategory = DEFAULT_OPTIONS.citizenCategory;
}
if (validated.experience && !getExperienceLevelByKey(validated.experience)) {
errors.push(`Niveau d'expérience invalide: ${validated.experience}`);
validated.experience = DEFAULT_OPTIONS.experience;
}
if (validated.role && !getRoleByKey(validated.role)) {
errors.push(`Rôle invalide: ${validated.role}`);
validated.role = DEFAULT_OPTIONS.role;
}
if (validated.gender && !getGenderByKey(validated.gender)) {
errors.push(`Genre invalide: ${validated.gender}`);
validated.gender = DEFAULT_OPTIONS.gender;
}
if (errors.length > 0) {
console.warn(`${MODULE_ID} | Options de génération invalides:`, errors);
}
return validated;
}
// ============================================================================
// 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
};
// ============================================================================
// Traductions françaises des compétences Traveller
// ============================================================================
/**
* Libellés français des compétences Traveller
* Basé sur les traductions du système mgt2e et les standards Traveller FR
*/
export const SKILL_LABELS_FR = {
'Pilot-Spacecraft': 'Pilote Vaisseau spatial',
'Pilot-Small Craft': 'Pilote Aéronef léger',
'Pilot': 'Pilote',
'Flyer': 'Pilote Aéronef atmosphérique',
'Astrogation': 'Astrogation',
'Navigation': 'Navigation',
'Electronics-Sensors': 'Électronique Capteurs',
'Electronics-Communications': 'Électronique Communications',
'Electronics-Computers': 'Électronique Informatique',
'Electronics': 'Électronique',
'Computers': 'Informatique',
'Gunner-Turrets': 'Artilleur Tourelles',
'Gunner-Screens': 'Artilleur Boucliers',
'Gunner': 'Artilleur',
'Gun Combat': 'Combat aux armes à feu',
'Heavy Weapons': 'Armes lourdes',
'Explosives': 'Explosifs',
'Mechanic': 'Mécanique',
'Engineer-MDrive': 'Ingénieur Propulsion manœuvre',
'Engineer-Power': 'Ingénieur Énergie',
'Engineer-JDrive': 'Ingénieur Propulsion saut',
'Engineer-Life Support': 'Ingénieur Support vie',
'Engineer': 'Ingénieur',
'Steward': 'Intendant',
'Carouse': 'Festoyer',
'Persuade': 'Persuasion',
'Broker': 'Courtage',
'Admin': 'Administration',
'Advocate': 'Plaidoyer',
'Diplomat': 'Diplomatie',
'Streetwise': 'Rues',
'Leadership': 'Direction',
'Science-Biology': 'Science Biologie',
'Science-Chemistry': 'Science Chimie',
'Science': 'Science',
'Medic': 'Médecine',
'Deception': 'Tromperie',
'Investigate': 'Investigation',
'Melee-Unarmed': 'Mêlée Sans arme',
'Melee-Blade': 'Mêlée Arme blanche',
'Melee': 'Mêlée',
'Athletics-Strength': 'Athlétisme Force',
'Athletics-Dexterity': 'Athlétisme Dextérité',
'Athletics': 'Athlétisme',
'Tactics': 'Tactiques',
'Recon': 'Reconnaissance',
'Survival': 'Survie',
'Stealth': 'Discrétion',
'Communications': 'Communications',
'Drive-Grav': 'Conduite Gravité',
'Drive': 'Conduite',
'Vacc Suit': 'Combinaison spatiale',
'Language': 'Langue',
'Art-Acting': 'Art Jeu d\'acteur',
'Art-Instrument': 'Art Instrument',
'Art': 'Art'
};
export const CHARACTERISTIC_LABELS_FR = {
'STR': 'Force',
'DEX': 'Dextérité',
'END': 'Endurance',
'INT': 'Intellect',
'EDU': 'Éducation',
'SOC': 'Statut Social'
};
export const CITIZEN_CATEGORY_LABELS_FR = {
'belowAverage': 'En dessous de la moyenne',
'average': 'Moyenne',
'aboveAverage': 'Au-dessus de la moyenne',
'exceptional': 'Exceptionnel'
};
export const EXPERIENCE_LEVEL_LABELS_FR = {
'recruit': 'Recrue',
'rookie': 'Débutant',
'intermediate': 'Intermédiaire',
'regular': 'Régulier',
'veteran': 'Vétéran',
'elite': 'Élite'
};
export const ROLE_LABELS_FR = {
'pilot': 'Pilote',
'navigator': 'Navigateur',
'engineer': 'Ingénieur',
'steward': 'Intendant',
'medic': 'Médecin',
'marine': 'Marine',
'gunner': 'Artilleur',
'scout': 'Éclaireur',
'technician': 'Technicien',
'leader': 'Chef',
'diplomat': 'Diplomate',
'entertainer': 'Artiste',
'trader': 'Marchand',
'thug': 'Brute'
};
export const GENDER_LABELS_FR = {
'unspecified': 'Non spécifié',
'female': 'Féminin',
'male': 'Masculin'
};