foundryvtt-reve-de-dragon/module/item-competence.js
Vincent Vandemeulebrouck 8c3e7e2445 Ajout de Misc.findFirstLike
Méthode de recherche de valeur "proche" dans une liste.

Cherche un élément correspondant strictement, sinon cherche
les éléments contenant la valeur, et retourne le premier.

Notification si plusieurs valeurs peuvent correspondre.
2021-10-08 23:32:08 +02:00

256 lines
9.2 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) {
voie = Grammar.toLowerCaseNoAccent(voie);
return competences.find(it => RdDItemCompetence.isDraconic(it) && Grammar.toLowerCaseNoAccent(Misc.data(it).name).includes(voie));
}
/* -------------------------------------------- */
static getEsquive(competences) {
return { name: 'Esquive', niveau: RdDItemCompetence.findCompetence(competences, 'Esquive')?.data.niveau ?? -6 };
}
/* -------------------------------------------- */
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 isTronc(compName) {
for (let troncList of competenceTroncs) {
for (let troncName of troncList) {
if (troncName == compName)
return troncList;
}
}
return false;
}
/* -------------------------------------------- */
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) {
itemData.data.xpNext = RdDItemCompetence.getCompetenceNextXp(itemData.data.niveau);
itemData.data.isLevelUp = itemData.data.xp >= itemData.data.xpNext;
}
/* -------------------------------------------- */
static isVisible(itemData) {
return Number(itemData.data.niveau) != RdDItemCompetence.getNiveauBase(itemData.data.categorie);
}
/* -------------------------------------------- */
static isNiveauBase(itemData) {
return Number(itemData.data.niveau) == RdDItemCompetence.getNiveauBase(itemData.data.categorie);
}
/* -------------------------------------------- */
static findCompetence(list, name) {
return Misc.findFirstLike(name, list, it => it.name, 'compétences');
}
/* -------------------------------------------- */
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);
}
}