foundryvtt-reve-de-dragon/module/item-competence.js
Vincent Vandemeulebrouck bafc52a151 Fix: recherches incorrectes
* cas rare d'un personnage avec carac reve-actuel défini, mais sans
label, qui rend impossible de trouver une autre caractéristique, ce
qui empêche tout jet de caractéristique

* '/rdd <carac> <comp>', quand plusieurs compétences peuvent
correspondre, la première devrait être choisie... mais en pratique,
échec et rien ne se passe
2022-07-08 01:22:53 +02:00

276 lines
10 KiB
JavaScript

import { Grammar } from "./grammar.js";
import { Misc } from "./misc.js";
const competenceTroncs = [["Esquive", "Dague", "Corps à corps"],
["Epée à 1 main", "Epée à 2 mains", "Hache à 1 main", "Hache à 2 mains", "Lance", "Masse à 1 main", "Masse à 2 mains"]];
const xp_par_niveau = [5, 5, 5, 10, 10, 10, 10, 15, 15, 15, 15, 20, 20, 20, 20, 30, 30, 40, 40, 60, 60, 100, 100, 100, 100, 100, 100, 100, 100, 100];
const niveau_max = xp_par_niveau.length - 10;
/* -------------------------------------------- */
const limitesArchetypes = [
{ "niveau": 0, "nombreMax": 100, "nombre": 0 },
{ "niveau": 1, "nombreMax": 10, "nombre": 0 },
{ "niveau": 2, "nombreMax": 9, "nombre": 0 },
{ "niveau": 3, "nombreMax": 8, "nombre": 0 },
{ "niveau": 4, "nombreMax": 7, "nombre": 0 },
{ "niveau": 5, "nombreMax": 6, "nombre": 0 },
{ "niveau": 6, "nombreMax": 5, "nombre": 0 },
{ "niveau": 7, "nombreMax": 4, "nombre": 0 },
{ "niveau": 8, "nombreMax": 3, "nombre": 0 },
{ "niveau": 9, "nombreMax": 2, "nombre": 0 },
{ "niveau": 10, "nombreMax": 1, "nombre": 0 },
{ "niveau": 11, "nombreMax": 1, "nombre": 0 }
];
/* -------------------------------------------- */
const categorieCompetences = {
"generale": { base: -4, label: "Générales" },
"particuliere": { base: -8, label: "Particulières" },
"specialisee": { base: -11, label: "Spécialisées" },
"connaissance": { base: -11, label: "Connaissances" },
"draconic": { base: -11, label: "Draconics" },
"melee": { base: -6, label: "Mêlée" },
"tir": { base: -8, label: "Tir" },
"lancer": { base: -8, label: "Lancer" }
}
const compendiumCompetences = {
"personnage": "foundryvtt-reve-de-dragon.competences",
"creature": "foundryvtt-reve-de-dragon.competences-creatures",
"entite": "foundryvtt-reve-de-dragon.competences-entites"
};
function _buildCumulXP() {
let cumulXP = { "-11": 0 };
let cumul = 0;
for (let i = 0; i <= xp_par_niveau.length; i++) {
let level = i - 10;
cumul += xp_par_niveau[i];
cumulXP[level] = cumul;
}
return cumulXP;
}
const competence_xp_cumul = _buildCumulXP();
export class RdDItemCompetence extends Item {
/* -------------------------------------------- */
static actorCompendium(actorType) {
return compendiumCompetences[actorType];
}
/* -------------------------------------------- */
static getCategorieCompetences() {
return categorieCompetences;
}
/* -------------------------------------------- */
static getNiveauBase(category) {
return categorieCompetences[category].base;
}
/* -------------------------------------------- */
static getLabelCategorie(category) {
return categorieCompetences[category].label;
}
/* -------------------------------------------- */
static getCategorie(competence) {
return Misc.data(competence)?.data.categorie;
}
static isDraconic(competence) {
return Misc.data(competence)?.data.categorie == 'draconic';
}
/* -------------------------------------------- */
static getVoieDraconic(competences, voie) {
return RdDItemCompetence.findCompetence(competences.filter(it => RdDItemCompetence.isDraconic(it)), voie);
}
/* -------------------------------------------- */
static isCompetenceArme(competence) {
switch (Misc.templateData(competence).categorie) {
case 'melee':
return Misc.data(competence).name != 'Esquive';
case 'tir':
case 'lancer':
return true;
}
return false;
}
/* -------------------------------------------- */
static isArmeUneMain(competence) {
return Misc.data(competence)?.name.toLowerCase().includes("1 main");
}
static isArme2Main(competence) {
return Misc.data(competence)?.name.toLowerCase().includes("2 main");
}
/* -------------------------------------------- */
static isMalusEncombrementTotal(competence) {
return Misc.data(competence)?.name.toLowerCase().match(/(natation|acrobatie)/);
}
/* -------------------------------------------- */
static getListTronc(compName) {
for (let troncList of competenceTroncs) {
for (let troncName of troncList) {
if (troncName == compName)
return troncList;
}
}
return [];
}
/* -------------------------------------------- */
static computeTotalXP(competences) {
const total = competences.map(c => RdDItemCompetence.computeXP(c))
.reduce(Misc.sum(), 0);
const economieTronc = RdDItemCompetence.computeEconomieXPTronc(competences);
return total - economieTronc;
}
/* -------------------------------------------- */
static computeXP(competence) {
const itemData = Misc.data(competence);
const factor = itemData.name.includes('Thanatos') ? 2 : 1; // Thanatos compte double !
const xpNiveau = RdDItemCompetence.computeDeltaXP(itemData.data.base, itemData.data.niveau ?? itemData.data.base);
const xp = itemData.data.xp ?? 0;
const xpSort = itemData.data.xp_sort ?? 0;
return factor * (xpNiveau + xp) + xpSort;
}
/* -------------------------------------------- */
static computeEconomieXPTronc(competences) {
return competenceTroncs.map(
list => list.map(name => RdDItemCompetence.findCompetence(competences, name))
// calcul du coût xp jusqu'au niveau 0 maximum
.map(it => RdDItemCompetence.computeDeltaXP(it?.data.base ?? -11, Math.min(it?.data.niveau ?? -11, 0)))
.sort(Misc.ascending())
.splice(0, list.length - 1) // prendre toutes les valeurs sauf l'une des plus élevées
.reduce(Misc.sum(), 0)
).reduce(Misc.sum(), 0);
}
/* -------------------------------------------- */
static computeDeltaXP(from, to) {
RdDItemCompetence._valideNiveau(from);
RdDItemCompetence._valideNiveau(to);
return competence_xp_cumul[to] - competence_xp_cumul[from];
}
/* -------------------------------------------- */
static computeCompetenceXPCost(competence) {
const compData = Misc.data(competence);
let xp = RdDItemCompetence.getDeltaXp(compData.data.base, compData.data.niveau ?? compData.data.base);
xp += compData.data.xp ?? 0;
if (compData.name.includes('Thanatos')) xp *= 2; /// Thanatos compte double !
xp += compData.data.xp_sort ?? 0;
return xp;
}
/* -------------------------------------------- */
static computeEconomieCompetenceTroncXP(competences) {
let economie = 0;
for (let troncList of competenceTroncs) {
let list = troncList.map(name => RdDItemCompetence.findCompetence(competences, name))
.sort(Misc.descending(c => Misc.templateData(c).niveau)); // tri du plus haut au plus bas
list.splice(0, 1); // ignorer la plus élevée
list.map(c => Misc.templateData(c)).forEach(tplData => {
economie += RdDItemCompetence.getDeltaXp(tplData.base, Math.min(tplData.niveau, 0));
});
}
return economie;
}
/* -------------------------------------------- */
static levelUp(itemData, stressTransforme) {
itemData.data.xpNext = RdDItemCompetence.getCompetenceNextXp(itemData.data.niveau);
const xpManquant = itemData.data.xpNext - itemData.data.xp;
itemData.data.isLevelUp = xpManquant <= 0;
itemData.data.isStressLevelUp = (xpManquant > 0 && stressTransforme >= xpManquant && itemData.data.niveau < itemData.data.niveau_archetype);
itemData.data.stressXpMax = 0;
if (xpManquant > 0 && stressTransforme > 0 && itemData.data.niveau < itemData.data.niveau_archetype) {
itemData.data.stressXpMax = Math.min(xpManquant , stressTransforme);
}
}
/* -------------------------------------------- */
static isVisible(itemData) {
return Number(itemData.data.niveau) != RdDItemCompetence.getNiveauBase(itemData.data.categorie);
}
static nomContientTexte(itemData, texte) {
return Grammar.toLowerCaseNoAccent(itemData.name).includes(Grammar.toLowerCaseNoAccent(texte))
}
/* -------------------------------------------- */
static isNiveauBase(itemData) {
return Number(itemData.data.niveau) == RdDItemCompetence.getNiveauBase(itemData.data.categorie);
}
/* -------------------------------------------- */
static findCompetence(list, idOrName, options = {}) {
if (idOrName == undefined) {
return undefined;
}
options = mergeObject(options, {
preFilter: it => RdDItemCompetence.isCompetence(it),
description: 'compétence',
});
return list.find(it => it.id == idOrName && RdDItemCompetence.isCompetence(it))
?? Misc.findFirstLike(idOrName, list, options);
}
/* -------------------------------------------- */
static findCompetences(list, name) {
return Misc.findAllLike(name, list, { filter: it => RdDItemCompetence.isCompetence(it), description: 'compétence' });
}
static isCompetence(item) {
return item.type == 'competence' || item.type == 'competencecreature';
}
/* -------------------------------------------- */
static getCompetenceNextXp(niveau) {
return RdDItemCompetence.getCompetenceXp(niveau + 1);
}
/* -------------------------------------------- */
static getCompetenceXp(niveau) {
RdDItemCompetence._valideNiveau(niveau);
return niveau < -10 ? 0 : xp_par_niveau[niveau + 10];
}
/* -------------------------------------------- */
static getDeltaXp(from, to) {
RdDItemCompetence._valideNiveau(from);
RdDItemCompetence._valideNiveau(to);
return competence_xp_cumul[to] - competence_xp_cumul[from];
}
/* -------------------------------------------- */
static _valideNiveau(niveau) {
if (niveau < -11 || niveau > niveau_max) {
console.warn(`Niveau ${niveau} en dehors des niveaux de compétences: [-11, ${niveau_max} ]`);
}
}
/* -------------------------------------------- */
static computeResumeArchetype(competences) {
const archetype = RdDItemCompetence.getLimitesArchetypes();
competences.map(it => Math.max(0, Misc.templateData(it).niveau_archetype))
.forEach(niveau => {
archetype[niveau] = archetype[niveau] ?? { "niveau": niveau, "nombreMax": 0, "nombre": 0 };
archetype[niveau].nombre = (archetype[niveau]?.nombre ?? 0) + 1;
});
return archetype;
}
/* -------------------------------------------- */
static getLimitesArchetypes() {
return duplicate(limitesArchetypes);
}
}