09fe695777
Parfois, un conteneur peut contenir l'id d'un objet qui a été supprimé (potion bue? objets groupés?) Dans ce cas, la feuille ne s'ouvrait plus correctement. Une méthode de cleanup permet d'éliminer ces ids incorrects.
849 lines
37 KiB
JavaScript
849 lines
37 KiB
JavaScript
/* Common useful functions shared between objects */
|
|
|
|
import { ChatUtility } from "./chat-utility.js";
|
|
import { RdDCombat } from "./rdd-combat.js";
|
|
import { Misc } from "./misc.js";
|
|
import { Grammar } from "./grammar.js";
|
|
import { TMRUtility } from "./tmr-utility.js";
|
|
import { DialogItemAchat } from "./dialog-item-achat.js";
|
|
import { ReglesOptionelles } from "./regles-optionelles.js";
|
|
import { RdDDice } from "./rdd-dice.js";
|
|
|
|
/* -------------------------------------------- */
|
|
// This table starts at 0 -> niveau -10
|
|
const carac_array = ["taille", "apparence", "constitution", "force", "agilite", "dexterite", "vue", "ouie", "odoratgout", "volonte", "intellect", "empathie", "reve", "chance", "melee", "tir", "lancer", "derobee"];
|
|
const difficultesLibres = [0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10];
|
|
const ajustementsConditions = [-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, +1, +2, +3, +4, +5, +6, +7, +8, +9, +10];
|
|
const ajustementsEncaissement = [-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, +13, +14, +15, +16, +17, +18, +19, +20, +21, +22, +23, +24, +25];
|
|
|
|
/* -------------------------------------------- */
|
|
function _buildAllSegmentsFatigue(max) {
|
|
const cycle = [5, 2, 4, 1, 3, 0];
|
|
let fatigue = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]];
|
|
for (let i = 0; i <= max; i++) {
|
|
const ligneFatigue = duplicate(fatigue[i]);
|
|
const caseIncrementee = cycle[i % 6];
|
|
ligneFatigue[caseIncrementee]++;
|
|
ligneFatigue[caseIncrementee + 6]++;
|
|
ligneFatigue.fatigueMax = 2 * (i + 1);
|
|
fatigue[i + 1] = ligneFatigue;
|
|
}
|
|
return fatigue;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
function _cumulSegmentsFatigue(matrix) {
|
|
let cumulMatrix = [];
|
|
for (let line of matrix) {
|
|
let cumul = duplicate(line);
|
|
|
|
for (let i = 1; i < 12; i++) {
|
|
cumul[i] += cumul[i - 1];
|
|
}
|
|
cumulMatrix.push(cumul);
|
|
}
|
|
return cumulMatrix;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
const fatigueMatrix = _buildAllSegmentsFatigue(60);
|
|
const cumulFatigueMatrix = _cumulSegmentsFatigue(fatigueMatrix);
|
|
|
|
const fatigueMalus = [0, 0, 0, -1, -1, -1, -2, -3, -4, -5, -6, -7]; // Provides the malus for each segment of fatigue
|
|
const fatigueLineSize = [3, 6, 7, 8, 9, 10, 11, 12];
|
|
const fatigueLineMalus = [0, -1, -2, -3, -4, -5, -6, -7];
|
|
const fatigueMarche = {
|
|
"aise": { "4": 1, "6": 2, "8": 3, "10": 4, "12": 6 },
|
|
"malaise": { "4": 2, "6": 3, "8": 4, "10": 6 },
|
|
"difficile": { "4": 3, "6": 4, "8": 6 },
|
|
"tresdifficile": { "4": 4, "6": 6 }
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
const definitionsBlessures = [
|
|
{ type: "legere", facteur: 2 },
|
|
{ type: "grave", facteur: 4 },
|
|
{ type: "critique", facteur: 6 }
|
|
]
|
|
|
|
/* -------------------------------------------- */
|
|
const nomEthylisme = ["Emeché", "Gris", "Pinté", "Pas frais", "Ivre", "Bu", "Complètement fait", "Ivre mort"];
|
|
|
|
/* -------------------------------------------- */
|
|
const definitionsEncaissement = {
|
|
"mortel": [
|
|
{ minimum: undefined, maximum: 0, endurance: "0", vie: "0", eraflures: 0, legeres: 0, graves: 0, critiques: 0 },
|
|
{ minimum: 1, maximum: 10, endurance: "1d4", vie: "0", eraflures: 1, legeres: 0, graves: 0, critiques: 0 },
|
|
{ minimum: 11, maximum: 15, endurance: "1d6", vie: "0", eraflures: 0, legeres: 1, graves: 0, critiques: 0 },
|
|
{ minimum: 16, maximum: 19, endurance: "2d6", vie: "2", eraflures: 0, legeres: 0, graves: 1, critiques: 0 },
|
|
{ minimum: 20, maximum: undefined, endurance: "100", vie: "4 + @over20", eraflures: 0, legeres: 0, graves: 0, critiques: 1 },
|
|
],
|
|
"non-mortel": [
|
|
{ minimum: undefined, maximum: 0, endurance: "0", vie: "0", eraflures: 0, legeres: 0, graves: 0, critiques: 0 },
|
|
{ minimum: 1, maximum: 10, endurance: "1d4", vie: "0", eraflures: 1, legeres: 0, graves: 0, critiques: 0 },
|
|
{ minimum: 11, maximum: 15, endurance: "1d6", vie: "0", eraflures: 1, legeres: 0, graves: 0, critiques: 0 },
|
|
{ minimum: 16, maximum: 19, endurance: "2d6", vie: "0", eraflures: 0, legeres: 1, graves: 0, critiques: 0 },
|
|
{ minimum: 20, maximum: undefined, endurance: "100", vie: "0", eraflures: 0, legeres: 1, graves: 0, critiques: 0 },
|
|
],
|
|
"cauchemar": [
|
|
{ minimum: undefined, maximum: 0, endurance: "0", vie: "0", eraflures: 0, legeres: 0, graves: 0, critiques: 0 },
|
|
{ minimum: 1, maximum: 10, endurance: "1d4", vie: "0", eraflures: 1, legeres: 0, graves: 0, critiques: 0 },
|
|
{ minimum: 11, maximum: 15, endurance: "1d6", vie: "0", eraflures: 1, legeres: 0, graves: 0, critiques: 0 },
|
|
{ minimum: 16, maximum: 19, endurance: "2d6", vie: "0", eraflures: 1, legeres: 0, graves: 0, critiques: 0 },
|
|
{ minimum: 20, maximum: undefined, endurance: "3d6 + @over20", vie: "0", eraflures: 1, legeres: 0, graves: 0, critiques: 0 },
|
|
]
|
|
};
|
|
|
|
/* -------------------------------------------- */
|
|
export class RdDUtility {
|
|
|
|
/* -------------------------------------------- */
|
|
static async init() {
|
|
Hooks.on("renderChatMessage", async (app, html, msg) => RdDUtility.onRenderChatMessage(app, html, msg));
|
|
Hooks.on('renderChatLog', (log, html, data) => RdDUtility.chatListeners(html));
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static async preloadHandlebarsTemplates() {
|
|
const templatePaths = [
|
|
//Character Sheets
|
|
'systems/foundryvtt-reve-de-dragon/templates/actor-sheet.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/actor-creature-sheet.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/actor-entite-sheet.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/actor-vehicule-sheet.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/actor-sheet-competence-partial.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/actor-sheet-categorie-competences-partial.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/actor-sheet-oeuvre-partial.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/actor-liste-blessures-partial.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/actor-blessure-partial.html',
|
|
// Conteneur/item in Actor sheet
|
|
'systems/foundryvtt-reve-de-dragon/templates/actor-sheet-inventaire-conteneur.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/actor-sheet-editor-notes-mj.html',
|
|
//Items
|
|
'systems/foundryvtt-reve-de-dragon/templates/item-competence-sheet.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/item-competencecreature-sheet.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/item-arme-sheet.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/item-armure-sheet.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/item-objet-sheet.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/item-conteneur-sheet.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/item-sort-sheet.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/item-herbe-sheet.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/item-ingredient-sheet.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/item-livre-sheet.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/item-tache-sheet.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/item-potion-sheet.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/item-rencontresTMR-sheet.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/item-queue-sheet.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/item-souffle-sheet.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/item-tarot-sheet.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/item-tete-sheet.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/item-ombre-sheet.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/item-monnaie-sheet.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/item-meditation-sheet.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/item-nourritureboisson-sheet.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/item-signedraconique-sheet.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/competence-carac-defaut.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/competence-base.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/enum-aspect-tarot.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-competence.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-ingredient.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-parade.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-vehicule.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/enum-competence.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/enum-herbesoin-ingredient.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-potion.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/enum-initpremierround.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/enum-rarete.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/sort-draconic.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/sort-tmr.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/niveau-ethylisme.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/casetmr-specific-list.html',
|
|
// Dialogs
|
|
'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-resolution.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/dialog-competence.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-carac.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-sort.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-encaisser.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-meditation.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/dialog-tmr.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-alchimie.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/dialog-astrologie-joueur.html',
|
|
// Partials
|
|
'systems/foundryvtt-reve-de-dragon/templates/partial-description-overflow.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/partial-description-sort.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/partial-roll-ajustements.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/partial-roll-diffLibre.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/partial-roll-diffCondition.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/partial-roll-surenc.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/partial-roll-enctotal.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/partial-roll-moral.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/partial-select-carac.html',
|
|
// Calendrier
|
|
'systems/foundryvtt-reve-de-dragon/templates/calendar-template.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/calendar-editor-template.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/heures-select-option.html',
|
|
// HUD
|
|
'systems/foundryvtt-reve-de-dragon/templates/hud-actor-init.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/hud-actor-attaque.html',
|
|
// messages tchat
|
|
'systems/foundryvtt-reve-de-dragon/templates/chat-infojet.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/chat-poesie.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/chat-info-appel-au-moral.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/chat-demande-defense.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/chat-demande-attaque-particuliere.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/chat-demande-attaque-etotal.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/chat-resultat-appelchance.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/chat-resultat-attaque.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/chat-resultat-encaissement.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/chat-resultat-parade.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/chat-resultat-esquive.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/chat-resultat-competence.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/chat-resultat-general.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/chat-resultat-tache.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/chat-resultat-sort.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/chat-resultat-alchimie.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/chat-actor-turn-summary.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/chat-actor-competence-xp.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/chat-actor-carac-xp.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/chat-potionenchantee-chateaudormant.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/chat-fabriquer-potion-base.html',
|
|
'systems/foundryvtt-reve-de-dragon/templates/chat-signe-draconique-actor.html'
|
|
];
|
|
|
|
Handlebars.registerHelper('upperFirst', str => Misc.upperFirst(str ?? 'Null'));
|
|
Handlebars.registerHelper('upper', str => str?.toUpperCase() ?? 'NULL');
|
|
Handlebars.registerHelper('le', str => Grammar.articleDetermine(str));
|
|
Handlebars.registerHelper('un', str => Grammar.articleIndetermine(str));
|
|
Handlebars.registerHelper('accord', (genre, ...args) => Grammar.accord(genre, args));
|
|
Handlebars.registerHelper('buildConteneur', (objet) => { return RdDUtility.buildConteneur(objet); });
|
|
Handlebars.registerHelper('caseTmr-label', coord => TMRUtility.getTMRLabel(coord));
|
|
Handlebars.registerHelper('caseTmr-type', coord => TMRUtility.getTMRType(coord));
|
|
Handlebars.registerHelper('typeTmr-name', coord => TMRUtility.typeTmrName(coord));
|
|
|
|
Handlebars.registerHelper('sortCompetence', competences => competences.sort((a, b) => {
|
|
if (a.name.startsWith("Survie") && b.name.startsWith("Survie")) {
|
|
if (a.name.includes("Cité")) return -1;
|
|
if (b.name.includes("Cité")) return 1;
|
|
if (a.name.includes("Extérieur")) return -1;
|
|
if (b.name.includes("Extérieur")) return 1;
|
|
return a.name.localeCompare(b.name);
|
|
}
|
|
if (a.data.categorie.startsWith("melee") && b.data.categorie.startsWith("melee")) {
|
|
if (a.name.includes("Corps")) return -1;
|
|
if (b.name.includes("Corps")) return 1;
|
|
if (a.name.includes("Dague")) return -1;
|
|
if (b.name.includes("Dague")) return 1;
|
|
if (a.name.includes("Esquive")) return -1;
|
|
if (b.name.includes("Esquive")) return 1;
|
|
return a.name.localeCompare(b.name);
|
|
}
|
|
if (a.name.startsWith("Voie") && b.name.startsWith("Voie")) {
|
|
if (a.name.includes("Oniros")) return -1;
|
|
if (b.name.includes("Oniros")) return 1;
|
|
if (a.name.includes("Hypnos")) return -1;
|
|
if (b.name.includes("Hypnos")) return 1;
|
|
if (a.name.includes("Narcos")) return -1;
|
|
if (b.name.includes("Narcos")) return 1;
|
|
if (a.name.includes("Thanatos")) return -1;
|
|
if (b.name.includes("Thanatos")) return 1;
|
|
return a.name.localeCompare(b.name);
|
|
}
|
|
return a.name.localeCompare(b.name);
|
|
}));
|
|
|
|
return loadTemplates(templatePaths);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static buildListOptions(min, max) {
|
|
let options = ""
|
|
for (let i = min; i <= max; i++) {
|
|
options += `<option value="${i}">${i}</option>`
|
|
}
|
|
return options;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static checkNull(items) {
|
|
if (items && items.length) {
|
|
return items;
|
|
}
|
|
return [];
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static getNomEthylisme(niveauEthylisme) {
|
|
let index = -niveauEthylisme;
|
|
return index < 0 ? 'Aucun' : nomEthylisme[index];
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static initAfficheContenu(actorId) { // persistent handling of conteneur show/hide
|
|
if (!this.afficheContenu)
|
|
this.afficheContenu = {};
|
|
}
|
|
/* -------------------------------------------- */
|
|
static toggleAfficheContenu(conteneurId) {
|
|
this.afficheContenu[conteneurId] = !this.afficheContenu[conteneurId];
|
|
}
|
|
/* -------------------------------------------- */
|
|
static getAfficheContenu(conteneurId) {
|
|
if (conteneurId)
|
|
return this.afficheContenu[conteneurId];
|
|
return undefined;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static filterItemsPerTypeForSheet(formData) {
|
|
formData.materiel = this.checkNull(formData.itemsByType['objet']);
|
|
formData.conteneurs = this.checkNull(formData.itemsByType['conteneur']);
|
|
formData.armes = this.checkNull(formData.itemsByType['arme']);
|
|
formData.armures = this.checkNull(formData.itemsByType['armure']);
|
|
formData.livres = this.checkNull(formData.itemsByType['livre']);
|
|
formData.potions = this.checkNull(formData.itemsByType['potion']);
|
|
formData.ingredients = this.checkNull(formData.itemsByType['ingredient']);
|
|
formData.munitions = this.checkNull(formData.itemsByType['munition']);
|
|
formData.herbes = this.checkNull(formData.itemsByType['herbe']);
|
|
formData.sorts = this.checkNull(formData.itemsByType['sort']);
|
|
formData.signesdraconiques = this.checkNull(formData.itemsByType['signedraconique']);
|
|
formData.queues = this.checkNull(formData.itemsByType['queue']);
|
|
formData.souffles = this.checkNull(formData.itemsByType['souffle']);
|
|
formData.ombres = this.checkNull(formData.itemsByType['ombre']);
|
|
formData.tetes = this.checkNull(formData.itemsByType['tete']);
|
|
formData.taches = this.checkNull(formData.itemsByType['tache']);
|
|
formData.monnaie = this.checkNull(formData.itemsByType['monnaie']);
|
|
formData.nourritureboissons = this.checkNull(formData.itemsByType['nourritureboisson']);
|
|
formData.meditations = this.checkNull(formData.itemsByType['meditation']);
|
|
formData.chants = this.checkNull(formData.itemsByType['chant']);
|
|
formData.danses = this.checkNull(formData.itemsByType['danse']);
|
|
formData.musiques = this.checkNull(formData.itemsByType['musique']);
|
|
formData.oeuvres = this.checkNull(formData.itemsByType['oeuvre']);
|
|
formData.jeux = this.checkNull(formData.itemsByType['jeu']);
|
|
formData.recettescuisine = this.checkNull(formData.itemsByType['recettecuisine']);
|
|
formData.recettesAlchimiques = this.checkNull(formData.itemsByType['recettealchimique']);
|
|
formData.objets = formData.conteneurs.concat(formData.materiel)
|
|
.concat(formData.armes)
|
|
.concat(formData.armures)
|
|
.concat(formData.munitions)
|
|
.concat(formData.livres)
|
|
.concat(formData.potions)
|
|
.concat(formData.herbes)
|
|
.concat(formData.ingredients)
|
|
.concat(formData.nourritureboissons);
|
|
formData.competences = (formData.itemsByType.competence ?? []).concat(formData.itemsByType.competencecreature ?? []);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static buildArbreDeConteneurs(conteneurs, objets) {
|
|
let objetVersConteneur = {};
|
|
// Attribution des objets aux conteneurs
|
|
for (let conteneur of conteneurs) {
|
|
conteneur.subItems = [];
|
|
for (let id of conteneur.data.contenu ?? []) {
|
|
let objet = objets.find(objet => (id == objet._id));
|
|
if (objet) {
|
|
objet.estContenu = true; // Permet de filtrer ce qui est porté dans le template
|
|
objetVersConteneur[id] = conteneur._id;
|
|
conteneur.subItems.push(objet);
|
|
}
|
|
}
|
|
}
|
|
for (let conteneur of conteneurs) {
|
|
conteneur.data.encTotal = RdDUtility.calculEncContenu(conteneur, objets);
|
|
}
|
|
return objetVersConteneur;
|
|
}
|
|
|
|
static calculEncContenu(conteneur, objets) {
|
|
const itemData = Misc.data(conteneur);
|
|
const contenuDatas = (itemData.data.contenu ?? []).filter(id => id != undefined)
|
|
.map(id => Misc.data(objets.find(it => (id == it._id))))
|
|
.filter(it => it);
|
|
let enc = Number(itemData.data.encombrement ?? 0) * Number(itemData.data.quantite ?? 1);
|
|
for (let itemData of contenuDatas){
|
|
if (itemData.type == 'conteneur') {
|
|
enc += RdDUtility.calculEncContenu(itemData, objets);
|
|
}
|
|
else{
|
|
enc += Number(itemData.data.encombrement ?? 0) * Number(itemData.data.quantite ?? 1);
|
|
}
|
|
}
|
|
return enc;
|
|
}
|
|
|
|
// Construit la liste des conteneurs de niveau 1 (c'est à dire non contenu eux-même dans un conteneur)
|
|
static conteneursRacine(conteneurs) {
|
|
return conteneurs.filter((conteneur, index, arr) => !conteneur.estContenu);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
/** Construit la structure récursive des conteneurs, avec imbrication potentielle
|
|
*
|
|
*/
|
|
static buildConteneur(objet, niveau) {
|
|
if (!niveau) niveau = 1;
|
|
objet.niveau = niveau;
|
|
//console.log("OBJ:", objet);
|
|
let str = Handlebars.partials['systems/foundryvtt-reve-de-dragon/templates/actor-sheet-inventaire-conteneur.html']({ item: objet });
|
|
if (objet.type == 'conteneur') {
|
|
//console.log("ITEM DISPLAYED", objet );
|
|
if (this.getAfficheContenu(objet._id)) {
|
|
str = str + "<ul class='item-list alterne-list item-display-show list-item-margin" + niveau + "'>";
|
|
} else {
|
|
str = str + "<ul class='item-list alterne-list item-display-hide list-item-margin" + niveau + "'>";
|
|
}
|
|
for (let subItem of objet.subItems) {
|
|
str = str + this.buildConteneur(subItem, niveau + 1);
|
|
}
|
|
str = str + "</ul>";
|
|
}
|
|
return new Handlebars.SafeString(str);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static getCaracArray() {
|
|
return carac_array;
|
|
}
|
|
static getDifficultesLibres() {
|
|
return difficultesLibres;
|
|
}
|
|
static getAjustementsConditions() {
|
|
return ajustementsConditions;
|
|
}
|
|
static getAjustementsEncaissement() {
|
|
return ajustementsEncaissement;
|
|
}
|
|
|
|
static getDefinitionsBlessures() {
|
|
return definitionsBlessures;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static getSegmentsFatigue(maxEnd) {
|
|
maxEnd = Math.max(maxEnd, 1);
|
|
maxEnd = Math.min(maxEnd, fatigueMatrix.length);
|
|
return fatigueMatrix[maxEnd];
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static calculMalusFatigue(fatigue, maxEnd) {
|
|
maxEnd = Math.max(maxEnd, 1);
|
|
maxEnd = Math.min(maxEnd, cumulFatigueMatrix.length);
|
|
let segments = cumulFatigueMatrix[maxEnd];
|
|
for (let i = 0; i < 12; i++) {
|
|
if (fatigue <= segments[i]) {
|
|
return fatigueMalus[i]
|
|
}
|
|
}
|
|
return -7;
|
|
}
|
|
|
|
static calculFatigueHtml(fatigue, endurance) {
|
|
return ReglesOptionelles.isUsing("appliquer-fatigue") ? {
|
|
malus: RdDUtility.calculMalusFatigue(fatigue, endurance),
|
|
html: "<table class='table-fatigue'>" + RdDUtility.makeHTMLfatigueMatrix(fatigue, endurance).html() + "</table>"
|
|
} : { malus: 0, html: '' };
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
// Build the nice (?) html table used to manage fatigue.
|
|
// max should be the endurance max value
|
|
static makeHTMLfatigueMatrix(fatigue, maxEndurance) {
|
|
let segments = this.getSegmentsFatigue(maxEndurance);
|
|
return this.makeHTMLfatigueMatrixForSegment(fatigue, segments);
|
|
}
|
|
|
|
static makeHTMLfatigueMatrixForSegment(fatigue, segments) {
|
|
fatigue = Math.max(fatigue, 0);
|
|
fatigue = Math.min(fatigue, segments.fatigueMax);
|
|
|
|
let table = $("<table/>").addClass('table-fatigue');
|
|
let segmentIdx = 0;
|
|
let fatigueCount = 0;
|
|
for (var line = 0; line < fatigueLineSize.length; line++) {
|
|
let row = $("<tr/>");
|
|
let segmentsPerLine = fatigueLineSize[line];
|
|
row.append("<td class='fatigue-malus'>" + fatigueLineMalus[line] + "</td>");
|
|
while (segmentIdx < segmentsPerLine) {
|
|
let freeSize = segments[segmentIdx];
|
|
for (let col = 0; col < 5; col++) {
|
|
if (col < freeSize) {
|
|
if (fatigueCount < fatigue)
|
|
row.append("<td class='fatigue-used'>X</td>");
|
|
|
|
|
|
else
|
|
row.append("<td class='fatigue-free'/>");
|
|
fatigueCount++;
|
|
} else {
|
|
row.append("<td class='fatigue-none'/>");
|
|
}
|
|
}
|
|
row.append("<td class='fatigue-separator'/>");
|
|
segmentIdx = segmentIdx + 1;
|
|
}
|
|
table.append(row);
|
|
}
|
|
return table;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static async getLocalisation(type = 'personnage') {
|
|
let result = await RdDDice.rollTotal("1d20");
|
|
let txt = ""
|
|
if (type == 'personnage') {
|
|
if (result <= 3) txt = "Jambe, genou, pied, jarret";
|
|
else if (result <= 7) txt = "Hanche, cuisse, fesse";
|
|
else if (result <= 9) txt = "Ventre, reins";
|
|
else if (result <= 12) txt = "Poitrine, dos";
|
|
else if (result <= 14) txt = "Avant-bras, main, coude";
|
|
else if (result <= 18) txt = "Epaule, bras, omoplate";
|
|
else if (result == 19) txt = "Tête";
|
|
else if (result == 20) txt = "Tête (visage)";
|
|
} else {
|
|
if (result <= 7) txt = "Jambes/Pattes";
|
|
else if (result <= 18) txt = "Corps";
|
|
else if (result <= 20) txt = "Tête";
|
|
}
|
|
|
|
return { result: result, label: txt };
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static selectEncaissement(degats, mortalite) {
|
|
const table = definitionsEncaissement[mortalite] === undefined ? definitionsEncaissement["mortel"] : definitionsEncaissement[mortalite];
|
|
for (let encaissement of table) {
|
|
if ((encaissement.minimum === undefined || encaissement.minimum <= degats)
|
|
&& (encaissement.maximum === undefined || degats <= encaissement.maximum)) {
|
|
return duplicate(encaissement);
|
|
}
|
|
}
|
|
return duplicate(table[0]);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static currentFatigueMalus(value, max) {
|
|
if (ReglesOptionelles.isUsing("appliquer-fatigue")) {
|
|
max = Math.max(1, Math.min(max, 60));
|
|
value = Math.min(max * 2, Math.max(0, value));
|
|
|
|
let fatigueTab = fatigueMatrix[max];
|
|
let fatigueRem = value;
|
|
for (let idx = 0; idx < fatigueTab.length; idx++) {
|
|
fatigueRem -= fatigueTab[idx];
|
|
if (fatigueRem <= 0) {
|
|
return fatigueMalus[idx];
|
|
}
|
|
}
|
|
return -7; // This is the max !
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static async loadCompendiumData(compendium) {
|
|
const pack = game.packs.get(compendium);
|
|
return await pack?.getDocuments() ?? [];
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static async loadCompendium(compendium, filter = item => true) {
|
|
let compendiumData = await RdDUtility.loadCompendiumData(compendium);
|
|
return compendiumData.filter(filter);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static async responseNombreAstral(data) {
|
|
let actor = game.actors.get(data.id);
|
|
actor.ajouteNombreAstral(data);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static onSocketMesssage(sockmsg) {
|
|
switch (sockmsg.msg) {
|
|
case "msg_gm_chat_message":
|
|
return ChatUtility.handleGMChatMessage(sockmsg.data);
|
|
case "msg_sync_time":
|
|
return game.system.rdd.calendrier.syncPlayerTime(sockmsg.data);
|
|
case "msg_request_nombre_astral":
|
|
return game.system.rdd.calendrier.requestNombreAstral(sockmsg.data);
|
|
case "msg_response_nombre_astral":
|
|
return RdDUtility.responseNombreAstral(sockmsg.data);
|
|
case "msg_tmr_move":
|
|
let actor = game.actors.get(sockmsg.data.actorId);
|
|
if (actor.isOwner || game.user.isGM) {
|
|
actor.refreshTMRView(sockmsg.data.tmrPos);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static async chatListeners(html) {
|
|
RdDCombat.registerChatCallbacks(html);
|
|
|
|
// Gestion spécifique message passeurs
|
|
html.on("click", '.tmr-passeur-coord a', event => {
|
|
let coord = event.currentTarget.attributes['data-tmr-coord'].value;
|
|
let actorId = event.currentTarget.attributes['data-actor-id'].value;
|
|
let actor = game.actors.get(actorId);
|
|
actor.tmrApp.forceDemiRevePosition(coord);
|
|
});
|
|
// Gestion spécifique des sorts en réserve multiples (ie têtes)
|
|
html.on("click", '#sort-reserve', event => {
|
|
let coord = event.currentTarget.attributes['data-tmr-coord'].value;
|
|
let sortId = event.currentTarget.attributes['data-sort-id'].value;
|
|
let actorId = event.currentTarget.attributes['data-actor-id'].value;
|
|
let actor = game.actors.get(actorId);
|
|
actor.tmrApp.lancerSortEnReserve(coord, sortId);
|
|
});
|
|
|
|
// gestion bouton tchat Acheter
|
|
html.on("click", '.button-acheter', event => DialogItemAchat.onButtonAcheter(event));
|
|
|
|
// Gestion du bouton payer
|
|
html.on("click", '.payer-button', event => {
|
|
let sumdenier = event.currentTarget.attributes['data-somme-denier']?.value ?? 0;
|
|
let quantite = event.currentTarget.attributes['data-quantite']?.value ?? 1;
|
|
let fromActorId = event.currentTarget.attributes['data-actor-id']?.value;
|
|
let jsondata = event.currentTarget.attributes['data-jsondata']
|
|
let objData
|
|
if (jsondata) {
|
|
objData = JSON.parse(jsondata.value)
|
|
}
|
|
let actor = RdDUtility.getSelectedActor("Pour effectuer le paiement:");
|
|
if (actor) {
|
|
actor.depenserDeniers(sumdenier, objData, quantite, fromActorId);
|
|
let chatMessageId = RdDUtility.findChatMessageId(event.currentTarget);
|
|
if (chatMessageId) {
|
|
ChatUtility.removeChatMessageId(chatMessageId);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
static findChatMessageId(current) {
|
|
return RdDUtility.getChatMessageId(RdDUtility.findChatMessage(current));
|
|
}
|
|
|
|
static getChatMessageId(node) {
|
|
return node?.attributes.getNamedItem('data-message-id')?.value;
|
|
}
|
|
|
|
static findChatMessage(current) {
|
|
return RdDUtility.findNodeMatching(current, it => it.classList.contains('chat-message') && it.attributes.getNamedItem('data-message-id'));
|
|
}
|
|
|
|
static findNodeMatching(current, predicate) {
|
|
if (current) {
|
|
if (predicate(current)) {
|
|
return current;
|
|
}
|
|
return RdDUtility.findNodeMatching(current.parentElement, predicate);
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
static getSelectedActor(msgPlayer = undefined) {
|
|
if (canvas.tokens.controlled.length == 1) {
|
|
let token = canvas.tokens.controlled[0];
|
|
if (token.actor && token.data.actorLink) {
|
|
return token.actor;
|
|
}
|
|
if (msgPlayer != undefined) {
|
|
msgPlayer += "<br>le token sélectionné doit être lié à un personnage";
|
|
}
|
|
}
|
|
if (game.user.character) {
|
|
return game.user.character;
|
|
}
|
|
if (msgPlayer != undefined) {
|
|
msgPlayer += "<br>vous pouvez sélectionner un seul token lié à un personnage";
|
|
msgPlayer += "<br>vous devez être connecté comme joueur avec un personnage sélectionné";
|
|
ui.notifications.warn(msgPlayer);
|
|
ChatMessage.create({ content: msgPlayer, whisper: [game.user] });
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static createMonnaie(name, valeur_deniers, img = "", enc = 0.01) {
|
|
let piece = {
|
|
name: name, type: 'monnaie', img: img, _id: randomID(16),
|
|
data: {
|
|
quantite: 0,
|
|
valeur_deniers: valeur_deniers,
|
|
encombrement: enc,
|
|
description: ""
|
|
}
|
|
}
|
|
return piece;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static afficherDemandePayer(som1, som2) {
|
|
som1 = (som1) ? som1.toLowerCase() : "0d";
|
|
som2 = (som2) ? som2.toLowerCase() : "0d";
|
|
let regExp = /(\d+)(\w+)/g;
|
|
let p1 = regExp.exec(som1);
|
|
regExp = /(\d+)(\w+)/g;
|
|
let p2 = regExp.exec(som2);
|
|
let sumd = 0;
|
|
let sums = 0;
|
|
if (p1[2] == 'd') sumd += Number(p1[1]);
|
|
if (p1[2] == 's') sums += Number(p1[1]);
|
|
if (p2[2] == 'd') sumd += Number(p2[1]);
|
|
if (p2[2] == 's') sums += Number(p2[1]);
|
|
|
|
let sumtotald = sumd + (sums * 100);
|
|
let msgPayer = "La somme de " + sums + " Sols et " + sumd + " Deniers est à payer, cliquer sur le lien ci-dessous si besoin.<br>";
|
|
msgPayer += "<a class='payer-button chat-card-button' data-somme-denier='" + sumtotald + "'>Payer</a>"
|
|
ChatMessage.create({ content: msgPayer });
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static chatDataSetup(content, modeOverride, isRoll = false, forceWhisper) {
|
|
let chatData = {
|
|
user: game.user.id,
|
|
rollMode: modeOverride || game.settings.get("core", "rollMode"),
|
|
content: content
|
|
};
|
|
|
|
if (["gmroll", "blindroll"].includes(chatData.rollMode)) chatData["whisper"] = ChatMessage.getWhisperRecipients("GM").map(u => u.id);
|
|
if (chatData.rollMode === "blindroll") chatData["blind"] = true;
|
|
else if (chatData.rollMode === "selfroll") chatData["whisper"] = [game.user];
|
|
|
|
if (forceWhisper) { // Final force !
|
|
chatData["speaker"] = ChatMessage.getSpeaker();
|
|
chatData["whisper"] = ChatMessage.getWhisperRecipients(forceWhisper);
|
|
}
|
|
|
|
return chatData;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static confirmerSuppressionSubacteur(actorSheet, li) {
|
|
let actorId = li.data("actor-id");
|
|
let actor = game.actors.get(actorId);
|
|
let msgTxt = "<p>Etes vous certain de vouloir supprimer le lien vers ce véhicule/monture/suivant : " + actor.data.name + " ?</p>";
|
|
let d = new Dialog({
|
|
title: "Confirmer la suppression du lien",
|
|
content: msgTxt,
|
|
buttons: {
|
|
delete: {
|
|
icon: '<i class="fas fa-check"></i>',
|
|
label: "Supprimer le lien",
|
|
callback: () => {
|
|
console.log("Delete : ", actorId);
|
|
actorSheet.actor.removeSubacteur(actorId);
|
|
li.slideUp(200, () => actorSheet.render(false));
|
|
}
|
|
},
|
|
cancel: {
|
|
icon: '<i class="fas fa-times"></i>',
|
|
label: "Annuler"
|
|
}
|
|
},
|
|
default: "cancel"
|
|
});
|
|
d.render(true);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static async confirmerSuppression(actorSheet, li) {
|
|
let itemId = li.data("item-id");
|
|
let objet = actorSheet.actor.getObjet(itemId);
|
|
let msgTxt = "<p>Etes vous certain de vouloir supprimer cet objet ?";
|
|
let buttons = {
|
|
delete: {
|
|
icon: '<i class="fas fa-check"></i>',
|
|
label: "Supprimer l'objet",
|
|
callback: () => {
|
|
console.log("Delete : ", itemId);
|
|
actorSheet.actor.deleteEmbeddedDocuments('Item', [itemId]);
|
|
li.slideUp(200, () => actorSheet.render(false));
|
|
}
|
|
},
|
|
cancel: {
|
|
icon: '<i class="fas fa-times"></i>',
|
|
label: "Annuler"
|
|
}
|
|
}
|
|
const docData = Misc.data(objet);
|
|
if (docData.type == 'conteneur' && docData.data.contenu.length > 0) {
|
|
msgTxt += "<br>Ce conteneur n'est pas vide. Choisissez l'option de suppression";
|
|
buttons['deleteall'] = {
|
|
icon: '<i class="fas fa-check"></i>',
|
|
label: "Supprimer le conteneur et tout son contenu",
|
|
callback: () => {
|
|
console.log("Delete : ", itemId);
|
|
actorSheet.actor.deleteAllConteneur(itemId);
|
|
li.slideUp(200, () => actorSheet.render(false));
|
|
}
|
|
}
|
|
}
|
|
msgTxt += "</p>";
|
|
let d = new Dialog({
|
|
title: "Confirmer la suppression",
|
|
content: msgTxt,
|
|
buttons: buttons,
|
|
default: "cancel"
|
|
});
|
|
d.render(true);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static afficherHeuresChanceMalchance(heureNaissance) {
|
|
if (heureNaissance) {
|
|
let ajustement = game.system.rdd.calendrier.getAjustementAstrologique(heureNaissance);
|
|
ChatMessage.create({
|
|
content: `A l'heure ${game.system.rdd.calendrier.getCurrentHeure()}, le modificateur de Chance/Malchance pour l'heure de naissance ${heureNaissance} est de : ${ajustement}.`,
|
|
whisper: ChatMessage.getWhisperRecipients("GM")
|
|
});
|
|
}
|
|
else {
|
|
ui.notifications.warn("Pas d'heure de naissance selectionnée")
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------- */
|
|
static checkThanatosXP(compName) {
|
|
if (compName.includes('Thanatos')) {
|
|
let message = "Vous avez mis des points d'Expérience dans la Voie de Thanatos !<br>Vous devez réduire manuellement d'un même montant d'XP une autre compétence Draconique.";
|
|
ChatMessage.create({
|
|
whisper: ChatMessage.getWhisperRecipients(game.user.name),
|
|
content: message
|
|
});
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------- */
|
|
static distribuerStress(stressValue, raison = 'Inconnu', nomJoueur = undefined) {
|
|
if (game.user.isGM) {
|
|
if (nomJoueur == undefined) {
|
|
for (let actor of game.actors) {
|
|
if (actor.hasPlayerOwner) {
|
|
actor.addCompteurValue('stress', stressValue, raison);
|
|
ui.notifications.info(`${actor.name} a reçu ${stressValue} points de Stress (raison : ${raison})`);
|
|
}
|
|
}
|
|
} else {
|
|
//console.log(stressValue, nomJoueur);
|
|
let joueur = game.users.find(user => user.name.toLowerCase() == nomJoueur.toLowerCase());
|
|
//console.log("Player", joueur, joueur.character );
|
|
joueur.character.addCompteurValue('stress', stressValue, raison);
|
|
ui.notifications.info(`${joueur.character.name} a reçu ${stressValue} points de Stress (raison : ${raison})`);
|
|
}
|
|
} else {
|
|
ui.notifications.warn("Seul le MJ est autorisé à utiliser la commande /stress");
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------- */
|
|
static async onRenderChatMessage(app, html, msg) {
|
|
// TODO
|
|
//console.log(app, html, msg);
|
|
}
|
|
|
|
}
|