Files
mgt2-compendium-amiral-denisov/scripts/mgt2eSkills.js
T

181 lines
6.4 KiB
JavaScript

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,
};
}