/* -------------------------------------------- */ import { VadentisUtility } from "./vadentis-utility.js"; /* -------------------------------------------- */ const MIN_PV = -50; const MIN_PE = -50; /* -------------------------------------------- */ /** * Extend the base Actor entity by defining a custom roll data structure which is ideal for the Simple system. * @extends {Actor} */ export class VadentisActor extends Actor { /* -------------------------------------------- */ /** * Override the create() function to provide additional SoS functionality. * * This overrided create() function adds initial items * Namely: Basic skills, money, * * @param {Object} data Barebones actor data which this function adds onto. * @param {Object} options (Unused) Additional options which customize the creation workflow. * */ static async create(data, options) { // Case of compendium global import if (data instanceof Array) { return super.create(data, options); } // If the created actor has items (only applicable to duplicated actors) bypass the new actor creation logic if (data.items) { let actor = super.create(data, options); return actor; } return super.create(data, options); } /* -------------------------------------------- */ async prepareData() { super.prepareData(); } /* -------------------------------------------- */ getCompetences() { return this.data.items.filter( item => item.type == 'competence'); } /* -------------------------------------------- */ getDonnees() { return this.data.items.filter( item => item.type == 'donnee'); } /* -------------------------------------------- */ getEglises() { return this.data.items.filter( item => item.type == 'eglise'); } /* -------------------------------------------- */ getSorts() { return this.data.items.filter( item => item.type == 'sort'); } /* -------------------------------------------- */ getAttributs() { return this.data.items.filter( item => item.type == 'attribut'); } /* -------------------------------------------- */ getTechniques() { return this.data.items.filter( item => item.type == 'technique'); } /* -------------------------------------------- */ getDevotions() { return this.data.items.filter( item => item.type == 'devotion'); } /* -------------------------------------------- */ getEquipements() { return this.data.items.filter( item => item.type == 'equipement' ); } /* -------------------------------------------- */ getArmes() { return this.data.items.filter( item => item.type == 'armecc' || item.type == 'tir' ); } /* -------------------------------------------- */ getArmures() { return this.data.items.filter( item => item.type == 'armurebouclier' ); } /* -------------------------------------------- */ getMonnaies() { return this.data.items.filter( item => item.type == 'monnaie' ); } /* -------------------------------------------- */ async updateCompetence( name, field, value) { let competence = this.data.items.find( item => item.type == 'competence' && item.name == name); if (competence) { let dataPath = 'data.'+field; await this.updateOwnedItem( { _id: competence._id, [dataPath]:value }); } } /* -------------------------------------------- */ async equiperObject( equipementId ) { let item = this.getOwnedItem(equipementId); if (item && item.data.data) { let update = { _id: item._id, "data.equipee": !item.data.data.equipee }; await this.updateEmbeddedEntity("OwnedItem", update); } } /* -------------------------------------------- */ calculerSommeStats( ) { for (const key in this.data.data.combat) { let combatData = this.data.data.combat[key]; combatData.total = combatData.base + combatData.malus + combatData.bonus; } for (const key in this.data.data.magie) { let magieData = this.data.data.magie[key]; magieData.total = magieData.base + magieData.malus + magieData.bonus; } } /* -------------------------------------------- */ async processSortDevotion( name, devotionSort ) { if ( this.data.data.stats.pointsenergie.value == 0) { // Vérification du ~ de points d'énergie ChatMessage.create({ content: `${this.name} n'a pas assez de Points d'Energie pour lancer ${name} ${devotionSort.name}` } ); return; } let scores = this.data.data.magie[(name =="devotion") ? 'devotion': 'matriseelementaire']; let statValue = scores.base + scores.malus + scores.bonus; let formulaFull = this.buildTexteFormula( scores ); let myRoll = await VadentisUtility.processRoll("1d20+"+statValue ); let msgData = { alias: this.name, title: `Sort ${devotionSort.name}`, isSort: true } if (myRoll.results[0] > 1 && myRoll.total >= devotionSort.data.difficulty) { msgData.img = 'systems/foundryvtt-vadentis/images/icons/tchat_sort_réussi.webp'; msgData.msg = `${this.name} a réussi son ${name} et perd ${devotionSort.data.pe} Points d'Energie (lancer : ${formulaFull} => ${myRoll.total} / ${devotionSort.data.difficulty}).`; let maintain = (devotionSort.data.ismaintain)?"Oui":"Non"; let complex = (devotionSort.data.complexactions)?"Oui":"Non"; msgData.msg += `
Peut être maintenu: ${maintain}
Actions complexes : ${complex}`; msgData.msg += `
Description : ${devotionSort.data.notes.replace(/<\/?[^>]+(>|$)/g, "")}`; let newEnergie = this.data.data.stats.pointsenergie.value - devotionSort.data.pe; await this.update( {'data.stats.pointsenergie.value': newEnergie }); if (myRoll.results[0] >= devotionSort.data.valuecritical ) { // Critique ? msgData.img = 'systems/foundryvtt-vadentis/images/icons/tchat_réussite_critique.webp'; msgData.msg += "
C'est une réussite critique !"; msgData.msg += `
Effet critique : ${devotionSort.data.criticaleffect.replace(/<\/?[^>]+(>|$)/g, "")}`; } else { msgData.msg += `
Effet : ${devotionSort.data.effect.replace(/<\/?[^>]+(>|$)/g, "")}`; } if ( devotionSort.data.damage != "") { let formula = devotionSort.data.damage; if (myRoll.results[0] >= devotionSort.data.valuecritical ) { // Critique ? msgData.msg += `
Et provoque les dégats critiques suivants : `; formula = devotionSort.data.damagecritical; } else { msgData.msg += `
Et provoque les dégats suivants : `; } } if ( newEnergie < 0) { msgData.msg += `
Attention : Les Points d'Energie de ${this.name} sont négatifs ! Il convient d'éditer ses Points de Vie en conséquence.`; } } else { msgData.img = 'systems/foundryvtt-vadentis/images/icons/tchat_sort_échoué.webp'; if (myRoll.results[0] == 1 ) { // Critique ? msgData.img = 'systems/foundryvtt-vadentis/images/icons/tchat_échec_critique.webp'; msgData.msg = `${this.name} a fait un échec critique à son lancer de ${name}`; } else { msgData.msg = `${this.name} a échoué son lancer de ${name}`; } } console.log(devotionSort.data.description, msgData); ChatMessage.create({ //whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name), content: await renderTemplate(`systems/foundryvtt-vadentis/templates/chat-generic-result.html`, msgData) }); } /* -------------------------------------------- */ async rollDamage( weapon, damageType ) { let formula = VadentisUtility.processDamageString( weapon.data.data[damageType], this ); let degatsRoll = await VadentisUtility.processRoll( formula ); let msgData = { alias: this.name, img: "systems/foundryvtt-vadentis/images/icons/tchat_dégâts_infligés.webp", title: `Dégâts de ${weapon.name}`, msg: `${this.name} frappe avec ${weapon.name} et produit ${degatsRoll.total} Points de Dégâts (${formula}).` } ChatMessage.create({ //whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name), content: await renderTemplate(`systems/foundryvtt-vadentis/templates/chat-generic-result.html`, msgData) }); } /* -------------------------------------------- */ async applyDamage( damageValue ) { let pvData = this.data.data.stats.pointsvie; let newValue = Math.max( pvData.value - damageValue, MIN_PV); await this.update( {'data.stats.pointsvie.value': newValue }); let msgData = { alias: this.name, img: "systems/foundryvtt-vadentis/images/icons/tchat_dégâts_infligés.webp", title: `${this.name} encaisse des dégâts !`, msg: `${this.name} encaisse ${damageValue} dégâts !` } ChatMessage.create({ //whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name), content: await renderTemplate(`systems/foundryvtt-vadentis/templates/chat-generic-result.html`, msgData) }); if ( game.user.isGM) { msgData.msg = `
Ses Points de Vie actuels sont désormais de ${newValue}.`; ChatMessage.create({ whisper: ChatMessage.getWhisperRecipients( 'GM'), content: await renderTemplate(`systems/foundryvtt-vadentis/templates/chat-generic-result.html`, msgData) }); } } /* -------------------------------------------- */ _getCombatValue(mydata) { if ( typeof mydata.base == 'number' ) { return mydata.base + mydata.malus + mydata.bonus; } else { return Number(mydata.base[0]) + Number(mydata.malus[0]) + Number(mydata.bonus[0]); } } /* -------------------------------------------- */ getInitiativeScore( ) { let initData = this.data.data.combat.initiative; return this._getCombatValue( initData); } /* -------------------------------------------- */ getDefenseScore( ) { let defenseData = this.data.data.combat.defense; return this._getCombatValue( defenseData); } /* -------------------------------------------- */ getForceScore( ) { let forceData = this.data.data.combat.force; return this._getCombatValue( forceData); } /* -------------------------------------------- */ getAttaqueScore( ) { let attaqueData = this.data.data.combat.attaque; return this._getCombatValue( attaqueData); } /* -------------------------------------------- */ buildTexteFormula( stat) { let signMalus = (stat.malus < 0) ? "" : "+"; return `1d20+ ${stat.base} ${signMalus} ${stat.malus} + ${stat.bonus}`; } /* -------------------------------------------- */ async rollSort( sortId ) { let sort = this.data.items.find( item => item.type == 'sort' && item._id == sortId ); if ( sort ) { this.processSortDevotion( "sort", sort); } } /* -------------------------------------------- */ async rollDevotion( devotionId ) { let devotion = this.data.items.find( item => item.type == 'devotion' && item._id == devotionId ); if ( devotion ) { this.processSortDevotion( "devotion", devotion); } } /* -------------------------------------------- */ async rollTechnique( techniqueId ) { let technique = this.data.items.find( item => item.type == 'technique' && item._id == techniqueId ); if (technique) { let msgData = { alias: this.name, img: technique.img, title: `Technique ${technique.name}` } if ( this.data.data.stats.pointsadrenaline.value < technique.data.pacost) { // Vérification du ~ de points d'adrénaline msgData.msg = `${this.name} n'a pas assez de Points d'Adrénaline pour éxecuter sa technique ${technique.name}`; } else { let newAdrenaline = this.data.data.stats.pointsadrenaline.value - technique.data.pacost; await this.update( {'data.stats.pointsadrenaline.value': newAdrenaline }); msgData.msg = `${this.name} execute sa technique ${technique.name}, pour un côut de ${technique.data.pacost} Points d'Adrénaline
Les effets sont : ${technique.data.effect}`; } ChatMessage.create({ //whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name), content: await renderTemplate(`systems/foundryvtt-vadentis/templates/chat-generic-result.html`, msgData) }); } else { ui.notifications.warn("Technique non trouvée"); } } /* -------------------------------------------- */ async rollCompetence( competenceId ) { let competence = this.data.items.find( item => item.type == 'competence' && item._id == competenceId); if ( competence) { let msgData = { alias: this.name, img: competence.img, rollMode: game.settings.get("core", "rollMode"), title: `Compétence ${competence.name}` } let statValue = competence.data.base + competence.data.malus + competence.data.bonus; let formulaFull = this.buildTexteFormula( competence.data ); let myRoll = await VadentisUtility.processRoll("1d20+"+statValue, msgData.rollMode ); msgData.msg = `${formulaFull} => ${myRoll.total}`; if (myRoll.results[0] == 1 ) { // Critique ? msgData.img = 'systems/foundryvtt-vadentis/images/icons/tchat_échec_critique.webp'; msgData.msg += `
C'est un échec critique !`; } if (myRoll.results[0] == 20 ) { // Critique ? msgData.img = 'systems/foundryvtt-vadentis/images/icons/tchat_réussite_critique.webp'; msgData.msg += `
C'est une réussite critique !`; } if (["gmroll", "blindroll"].includes(msgData.rollMode)) msgData["whisper"] = ChatMessage.getWhisperRecipients("GM").map(u => u.id); if (msgData.rollMode === "blindroll") msgData["blind"] = true; else if (msgData.rollMode === "selfroll") msgData["whisper"] = [game.user]; ChatMessage.create({ whisper: msgData["whisper"], content: await renderTemplate(`systems/foundryvtt-vadentis/templates/chat-generic-result.html`, msgData) }); } else { ui.notifications.warn("Compétence non trouvée"); } } /* -------------------------------------------- */ async genericRoll( stat, key ) { let statValue = this._getCombatValue( stat ); let formulaFull = this.buildTexteFormula( stat ); let myRoll = await VadentisUtility.processRoll("1d20+"+statValue ); let msgData = { alias: this.name, img: `systems/foundryvtt-vadentis/images/icons/feuille_perso_${key}.webp`, title: VadentisUtility.buildJetText( stat), msg: `${formulaFull} => ${myRoll.total}` } if (myRoll.results[0] == 1 ) { // Critique ? msgData.img = 'systems/foundryvtt-vadentis/images/icons/tchat_échec_critique.webp'; msgData.msg += `
C'est un échec critique !`; } if (myRoll.results[0] == 20 ) { // Critique ? msgData.img = 'systems/foundryvtt-vadentis/images/icons/tchat_réussite_critique.webp'; msgData.msg += `
C'est une réussite critique !`; } ChatMessage.create({ //whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name), content: await renderTemplate(`systems/foundryvtt-vadentis/templates/chat-generic-result.html`, msgData) }); } /* -------------------------------------------- */ async rollCombat( combatName ) { let stat = this.data.data.combat[combatName]; this.genericRoll( stat, combatName ); } /* -------------------------------------------- */ rollMagie( magieName ) { let stat = this.data.data.magie[magieName]; this.genericRoll( stat, magieName ); } /* -------------------------------------------- */ async decrementeMunition( arme ) { let armeTir = this.data.items.find( item => item.type == 'tir' && item.name == arme.name); if (armeTir) { let newMunition = armeTir.data.munition - 1; await this.updateOwnedItem( { _id: armeTir._id, 'data.munition': newMunition } ); } } /* -------------------------------------------- */ rollArme(armeId) { let target = VadentisUtility.getTarget(); if ( target ) { let arme = this.data.items.find( item => (item.type == 'armecc' || item.type == 'tir') && item._id == armeId); if (arme) { if ( arme.type == 'tir' && arme.data.munition <= 0 ) { ui.notifications.warn("Vous n'avez plus de munitions avec cette arme."); return; } let combatData = { attackerActorId: this._id, targetActorId: target.actor._id, arme: duplicate(arme) } if (game.user.isGM) { VadentisUtility.performAttack( combatData); } else { game.socket.emit("system.foundryvtt-vadentis", { name: "msg_attack", data: combatData } ); } } } else { ui.notifications.warn("Vous devez désigner une cible pour attaquer avec une arme.") } } }