Migration vers le système officiel
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
const SKILL_LOCALIZATION_PREFIX = 'MGT2.Skills.';
|
||||
|
||||
export const COMMERCE_SKILLS = Object.freeze({
|
||||
passengerEffect: ['carouse', 'broker', 'streetwise'],
|
||||
cargoEffect: ['broker', 'streetwise'],
|
||||
tradeBroker: 'broker',
|
||||
steward: 'steward',
|
||||
});
|
||||
|
||||
export function splitSkillFqn(skillFqn = '') {
|
||||
const [skillId = '', specialityId = ''] = String(skillFqn ?? '').split('.');
|
||||
return { skillId, specialityId };
|
||||
}
|
||||
|
||||
export function localizeSkill(skillFqn, fallback = '') {
|
||||
const { skillId, specialityId } = splitSkillFqn(skillFqn);
|
||||
if (!skillId) return fallback;
|
||||
|
||||
const skillLabel = localizeSkillId(skillId);
|
||||
if (!specialityId) return skillLabel;
|
||||
return `${skillLabel} (${localizeSkillId(specialityId)})`;
|
||||
}
|
||||
|
||||
export function getActiveTravellerActor() {
|
||||
const controlled = canvas?.tokens?.controlled?.find((token) => token.actor?.type === 'traveller' || token.actor?.type === 'npc')?.actor;
|
||||
if (controlled) return { actor: controlled, source: 'token' };
|
||||
|
||||
if (game.user?.character?.type === 'traveller' || game.user?.character?.type === 'npc') {
|
||||
return { actor: game.user.character, source: 'character' };
|
||||
}
|
||||
|
||||
const owned = game.actors?.find((actor) => actor.isOwner && (actor.type === 'traveller' || actor.type === 'npc'));
|
||||
if (owned) return { actor: owned, source: 'owned' };
|
||||
|
||||
return { actor: null, source: null };
|
||||
}
|
||||
|
||||
export function getActorSkillSummary(actor, skillFqn) {
|
||||
if (!actor?.system?.skills) return buildEmptySkillSummary(skillFqn);
|
||||
|
||||
const { skillId, specialityId } = splitSkillFqn(skillFqn);
|
||||
const skill = actor.system.skills[skillId];
|
||||
if (!skill) return buildEmptySkillSummary(skillFqn);
|
||||
|
||||
const speciality = specialityId ? skill.specialities?.[specialityId] ?? null : null;
|
||||
const characteristic = speciality?.default || skill.default || null;
|
||||
const characteristicDm = characteristic ? Number(actor.system.characteristics?.[characteristic]?.dm ?? 0) : 0;
|
||||
const trained = Boolean(skill.trained) || Number(skill.value ?? 0) > 0 || Number(speciality?.value ?? 0) > 0;
|
||||
const value = speciality ? Number(speciality.value ?? 0) : Number(skill.value ?? 0);
|
||||
const jackOfAllTrades = Number(actor.system.skills?.jackofalltrades?.value ?? 0);
|
||||
const rollValue = trained ? value : jackOfAllTrades - 3;
|
||||
|
||||
return {
|
||||
skillFqn,
|
||||
skillId,
|
||||
specialityId,
|
||||
label: localizeSkill(skillFqn, skill.label || skillId),
|
||||
value,
|
||||
rollValue,
|
||||
trained,
|
||||
characteristic,
|
||||
characteristicDm,
|
||||
totalModifier: rollValue + characteristicDm,
|
||||
};
|
||||
}
|
||||
|
||||
export function getActorCharacteristicSummary(actor, characteristicId) {
|
||||
if (!actor?.system?.characteristics?.[characteristicId]) {
|
||||
return { id: characteristicId, value: 0, dm: 0, label: characteristicId };
|
||||
}
|
||||
|
||||
const characteristic = actor.system.characteristics[characteristicId];
|
||||
return {
|
||||
id: characteristicId,
|
||||
value: Number(characteristic.value ?? 0),
|
||||
dm: Number(characteristic.dm ?? 0),
|
||||
label: game.i18n.localize(`MGT2.Characteristics.${characteristicId}`),
|
||||
};
|
||||
}
|
||||
|
||||
export function getBestActorSkillSummary(actor, skillList = []) {
|
||||
const summaries = skillList
|
||||
.map((skillFqn) => getActorSkillSummary(actor, skillFqn))
|
||||
.filter((summary) => summary.skillId);
|
||||
|
||||
if (!summaries.length) return null;
|
||||
|
||||
return summaries.sort((left, right) => {
|
||||
if (right.totalModifier !== left.totalModifier) return right.totalModifier - left.totalModifier;
|
||||
if (right.value !== left.value) return right.value - left.value;
|
||||
return left.label.localeCompare(right.label, 'fr');
|
||||
})[0];
|
||||
}
|
||||
|
||||
export async function rollActorSkillEffect(actor, skillList = [], difficulty = 8) {
|
||||
const summary = getBestActorSkillSummary(actor, skillList);
|
||||
if (!summary) return null;
|
||||
|
||||
const roll = await new Roll('2d6').evaluate();
|
||||
const total = Number(roll.total ?? 0) + summary.totalModifier;
|
||||
return {
|
||||
...summary,
|
||||
diceTotal: Number(roll.total ?? 0),
|
||||
total,
|
||||
effect: total - difficulty,
|
||||
difficulty,
|
||||
};
|
||||
}
|
||||
|
||||
export function setSkillLevel(skills, skillFqn, level) {
|
||||
const { skillId, specialityId } = splitSkillFqn(skillFqn);
|
||||
if (!skillId || !skills?.[skillId]) return skills;
|
||||
|
||||
const numericLevel = Number(level ?? 0);
|
||||
const skill = foundry.utils.mergeObject(skills[skillId], { trained: numericLevel > 0 || skills[skillId].trained });
|
||||
|
||||
if (specialityId && skill.specialities?.[specialityId]) {
|
||||
skill.specialities[specialityId] = foundry.utils.mergeObject(skill.specialities[specialityId], {
|
||||
value: Math.max(Number(skill.specialities[specialityId].value ?? 0), numericLevel),
|
||||
});
|
||||
} else {
|
||||
skill.value = Math.max(Number(skill.value ?? 0), numericLevel);
|
||||
}
|
||||
|
||||
skills[skillId] = skill;
|
||||
return skills;
|
||||
}
|
||||
|
||||
export function buildActiveActorContext() {
|
||||
const { actor, source } = getActiveTravellerActor();
|
||||
if (!actor) return null;
|
||||
|
||||
return {
|
||||
id: actor.id,
|
||||
name: actor.name,
|
||||
source,
|
||||
sourceLabel: source === 'token' ? 'token sélectionné' : source === 'character' ? 'personnage assigné' : 'acteur possédé',
|
||||
broker: getActorSkillSummary(actor, 'broker'),
|
||||
carouse: getActorSkillSummary(actor, 'carouse'),
|
||||
streetwise: getActorSkillSummary(actor, 'streetwise'),
|
||||
steward: getActorSkillSummary(actor, 'steward'),
|
||||
soc: getActorCharacteristicSummary(actor, 'SOC'),
|
||||
};
|
||||
}
|
||||
|
||||
export function inferWeaponSkillFromName(name, melee) {
|
||||
const label = String(name ?? '').toLowerCase();
|
||||
if (melee) {
|
||||
if (/(sabre|épée|epee|lame|poignard|couteau|fleuret|rapiere|rapière)/i.test(label)) return 'melee.blade';
|
||||
if (/(massue|matraque|bâton|baton|gourdin|marteau|masse)/i.test(label)) return 'melee.bludgeon';
|
||||
if (/(griffe|morsure|corne|tentacule|naturel)/i.test(label)) return 'melee.natural';
|
||||
return 'melee.unarmed';
|
||||
}
|
||||
|
||||
if (/(laser|plasma|fusion|particule|meson)/i.test(label)) return 'guncombat.energy';
|
||||
if (/(arc|arbal[eè]te|javelot|lance[- ]?harpon)/i.test(label)) return 'guncombat.archaic';
|
||||
if (/(canon|lance[- ]?grenade|missile|mortier|roquette)/i.test(label)) return 'heavyweapons.portable';
|
||||
return 'guncombat.slug';
|
||||
}
|
||||
|
||||
function localizeSkillId(skillId) {
|
||||
const localized = game.i18n.localize(`${SKILL_LOCALIZATION_PREFIX}${skillId}`);
|
||||
return localized.startsWith(SKILL_LOCALIZATION_PREFIX) ? skillId : localized;
|
||||
}
|
||||
|
||||
function buildEmptySkillSummary(skillFqn) {
|
||||
const { skillId, specialityId } = splitSkillFqn(skillFqn);
|
||||
return {
|
||||
skillFqn,
|
||||
skillId,
|
||||
specialityId,
|
||||
label: localizeSkill(skillFqn, skillId),
|
||||
value: 0,
|
||||
rollValue: -3,
|
||||
trained: false,
|
||||
characteristic: null,
|
||||
characteristicDm: 0,
|
||||
totalModifier: -3,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user