La méthode splice retourne les éléments supprimées, et non pas le tableau après suppression
		
			
				
	
	
		
			269 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			269 lines
		
	
	
		
			9.8 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) {
 | |
|     name = Grammar.toLowerCaseNoAccent(name);
 | |
|     const competences = list.filter(it => Grammar.toLowerCaseNoAccent(it.name).includes(name) && (it.type == "competence" || it.type == "competencecreature"));
 | |
|     if (competences.length == 0) {
 | |
|       return undefined;
 | |
|     }
 | |
|     let competence = competences.find(it => Grammar.toLowerCaseNoAccent(it.name) == name);
 | |
|     if (!competence) {
 | |
|       competence = competences[0];
 | |
|       if (competences.length > 1) {
 | |
|         const names = competences.map(it => it.name).reduce((a, b) => `${a}<br>${b}`);
 | |
|         ui.notifications.info(`Plusieurs compétences possibles:<br>${names}<br>La première sera choisie: ${competence.name}`);
 | |
|       }
 | |
|     }
 | |
|     return competence;
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   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);
 | |
|   }
 | |
| 
 | |
| } |