/* -------------------------------------------- */ import { VadentisCombat } from "./vadentis-combat.js"; /* -------------------------------------------- */ export class VadentisUtility { /* -------------------------------------------- */ static async preloadHandlebarsTemplates() { // Handle v12 removal of this helper Handlebars.registerHelper('select', function (selected, options) { const escapedValue = RegExp.escape(Handlebars.escapeExpression(selected)); const rgx = new RegExp(' value=[\"\']' + escapedValue + '[\"\']'); const html = options.fn(this); return html.replace(rgx, "$& selected"); }); const templatePaths = [ 'systems/foundryvtt-vadentis/templates/actor-sheet.html', 'systems/foundryvtt-vadentis/templates/item-sheet.html', 'systems/foundryvtt-vadentis/templates/editor-notes-gm.html', 'systems/foundryvtt-vadentis/templates/hud-actor-attaque.html', 'systems/foundryvtt-vadentis/templates/hud-actor-sort.html' ] return loadTemplates(templatePaths); } /* -------------------------------------------- */ static updateCombat(combat, round, diff, id) { if (game.user.isGM && combat.round != 0 && combat.turns && combat.active) { let turn = combat.turns.find(t => t.tokenId == combat.current.tokenId); ChatMessage.create({ content: `Round ${combat.round} : C'est au tour de ${turn.actor.name}
` }); canvas.tokens.get(turn.token._id).control(); canvas.tokens.cycleTokens(1, true); } } /* -------------------------------------------- */ static createOptionList(min, max) { let options = "" for (let i = min; i <= max; i++) { options += `\n`; } return options; } /* -------------------------------------------- */ static createDirectOptionList(min, max) { let options = {}; for (let i = min; i <= max; i++) { options[`${i}`] = `${i}`; } return options; } /* -------------------------------------------- */ static createDirectIntegerOptionList(min, max) { let options = {}; for (let i = min; i <= max; i++) { options[i] = `${i}`; } return options; } /* -------------------------------------------- */ static createDirectReverseOptionList(min, max) { let options = {}; for (let i = max; i >= min; i--) { options[`${i}`] = `${i}`; } return options; } /* -------------------------------------------- */ static getTarget() { if (game.user.targets && game.user.targets.size == 1) { for (let target of game.user.targets) { return target; } } return undefined; } /* -------------------------------------------- */ static processDamageString(formula, actor) { let workFormula = formula.toLowerCase(); if (workFormula.includes('bonus de force')) { workFormula = workFormula.replace('bonus de force', actor.getForceScore()); } return workFormula; } /* -------------------------------------------- */ static async processRoll(formula, rollMode) { let myRoll = new Roll(formula); await myRoll.roll(); if (game.modules.get("dice-so-nice")?.active) { await game.dice3d.showForRoll(myRoll, game.user, true); } return myRoll; } /* -------------------------------------------- */ static async performAttack(combatData) { let attacker = game.actors.get(combatData.attackerActorId); let defender = game.actors.get(combatData.targetActorId); console.log("ATTACK !!", attacker, defender); if (attacker && defender) { let defense = defender.getDefenseScore(); let attaque = attacker.getAttaqueScore(); let msgData = { alias: this.name, title: `${attacker.name} attaque ${defender.name}` } let tirMsg = ""; if (combatData.arme.type == 'tir') { attacker.decrementeMunition(combatData.arme); tirMsg += `
C'est un tir, les munitions de l'attaquant ont été décrémentées`; } let formulaTouche = "1d20+" + attaque; let formulaFull = attacker.buildTexteFormula(attacker.system.combat.attaque); let myRoll = await this.processRoll(formulaTouche); if (myRoll.dice[0].results[0].result > 1 && myRoll.total >= defense) { // Success ! let degats = `normaux : ${combatData.arme.system.damage}`; let formula = combatData.arme.system.damage.toLowerCase(); msgData.msg = `${attacker.name} a réussi son attaque sur ${defender.name} (${formulaFull} => ${myRoll.total} / ${defense}) !
Les dégâts sont ${degats}.`; msgData.msg += tirMsg; if (myRoll.dice[0].results[0].result >= combatData.arme.system.valuecritical) { degats = `critiques : ${combatData.arme.system.criticaldamage}`; formula = combatData.arme.system.criticaldamage.toLowerCase(); msgData.msg += `
C'est une réussite critique !`; } msgData.img = 'systems/foundryvtt-vadentis/images/icons/tchat_attaque_réussie.webp' formula = this.processDamageString(formula, attacker); let degatsRoll = await this.processRoll(formula); msgData.msg += `
Les dégats infligés sont de ${degatsRoll.total} (${formula}).`; defender.applyDamage(degatsRoll.total); } else { //Echec msgData.img = 'systems/foundryvtt-vadentis/images/icons/tchat_attaque_échouée.webp'; if (myRoll.dice[0].results[0].result == 1) { msgData.msg = `${attacker.name} a fait un échec critique et a raté son attaque sur ${defender.name} (${myRoll.total} / ${defense}) !`; } else { msgData.msg = `${attacker.name} a raté son attaque sur ${defender.name} (${myRoll.total} / ${defense}) !`; } msgData.msg += tirMsg; } ChatMessage.create({ //whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name), content: await renderTemplate(`systems/foundryvtt-vadentis/templates/chat-generic-result.html`, msgData) }); } else { ui.notifications.warn("Impossible de trouver l'attaquant et le défenseur.") } } /* -------------------------------------------- */ static buildJetText(stat) { let name = stat.label; let title = `Jet de ${name}`; if (name.toLowerCase().substr(0, 1).match(/[aeoiou]/g)) { title = `Jet d'${name}`; } return title; } /* -------------------------------------------- */ static registerChatCallbacks() { } /* -------------------------------------------- */ static fillRange(start, end) { return Array(end - start + 1).fill().map((item, index) => start + index); } /* -------------------------------------------- */ static onSocketMesssage(msg) { if (!game.user.isGM) return; // Only GM if (msg.name == 'msg_attack') { this.performAttack(msg.data); } } /* -------------------------------------------- */ static async loadCompendiumNames(compendium) { const pack = game.packs.get(compendium); let competences; await pack.getIndex().then(index => competences = index); return competences; } /* -------------------------------------------- */ static async loadCompendium(compendium, filter = item => true) { let compendiumItems = await SoSUtility.loadCompendiumNames(compendium); const pack = game.packs.get(compendium); let list = []; for (let compendiumItem of compendiumItems) { await pack.getEntity(compendiumItem._id).then(it => { const item = it.data; if (filter(item)) { list.push(item); } }); }; return list; } /* -------------------------------------------- */ static getDonnees() { return this.loadCompendiumNames('foundryvtt-vadentis.donnees'); } /* -------------------------------------------- */ static getEglises() { return this.loadCompendiumNames('foundryvtt-vadentis.croyances'); } /* -------------------------------------------- */ static async confirmDelete(actorSheet, li) { let itemId = li.data("item-id"); let msgTxt = "

Etes vous certain de souhaiter supprimer cet item ?"; let buttons = { delete: { icon: '', label: "Oui, à supprimer", callback: () => { actorSheet.actor.deleteEmbeddedDocuments('Item', [itemId]); li.slideUp(200, () => actorSheet.render(false)); } }, cancel: { icon: '', label: "Annuler" } } msgTxt += "

"; let d = new Dialog({ title: "Confirmer la suppression", content: msgTxt, buttons: buttons, default: "cancel" }); d.render(true); } }