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 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, 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 isNiveauBase(itemData) { return Number(itemData.data.niveau) == RdDItemCompetence.getNiveauBase(itemData.data.categorie); } /* -------------------------------------------- */ static findCompetence(list, idOrName, options = {}) { options = mergeObject(options, { filter: 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); } }