Migration vers le système officiel
This commit is contained in:
+106
-94
@@ -10,31 +10,40 @@ import {
|
||||
RANDOM_OPPOSITION_TABLE,
|
||||
ENCOUNTER_CONTEXTS,
|
||||
} from './data/npcTables.js';
|
||||
import { localizeSkill, setSkillLevel } from './mgt2eSkills.js';
|
||||
|
||||
const MODULE_ID = 'mgt2-compendium-amiral-denisov';
|
||||
const SKILL_PACK_ID = `${MODULE_ID}.competences`;
|
||||
|
||||
const CORE_CHARACTERISTICS = ['strength', 'dexterity', 'endurance', 'intellect', 'education', 'social'];
|
||||
const MGT2E_CHARACTERISTICS = {
|
||||
strength: 'STR',
|
||||
dexterity: 'DEX',
|
||||
endurance: 'END',
|
||||
intellect: 'INT',
|
||||
education: 'EDU',
|
||||
social: 'SOC',
|
||||
};
|
||||
const DEFAULT_PRIORITIES = {
|
||||
'Non-combattant': ['intellect', 'education', 'social', 'dexterity', 'endurance', 'strength'],
|
||||
'Combattant': ['dexterity', 'endurance', 'strength', 'education', 'intellect', 'social'],
|
||||
};
|
||||
let mgt2eBaseActorSystemPromise = null;
|
||||
|
||||
const ROLE_HINTS = [
|
||||
{ match: /médecin/i, skills: ['Médecine', 'Science'], priorities: ['education', 'intellect', 'dexterity'] },
|
||||
{ match: /scientifique|chercheur/i, skills: ['Science', 'Électronique'], priorities: ['intellect', 'education', 'dexterity'] },
|
||||
{ match: /diplomate|ambassadeur|attaché culturel/i, skills: ['Diplomatie', 'Langage', 'Persuader'], priorities: ['social', 'education', 'intellect'] },
|
||||
{ match: /marchand|franc-marchand|courtier/i, skills: ['Courtier', 'Administration', 'Persuader'], priorities: ['social', 'education', 'intellect'] },
|
||||
{ match: /mercenaire|seigneur de guerre/i, skills: ['Combat Arme', 'Mêlée', 'Reconnaissance'], priorities: ['dexterity', 'endurance', 'strength'] },
|
||||
{ match: /officier de marine|amiral|capitaine/i, skills: ['Leadership', 'Marin', 'Tactique'], priorities: ['education', 'social', 'intellect'] },
|
||||
{ match: /explorateur|éclaireur/i, skills: ['Reconnaissance', 'Survie', 'Navigation'], priorities: ['education', 'dexterity', 'endurance'] },
|
||||
{ match: /interprète|xéno/i, skills: ['Langage', 'Diplomatie', 'Science'], priorities: ['education', 'intellect', 'social'] },
|
||||
{ match: /cadre de corpo|agent corpo|administrateur|gouverneur|homme d'état|noble/i, skills: ['Administration', 'Leadership', 'Diplomatie'], priorities: ['social', 'education', 'intellect'] },
|
||||
{ match: /journaliste|enquêteur|inspecteur|agent impérial/i, skills: ['Enquêter', 'Persuader', 'Combat Arme'], priorities: ['intellect', 'education', 'dexterity'] },
|
||||
{ match: /conspirateur|criminel|contrebandier/i, skills: ['Duperie', 'Sens de la rue', 'Combat Arme'], priorities: ['social', 'dexterity', 'intellect'] },
|
||||
{ match: /chef religieux|cultiste/i, skills: ['Persuader', 'Leadership', 'Langage'], priorities: ['social', 'education', 'intellect'] },
|
||||
{ match: /joueur|playboy/i, skills: ['Flambeur', 'Mondanités', 'Persuader'], priorities: ['social', 'intellect', 'education'] },
|
||||
{ match: /intelligence artificielle/i, skills: ['Électronique', 'Science', 'Profession'], priorities: ['intellect', 'education', 'social'] },
|
||||
{ match: /médecin/i, skills: ['medic', 'science'], priorities: ['education', 'intellect', 'dexterity'] },
|
||||
{ match: /scientifique|chercheur/i, skills: ['science', 'electronics'], priorities: ['intellect', 'education', 'dexterity'] },
|
||||
{ match: /diplomate|ambassadeur|attaché culturel/i, skills: ['diplomat', 'language', 'persuade'], priorities: ['social', 'education', 'intellect'] },
|
||||
{ match: /marchand|franc-marchand|courtier/i, skills: ['broker', 'admin', 'persuade'], priorities: ['social', 'education', 'intellect'] },
|
||||
{ match: /mercenaire|seigneur de guerre/i, skills: ['guncombat', 'melee', 'recon'], priorities: ['dexterity', 'endurance', 'strength'] },
|
||||
{ match: /officier de marine|amiral|capitaine/i, skills: ['leadership', 'seafarer', 'tactics'], priorities: ['education', 'social', 'intellect'] },
|
||||
{ match: /explorateur|éclaireur/i, skills: ['recon', 'survival', 'navigation'], priorities: ['education', 'dexterity', 'endurance'] },
|
||||
{ match: /interprète|xéno/i, skills: ['language', 'diplomat', 'science'], priorities: ['education', 'intellect', 'social'] },
|
||||
{ match: /cadre de corpo|agent corpo|administrateur|gouverneur|homme d'état|noble/i, skills: ['admin', 'leadership', 'diplomat'], priorities: ['social', 'education', 'intellect'] },
|
||||
{ match: /journaliste|enquêteur|inspecteur|agent impérial/i, skills: ['investigate', 'persuade', 'guncombat'], priorities: ['intellect', 'education', 'dexterity'] },
|
||||
{ match: /conspirateur|criminel|contrebandier/i, skills: ['deception', 'streetwise', 'guncombat'], priorities: ['social', 'dexterity', 'intellect'] },
|
||||
{ match: /chef religieux|cultiste/i, skills: ['persuade', 'leadership', 'language'], priorities: ['social', 'education', 'intellect'] },
|
||||
{ match: /joueur|playboy/i, skills: ['gambler', 'carouse', 'persuade'], priorities: ['social', 'intellect', 'education'] },
|
||||
{ match: /intelligence artificielle/i, skills: ['electronics', 'science', 'profession'], priorities: ['intellect', 'education', 'social'] },
|
||||
];
|
||||
|
||||
function getD66Entry(entries, total) {
|
||||
@@ -126,9 +135,9 @@ async function generateExperience(mode = 'random') {
|
||||
}
|
||||
|
||||
function findRoleHint(roleName, category) {
|
||||
const hint = ROLE_HINTS.find((entry) => entry.match.test(roleName));
|
||||
const hint = ROLE_HINTS.find((entry) => entry.match.test(roleName));
|
||||
return hint ?? {
|
||||
skills: ['Profession'],
|
||||
skills: ['profession'],
|
||||
priorities: DEFAULT_PRIORITIES[category] ?? DEFAULT_PRIORITIES['Non-combattant'],
|
||||
};
|
||||
}
|
||||
@@ -158,41 +167,6 @@ function buildCharacteristicValues(result) {
|
||||
};
|
||||
}
|
||||
|
||||
function buildCharacteristicsData(values) {
|
||||
const allKeys = {
|
||||
strength: { showMax: true },
|
||||
dexterity: { showMax: true },
|
||||
endurance: { showMax: true },
|
||||
intellect: { showMax: false },
|
||||
education: { showMax: false },
|
||||
social: { showMax: false },
|
||||
morale: { showMax: false, value: 0 },
|
||||
luck: { showMax: false, value: 0 },
|
||||
sanity: { showMax: false, value: 0 },
|
||||
charm: { showMax: false, value: 0 },
|
||||
psionic: { showMax: false, value: 0 },
|
||||
other: { showMax: false, value: 0 },
|
||||
};
|
||||
|
||||
return Object.fromEntries(Object.entries(allKeys).map(([key, config]) => {
|
||||
const value = values[key] ?? config.value ?? 0;
|
||||
return [key, {
|
||||
value,
|
||||
max: value,
|
||||
dm: calculateDm(value),
|
||||
show: true,
|
||||
showMax: config.showMax,
|
||||
}];
|
||||
}));
|
||||
}
|
||||
|
||||
async function getSkillPackIndex() {
|
||||
const pack = game.packs.get(SKILL_PACK_ID);
|
||||
if (!pack) throw new Error(`Pack de compétences introuvable : ${SKILL_PACK_ID}`);
|
||||
const index = await pack.getIndex();
|
||||
return { pack, index };
|
||||
}
|
||||
|
||||
function mergeSkillLevels(profileSkills, roleSkills, baseLevel) {
|
||||
const levels = new Map();
|
||||
|
||||
@@ -202,36 +176,69 @@ function mergeSkillLevels(profileSkills, roleSkills, baseLevel) {
|
||||
return levels;
|
||||
}
|
||||
|
||||
async function buildSkillItems(result) {
|
||||
const { pack, index } = await getSkillPackIndex();
|
||||
const { hint } = buildCharacteristicValues(result);
|
||||
const skillLevels = mergeSkillLevels(result.experience.profile.skills, hint.skills, result.experience.profile.skillLevel);
|
||||
const items = [];
|
||||
function buildMgt2eCharacteristics(existingCharacteristics = {}, values) {
|
||||
const characteristics = foundry.utils.deepClone(existingCharacteristics);
|
||||
|
||||
for (const [skillName, level] of skillLevels.entries()) {
|
||||
const entry = index.contents.find((item) => item.name === skillName);
|
||||
if (!entry) continue;
|
||||
const document = await pack.getDocument(entry._id);
|
||||
const data = document.toObject();
|
||||
delete data._id;
|
||||
delete data.folder;
|
||||
data.system.level = level;
|
||||
items.push(data);
|
||||
for (const [legacyKey, targetKey] of Object.entries(MGT2E_CHARACTERISTICS)) {
|
||||
const value = values[legacyKey] ?? 7;
|
||||
characteristics[targetKey] = foundry.utils.mergeObject(characteristics[targetKey] ?? {}, {
|
||||
value,
|
||||
current: value,
|
||||
dm: calculateDm(value),
|
||||
show: true,
|
||||
default: false,
|
||||
});
|
||||
}
|
||||
|
||||
return items;
|
||||
return characteristics;
|
||||
}
|
||||
|
||||
function buildMgt2eSkills(existingSkills = {}, result) {
|
||||
const skills = foundry.utils.deepClone(existingSkills);
|
||||
const { hint } = buildCharacteristicValues(result);
|
||||
const skillLevels = mergeSkillLevels(result.experience.profile.skills, hint.skills, result.experience.profile.skillLevel);
|
||||
|
||||
for (const [skillFqn, level] of skillLevels.entries()) {
|
||||
setSkillLevel(skills, skillFqn, level);
|
||||
}
|
||||
|
||||
return skills;
|
||||
}
|
||||
|
||||
function buildActorDescription(result, actorName, ucp) {
|
||||
const { hint } = buildCharacteristicValues(result);
|
||||
const notableSkills = mergeSkillLevels(result.experience.profile.skills, hint.skills, result.experience.profile.skillLevel);
|
||||
return [
|
||||
`${actorName} — ${result.role.entry.text}`,
|
||||
`Relation : ${result.relation.label}`,
|
||||
`Particularité : ${result.quirk.entry.text}`,
|
||||
`Expérience : ${result.experience.profile.label}`,
|
||||
`UCP : ${ucp}`,
|
||||
`UPP : ${ucp}`,
|
||||
`Compétences clés : ${Array.from(notableSkills.keys()).map((skill) => localizeSkill(skill)).join(', ')}`,
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
async function getMgt2eBaseActorSystem() {
|
||||
if (!mgt2eBaseActorSystemPromise) {
|
||||
mgt2eBaseActorSystemPromise = (async () => {
|
||||
const pack = game.packs.get('mgt2e.base-actors');
|
||||
if (!pack) return null;
|
||||
|
||||
const index = Array.from(await pack.getIndex({ fields: ['name', 'type'] }));
|
||||
const entry = index.find((document) => document.name === 'DEFAULT TRAVELLER')
|
||||
?? index.find((document) => document.type === 'traveller')
|
||||
?? index[0];
|
||||
if (!entry?._id) return null;
|
||||
|
||||
const document = await pack.getDocument(entry._id);
|
||||
return document?.toObject()?.system ?? null;
|
||||
})();
|
||||
}
|
||||
|
||||
const system = await mgt2eBaseActorSystemPromise;
|
||||
return system ? foundry.utils.deepClone(system) : null;
|
||||
}
|
||||
|
||||
export function formatSigned(value) {
|
||||
return value >= 0 ? `+${value}` : `${value}`;
|
||||
}
|
||||
@@ -254,32 +261,27 @@ export async function generateQuickNpc(params = {}) {
|
||||
}
|
||||
|
||||
export async function createNpcActor(result, options = {}) {
|
||||
const actorName = options.name?.trim() || `PNJ — ${result.role.entry.text}`;
|
||||
const requestedName = options.name?.trim();
|
||||
const { values, ucp } = buildCharacteristicValues(result);
|
||||
const items = await buildSkillItems(result);
|
||||
const actor = await Actor.create({
|
||||
name: actorName,
|
||||
type: 'character',
|
||||
img: 'icons/svg/mystery-man.svg',
|
||||
const baseActorSystem = game.system?.id === 'mgt2e' ? await getMgt2eBaseActorSystem() : null;
|
||||
const npcData = {
|
||||
name: requestedName || `PNJ — ${result.role.entry.text}`,
|
||||
type: 'npc',
|
||||
img: 'systems/mgt2e/icons/cargo/passenger-middle.svg',
|
||||
system: {
|
||||
life: {
|
||||
value: values.endurance,
|
||||
max: values.endurance,
|
||||
},
|
||||
personal: {
|
||||
title: result.role.entry.text,
|
||||
species: '',
|
||||
speciesText: {},
|
||||
age: '',
|
||||
ucp,
|
||||
traits: [
|
||||
{ name: result.relation.label },
|
||||
{ name: result.quirk.entry.text },
|
||||
],
|
||||
},
|
||||
characteristics: buildCharacteristicsData(values),
|
||||
biography: buildActorDescription(result, actorName, ucp),
|
||||
notes: buildActorDescription(result, actorName, ucp),
|
||||
settings: foundry.utils.mergeObject(foundry.utils.deepClone(baseActorSystem?.settings ?? {}), {
|
||||
hideUntrained: true,
|
||||
lockCharacteristics: true,
|
||||
}),
|
||||
sophont: foundry.utils.mergeObject(foundry.utils.deepClone(baseActorSystem?.sophont ?? {}), {
|
||||
age: 18,
|
||||
homeworld: '',
|
||||
profession: result.role.entry.text,
|
||||
}),
|
||||
characteristics: foundry.utils.deepClone(baseActorSystem?.characteristics ?? {}),
|
||||
hits: foundry.utils.deepClone(baseActorSystem?.hits ?? {}),
|
||||
skills: foundry.utils.deepClone(baseActorSystem?.skills ?? {}),
|
||||
description: '',
|
||||
},
|
||||
flags: {
|
||||
[MODULE_ID]: {
|
||||
@@ -288,12 +290,22 @@ export async function createNpcActor(result, options = {}) {
|
||||
role: result.role.entry.text,
|
||||
quirk: result.quirk.entry.text,
|
||||
experience: result.experience.profile.label,
|
||||
upp: ucp,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, { renderSheet: false });
|
||||
};
|
||||
|
||||
npcData.name = requestedName || npcData.name || `PNJ — ${result.role.entry.text}`;
|
||||
npcData.system.sophont = foundry.utils.mergeObject(npcData.system.sophont ?? {}, {
|
||||
profession: result.role.entry.text,
|
||||
});
|
||||
npcData.system.characteristics = buildMgt2eCharacteristics(npcData.system.characteristics, values);
|
||||
npcData.system.skills = buildMgt2eSkills(npcData.system.skills, result);
|
||||
npcData.system.description = buildActorDescription(result, npcData.name, ucp).replace(/\n/g, '<br>');
|
||||
|
||||
const actor = await Actor.create(npcData, { renderSheet: false });
|
||||
|
||||
if (items.length) await actor.createEmbeddedDocuments('Item', items);
|
||||
if (options.openSheet !== false) actor.sheet?.render(true);
|
||||
return actor;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user