700 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			700 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| import { ENTITE_INCARNE, renderTemplate, SHOW_DICE, SYSTEM_RDD } from "../constants.js";
 | |
| import { Grammar } from "../grammar.js";
 | |
| import { Misc } from "../misc.js";
 | |
| import { RdDResolutionTable } from "../rdd-resolution-table.js";
 | |
| import { RdDEncaisser } from "../rdd-roll-encaisser.js";
 | |
| import { RdDRoll } from "../rdd-roll.js";
 | |
| import { RdDUtility } from "../rdd-utility.js";
 | |
| import { ReglesOptionnelles } from "../settings/regles-optionnelles.js";
 | |
| import { RdDBaseActor } from "./base-actor.js";
 | |
| import { ITEM_TYPES } from "../constants.js";
 | |
| import { StatusEffects, STATUSES } from "../settings/status-effects.js";
 | |
| import { Targets } from "../targets.js";
 | |
| import { RdDConfirm } from "../rdd-confirm.js";
 | |
| import { CARACS, RdDCarac } from "../rdd-carac.js";
 | |
| import { RdDRollResult } from "../rdd-roll-result.js";
 | |
| 
 | |
| import { RdDItemArme } from "../item/arme.js";
 | |
| import { RdDItemCompetence } from "../item-competence.js";
 | |
| 
 | |
| import { ChatUtility } from "../chat-utility.js";
 | |
| import { DialogValidationEncaissement } from "../dialog-validation-encaissement.js";
 | |
| import { RdDCombat } from "../rdd-combat.js";
 | |
| import { RdDEmpoignade } from "../rdd-empoignade.js";
 | |
| import { RdDPossession } from "../rdd-possession.js";
 | |
| import { BASE_CORPS_A_CORPS, BASE_ESQUIVE, POSSESSION_SANS_DRACONIC } from "../item/base-items.js";
 | |
| import { RollDataAjustements } from "../rolldata-ajustements-v1.js";
 | |
| import { MappingCreatureArme } from "../item/mapping-creature-arme.mjs";
 | |
| import RollDialog from "../roll/roll-dialog.mjs";
 | |
| import { ATTAQUE_ROLL_TYPES, DEFAULT_ROLL_TYPES, DIFF, ROLL_TYPE_ATTAQUE, ROLL_TYPE_COMP, ROLL_TYPE_JEU, ROLL_TYPE_MEDITATION, ROLL_TYPE_OEUVRE, ROLL_TYPE_TACHE } from "../roll/roll-constants.mjs";
 | |
| import { OptionsAvancees, ROLL_DIALOG_V2 } from "../settings/options-avancees.js";
 | |
| import { PART_COMP } from "../roll/roll-part-comp.mjs";
 | |
| 
 | |
| /**
 | |
|  * Classe de base pour les acteurs disposant de rêve (donc, pas des objets)
 | |
|  * - Entités de rêve
 | |
|  * - Créatures de "sang": créatures et humanoides
 | |
|  */
 | |
| export class RdDBaseActorReve extends RdDBaseActor {
 | |
| 
 | |
|   prepareActorData() {
 | |
|     super.prepareActorData()
 | |
|     this.system.attributs.plusdom.value = this.getBonusDegat()
 | |
|     this.system.sante.endurance.max = this.getEnduranceMax()
 | |
|     this.system.sante.endurance.value = Math.min(this.system.sante.endurance.value, this.system.sante.endurance.max)
 | |
|   }
 | |
| 
 | |
|   getCarac() {
 | |
|     return foundry.utils.mergeObject(this.system.carac,
 | |
|       {
 | |
|         'reve-actuel': this.getCaracReveActuel(),
 | |
|         'chance-actuelle': this.getCaracChanceActuelle()
 | |
|       },
 | |
|       { inplace: false })
 | |
|   }
 | |
| 
 | |
|   getCaracChanceActuelle() {
 | |
|     return {
 | |
|       label: 'Chance actuelle',
 | |
|       value: this.getChanceActuel(),
 | |
|       type: "number"
 | |
|     };
 | |
|   }
 | |
| 
 | |
|   getCaracReveActuel() {
 | |
|     return {
 | |
|       label: 'Rêve actuel',
 | |
|       value: this.getReveActuel(),
 | |
|       type: "number"
 | |
|     };
 | |
|   }
 | |
| 
 | |
|   getTaille() { return Misc.toInt(this.system.carac.taille?.value) }
 | |
|   getConstitution() { return this.getReve() }
 | |
|   getForce() { return this.getReve() }
 | |
|   getAgilite() { return this.getForce() }
 | |
|   getReve() { return Misc.toInt(this.system.carac.reve?.value) }
 | |
|   getChance() { return this.getReve() }
 | |
| 
 | |
|   getReveActuel() { return this.getReve() }
 | |
|   getChanceActuel() { return this.getChance() }
 | |
| 
 | |
|   getEnduranceMax() { return Math.max(1, this.getTaille() + this.getConstitution()) }
 | |
|   getEncombrementMax() { return (this.getForce() + this.getTaille()) / 2 }
 | |
|   getBonusDegat() { return RdDCarac.getCaracDerivee(this.getEncombrementMax()).plusdom }
 | |
| 
 | |
|   getMoralTotal() { return 0 }
 | |
|   listeAmoureux() { return [] }
 | |
|   getProtectionNaturelle() { return Number(this.system.attributs?.protection?.value ?? 0) }
 | |
|   getSConst() { return 0 }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   isSurenc() { return false }
 | |
|   computeMalusSurEncombrement() { return 0 }
 | |
| 
 | |
|   ajustementAstrologique() { return 0 }
 | |
|   getMalusArmure() { return 0 }
 | |
| 
 | |
|   getEnduranceActuelle() {
 | |
|     return Number(this.system.sante?.endurance?.value ?? 0);
 | |
|   }
 | |
|   async jetEndurance(resteEndurance = undefined) { return { jetEndurance: 0, sonne: false } }
 | |
|   isDead() { return false }
 | |
|   isSonne() { return false }
 | |
|   blessuresASoigner() { return [] }
 | |
|   getEtatGeneral(options = { ethylisme: false }) { return 0 }
 | |
|   isActorCombat() { return true }
 | |
| 
 | |
|   getCaracInit(competence) {
 | |
|     if (!competence) {
 | |
|       return 0
 | |
|     }
 | |
|     if (competence.type == ITEM_TYPES.competencecreature) {
 | |
|       return competence.system.carac_value
 | |
|     }
 | |
|     return this.system.carac[competence.system.defaut_carac].value;
 | |
|   }
 | |
| 
 | |
|   listActions({ isAttaque = false, isEquipe = false }) {
 | |
|     return this.itemTypes[ITEM_TYPES.competencecreature]
 | |
|       .filter(it => it.isAttaque())
 | |
|       .map(it => it.attaqueCreature())
 | |
|       .filter(it => it != undefined);
 | |
|   }
 | |
| 
 | |
|   async computeArmure(dmg) { return this.getProtectionNaturelle() }
 | |
|   async remiseANeuf() { }
 | |
|   async appliquerAjoutExperience(rollData, hideChatMessage = 'show') { }
 | |
| 
 | |
|   async santeIncDec(name, inc, isCritique = false) { }
 | |
| 
 | |
|   async finDeRound(options = { terminer: false }) {
 | |
|     await this.$finDeRoundSuppressionEffetsTermines(options);
 | |
|     await this.finDeRoundBlessures();
 | |
|     await this.$finDeRoundSupprimerObsoletes();
 | |
|     await this.$finDeRoundEmpoignade();
 | |
|   }
 | |
| 
 | |
|   async $finDeRoundSuppressionEffetsTermines(options) {
 | |
|     for (let effect of this.getEffects()) {
 | |
|       if (effect.duration.type !== 'none' && (effect.duration.remaining <= 0 || options.terminer)) {
 | |
|         await effect.delete();
 | |
|         ChatMessage.create({ content: `${this.getAlias()} n'est plus ${Misc.lowerFirst(game.i18n.localize(effect.system.label))} !` });
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   async finDeRoundBlessures() {
 | |
|   }
 | |
| 
 | |
|   async $finDeRoundSupprimerObsoletes() {
 | |
|     const obsoletes = []
 | |
|       .concat(this.itemTypes[ITEM_TYPES.empoignade].filter(it => it.system.pointsemp <= 0))
 | |
|       .concat(this.itemTypes[ITEM_TYPES.possession].filter(it => it.system.compteur < -2 || it.system.compteur > 2))
 | |
|       .map(it => it.id);
 | |
|     await this.deleteEmbeddedDocuments('Item', obsoletes);
 | |
|   }
 | |
| 
 | |
|   async $finDeRoundEmpoignade() {
 | |
|     const immobilisations = this.itemTypes[ITEM_TYPES.empoignade].filter(it => it.system.pointsemp >= 2 && it.system.empoigneurid == this.id);
 | |
|     immobilisations.forEach(emp => RdDEmpoignade.onImmobilisation(this,
 | |
|       game.actors.get(emp.system.empoigneid),
 | |
|       emp
 | |
|     ))
 | |
|   }
 | |
| 
 | |
|   async setSonne(sonne = true) { }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   getCompetence(idOrName, options = {}) {
 | |
|     if (idOrName instanceof Item) {
 | |
|       return idOrName.isCompetence() ? idOrName : undefined
 | |
|     }
 | |
|     return RdDItemCompetence.findCompetence(
 | |
|       this.items.filter(it => [ITEM_TYPES.competence, ITEM_TYPES.competencecreature].includes(it.type)),
 | |
|       idOrName, options)
 | |
|   }
 | |
| 
 | |
|   getCompetences(name = undefined, options = { onMessage: message => { } }) {
 | |
|     if (name == undefined) {
 | |
|       return this.itemTypes[ITEM_TYPES.competence]
 | |
|     }
 | |
|     return RdDItemCompetence.findCompetences(this.itemTypes[ITEM_TYPES.competence], name, options)
 | |
|   }
 | |
| 
 | |
|   getCompetenceCorpsACorps(options = { onMessage: message => { } }) {
 | |
|     return this.getCompetence(BASE_CORPS_A_CORPS.name, options) ?? BASE_CORPS_A_CORPS
 | |
|   }
 | |
| 
 | |
|   getCompetencesEsquive(options = { onMessage: message => { } }) {
 | |
|     return this.getCompetences(BASE_ESQUIVE.name, options) ?? [BASE_ESQUIVE]
 | |
|   }
 | |
| 
 | |
|   getArmeParade(armeParadeId) {
 | |
|     return RdDItemArme.getArme(armeParadeId ? this.getEmbeddedDocument('Item', armeParadeId) : undefined)
 | |
|   }
 | |
| 
 | |
|   isForceInsuffisante(forceRequise) {
 | |
|     return false
 | |
|   }
 | |
| 
 | |
|   getDraconicOuPossession() { return POSSESSION_SANS_DRACONIC }
 | |
| 
 | |
|   getPossession(possessionId) {
 | |
|     return this.itemTypes[ITEM_TYPES.possession].find(it => it.system.possessionid == possessionId);
 | |
|   }
 | |
|   getEmpoignades() {
 | |
|     return this.itemTypes[ITEM_TYPES.empoignade];
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   async updateCreatureCompetence(idOrName, fieldName, value) {
 | |
|     let competence = this.getCompetence(idOrName);
 | |
|     if (competence) {
 | |
|       function getFieldPath(fieldName) {
 | |
|         switch (fieldName) {
 | |
|           case "niveau": return 'system.niveau';
 | |
|           case "dommages": return 'system.dommages';
 | |
|           case "carac_value": return 'system.carac_value';
 | |
|         }
 | |
|         return undefined
 | |
|       }
 | |
|       const path = getFieldPath(fieldName);
 | |
|       if (path) {
 | |
|         await competence.update({ [path]: value });
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   isEffectAllowed(effectId) { return false }
 | |
| 
 | |
|   getEffects(filter = e => true, forceRequise = undefined) {
 | |
|     const effects = this.getEmbeddedCollection("ActiveEffect")
 | |
|     const selected = effects.filter(filter)
 | |
|     if (forceRequise && this.isForceInsuffisante(forceRequise)) {
 | |
|       selected.push(StatusEffects.prepareActiveEffect(STATUSES.StatusForceWeak))
 | |
|     }
 | |
|     return selected
 | |
|   }
 | |
| 
 | |
|   getEffectByStatus(statusId) {
 | |
|     return this.getEffects().find(it => it.statuses.has(statusId));
 | |
|   }
 | |
| 
 | |
|   async setEffect(statusId, status) {
 | |
|     if (this.isEffectAllowed(statusId)) {
 | |
|       const effect = this.getEffectByStatus(statusId);
 | |
|       if (!status && effect) {
 | |
|         await this.deleteEmbeddedDocuments('ActiveEffect', [effect.id], { render: true})
 | |
|       }
 | |
|       if (status && !effect) {
 | |
|         await this.createEmbeddedDocuments("ActiveEffect", [StatusEffects.prepareActiveEffect(statusId)], { render: true})
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   async removeEffect(id) {
 | |
|     this.removeEffects(it => it.id == id)
 | |
|   }
 | |
| 
 | |
|   async removeEffects(filter = e => true) {
 | |
|     if (game.user.isGM) {
 | |
|       const effectsToRemove = this.getEffects(filter);
 | |
|       const ids = effectsToRemove.map(it => it.id);
 | |
|       await this.deleteEmbeddedDocuments('ActiveEffect', ids);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   isDemiReve() {
 | |
|     return this.getEffectByStatus(STATUSES.StatusDemiReve) != undefined
 | |
|   }
 | |
| 
 | |
|   getSurprise(isCombat = undefined) {
 | |
|     return StatusEffects.getSurprise(this.getEffects(), isCombat)
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   async computeEtatGeneral() {
 | |
|     // Par défaut, on ne calcule pas d'état général, seuls les personnages/créatures sont affectés
 | |
|     this.system.compteurs.etat.value = 0;
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   async openRollDialog({ name, label, template, rollData, callbacks }) {
 | |
|     const dialog = await RdDRoll.create(this, rollData,
 | |
|       { html: template, close: async html => await this._onCloseRollDialog(html) },
 | |
|       { name: name, label: label, callbacks: [this.createCallbackExperience(), this.createCallbackAppelAuMoral()].concat(callbacks) })
 | |
|     dialog.render(true)
 | |
|     return dialog
 | |
|   }
 | |
| 
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   createCallbackExperience() {
 | |
|     return { action: r => this.appliquerAjoutExperience(r) }
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   createCallbackAppelAuMoral() {
 | |
|     /* Si l'appel au moral est utilisé, on l'affiche dans le chat et on diminue éventuellement le moral */
 | |
|     return { action: r => this.appliquerAppelMoral(r) }
 | |
|   }
 | |
| 
 | |
|   async appliquerAjoutExperience(rollData, hideChatMessage = 'show') { }
 | |
|   async appliquerAppelMoral(rollData) { }
 | |
| 
 | |
|   async _onCloseRollDialog(html) { }
 | |
| 
 | |
|   async rollCaracCompetence(caracName, compName, diff, options = { title: "" }) {
 | |
|     RdDEmpoignade.checkEmpoignadeEnCours(this)
 | |
|     const competence = this.getCompetence(compName);
 | |
|     await this.openRollDialog({
 | |
|       name: 'jet-competence',
 | |
|       label: competence ? 'Jet ' + Grammar.apostrophe('de', competence.name) : `Jet sans compétence (${compName})`,
 | |
|       template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.hbs',
 | |
|       rollData: {
 | |
|         alias: this.getAlias(),
 | |
|         carac: this.system.carac,
 | |
|         selectedCarac: this.getCaracByName(caracName),
 | |
|         selectedCaracName: caracName,
 | |
|         diffLibre: diff,
 | |
|         competence: competence,
 | |
|         show: { title: options?.title ?? '' }
 | |
|       },
 | |
|       callbacks: [async r => this.$onRollCompetence(r, options)]
 | |
|     });
 | |
|   }
 | |
|   /**
 | |
|    * Méthode pour faire un jet prédéterminer sans ouvrir la fenêtre de dialogue
 | |
|    * @param {*} caracName code ou label de la caractéristique. On peut utiliser 'intel' pour Intellect.
 | |
|    * @param {*} compName nom de compétence ou nom abrégé.
 | |
|    * @param {*} diff difficulté (0 si undefined)
 | |
|    * @param {*} options
 | |
|    * @returns le jet effectué
 | |
|    */
 | |
|   async doRollCaracCompetence(caracName, compName, diff, options = { title: "" }) {
 | |
|     const carac = this.getCaracByName(caracName);
 | |
|     if (!carac) {
 | |
|       ui.notifications.warn(`${this.name} n'a pas de caractéristique correspondant à ${caracName}`)
 | |
|       return
 | |
|     }
 | |
|     const competence = this.getCompetence(compName);
 | |
|     let rollData = {
 | |
|       alias: this.getAlias(),
 | |
|       caracValue: Number(carac.value),
 | |
|       selectedCarac: carac,
 | |
|       competence: competence,
 | |
|       diffLibre: diff ?? 0,
 | |
|       show: { title: options?.title ?? '' }
 | |
|     }
 | |
|     RollDataAjustements.calcul(rollData, this);
 | |
|     await RdDResolutionTable.rollData(rollData);
 | |
|     this.gererExperience(rollData);
 | |
|     await RdDResolutionTable.displayRollData(rollData, this)
 | |
|     return rollData.rolled;
 | |
|   }
 | |
| 
 | |
|   gererExperience(rollData) { }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   async roll() {
 | |
|     RdDEmpoignade.checkEmpoignadeEnCours(this)
 | |
| 
 | |
|     const carac = this.getCarac()
 | |
|     const selectedCaracName = ['apparence', 'perception', 'force', 'reve'].find(it => carac[it] != undefined)
 | |
| 
 | |
|     await this.openRollDialog({
 | |
|       name: 'jet-quelconque',
 | |
|       label: 'Jet',
 | |
|       template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll.hbs',
 | |
|       rollData: {
 | |
|         alias: this.getAlias(),
 | |
|         carac: carac,
 | |
|         selectedCarac: carac[selectedCaracName],
 | |
|         selectedCaracName: selectedCaracName,
 | |
|         competences: this.itemTypes['competence']
 | |
|       },
 | |
|       callbacks: [{ action: r => this.$onRollCaracResult(r) }]
 | |
|     })
 | |
|   }
 | |
|   /* -------------------------------------------- */
 | |
|   async rollCarac(caracName, options = {}) {
 | |
|     if (Grammar.equalsInsensitive(caracName, CARACS.TAILLE)) {
 | |
|       return
 | |
|     }
 | |
|     if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
 | |
|       const rollData = {
 | |
|         ids: { actorId: this.id },
 | |
|         type: { allowed: DEFAULT_ROLL_TYPES, current: PART_COMP },
 | |
|         selected: {
 | |
|           carac: { key: caracName },
 | |
|           comp: options.resistance ? { key: undefined, forced: true } : undefined
 | |
|         }
 | |
|       }
 | |
|       RollDialog.create(rollData, options)
 | |
|       return
 | |
|     }
 | |
| 
 | |
|     foundry.utils.mergeObject(options, { resistance: false, diff: 0 }, { overwrite: false })
 | |
|     RdDEmpoignade.checkEmpoignadeEnCours(this)
 | |
|     let selectedCarac = this.getCaracByName(caracName)
 | |
|     const title = 'Jet ' + Grammar.apostrophe('de', selectedCarac.label);
 | |
|     const jetResistance = options.resistance ? caracName : undefined;
 | |
|     await this.openRollDialog({
 | |
|       name: 'jet-' + caracName,
 | |
|       label: title,
 | |
|       template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-carac.hbs',
 | |
|       rollData: {
 | |
|         alias: this.getAlias(),
 | |
|         selectedCarac: selectedCarac,
 | |
|         competences: this.itemTypes['competence'],
 | |
|         diffLibre: options.diff ?? 0,
 | |
|         jetResistance: jetResistance
 | |
|       },
 | |
|       callbacks: [{ action: r => this.$onRollCaracResult(r) }]
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   async $onRollCaracResult(rollData) {
 | |
|     // Final chat message
 | |
|     await RdDRollResult.displayRollData(rollData, this, 'chat-resultat-general.hbs');
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   async rollCompetenceV2(rollData) {
 | |
|     rollData.ids = rollData?.ids ?? {}
 | |
|     rollData.type = rollData.type ?? { allowed: DEFAULT_ROLL_TYPES }
 | |
|     rollData.ids.actorId = this.id
 | |
|     await RollDialog.create(rollData)
 | |
|   }
 | |
| 
 | |
|   async rollCompetence(idOrName, options = { tryTarget: true, arme: undefined }) {
 | |
|     RdDEmpoignade.checkEmpoignadeEnCours(this)
 | |
| 
 | |
|     const competence = this.getCompetence(idOrName);
 | |
|     if (competence.type != ITEM_TYPES.competencecreature && OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
 | |
|       const rollData = {
 | |
|         selected: {
 | |
|           comp: { key: competence.name },
 | |
|           diff: { type: DIFF.LIBRE, value: competence.system.default_diffLibre ?? 0 }
 | |
|         }
 | |
|       }
 | |
|       if (options.arme) {
 | |
|         rollData.selected.attaque = { arme: { id: options.arme.id }, comp: { id: competence.id } }
 | |
|         rollData.type = { allowed: ATTAQUE_ROLL_TYPES }
 | |
|       }
 | |
| 
 | |
|       await this.rollCompetenceV2(rollData)
 | |
|       return
 | |
|     }
 | |
| 
 | |
|     let rollData = {
 | |
|       carac: this.system.carac,
 | |
|       competence: competence,
 | |
|       arme: options.arme
 | |
|     }
 | |
|     if (competence.type == ITEM_TYPES.competencecreature) {
 | |
|       const token = RdDUtility.getSelectedToken(this)
 | |
|       const arme = MappingCreatureArme.armeCreature(competence)
 | |
|       if (arme && options.tryTarget && Targets.hasTargets()) {
 | |
|         Targets.selectOneTargetToken(target => {
 | |
|           if (arme.action == "possession") {
 | |
|             RdDPossession.onAttaquePossession(target, this, competence)
 | |
|           }
 | |
|           else {
 | |
|             RdDCombat.rddCombatTarget(target, this, token).attaque(competence, arme)
 | |
|           }
 | |
|         });
 | |
|         return
 | |
|       }
 | |
|       // Transformer la competence de créature
 | |
|       MappingCreatureArme.setRollDataCreature(rollData)
 | |
|     }
 | |
|     const dialogLabel = 'Jet ' + Grammar.apostrophe('de', competence.name);
 | |
|     await this.openRollDialog({
 | |
|       name: 'jet-competence',
 | |
|       label: dialogLabel,
 | |
|       template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.hbs',
 | |
|       rollData: rollData,
 | |
|       callbacks: [{ action: r => this.$onRollCompetence(r, options) }]
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   async $onRollCompetence(rollData, options) {
 | |
|     await RdDRollResult.displayRollData(rollData, this, 'chat-resultat-competence.hbs')
 | |
|     if (options?.onRollAutomate) {
 | |
|       options.onRollAutomate(rollData);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   rollAttaque(token) {
 | |
|     token = token ?? RdDUtility.getSelectedToken(this)
 | |
| 
 | |
|     if (Targets.hasTargets()) {
 | |
|       Targets.selectOneTargetToken(target => {
 | |
|         if (Targets.isTargetEntite(target)) {
 | |
|           ui.notifications.warn(`Vous ne pouvez pas attaquer une entité non incarnée!!!!`)
 | |
|           return
 | |
|         }
 | |
| 
 | |
|         RdDCombat.rddCombatTarget(target, this, token).attaqueV2();
 | |
|       })
 | |
|     }
 | |
|     else {
 | |
|       return RdDConfirm.confirmer({
 | |
|         settingConfirmer: "confirmer-combat-sans-cible",
 | |
|         content: `<p>Voulez vous faire une attaque sans choisir de cible valide?
 | |
|                   <br>Tous les jets de combats devront être gérés à la main
 | |
|                   </p>`,
 | |
|         title: 'Ne pas utiliser les automatisation de combat',
 | |
|         buttonLabel: "Pas d'automatisation",
 | |
|         onAction: async () => {
 | |
|           this.rollCompetenceV2({
 | |
|             ids: {
 | |
|               actorId: this.id,
 | |
|               actorTokenId: token?.id,
 | |
|             },
 | |
|             selected: {
 | |
|               conditions: { value: 0 }
 | |
|             },
 | |
|             type: {
 | |
|               allowed: [ROLL_TYPE_COMP, ROLL_TYPE_ATTAQUE, ROLL_TYPE_OEUVRE, ROLL_TYPE_TACHE, ROLL_TYPE_JEU],
 | |
|               current: ROLL_TYPE_COMP
 | |
|             }
 | |
|           })
 | |
|         }
 | |
|       })
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /** --------------------------------------------
 | |
|    * @param {*} arme item d'arme/compétence de créature
 | |
|    * @param {*} categorieArme catégorie d'attaque à utiliser: competence (== melee), lancer, tir; naturelle, possession
 | |
|    * @returns
 | |
|    */
 | |
|   rollArme(arme, categorieArme = 'competence', token = undefined) {
 | |
|     token = token ?? RdDUtility.getSelectedToken(this)
 | |
|     const compToUse = RdDItemArme.getCompetenceArme(arme, categorieArme)
 | |
|     if (!RdDItemArme.isUtilisable(arme)) {
 | |
|       ui.notifications.warn(`Arme inutilisable: ${arme.name} non équipée ou avec une résistance de 0 ou moins`)
 | |
|       return
 | |
|     }
 | |
|     if (!Targets.hasTargets()) {
 | |
|       RdDConfirm.confirmer({
 | |
|         settingConfirmer: "confirmer-combat-sans-cible",
 | |
|         content: `<p>Voulez vous faire un jet de ${compToUse} sans choisir de cible valide?
 | |
|                   <br>Tous les jets de combats devront être gérés à la main
 | |
|                   </p>`,
 | |
|         title: 'Ne pas utiliser les automatisation de combat',
 | |
|         buttonLabel: "Pas d'automatisation",
 | |
|         onAction: async () => {
 | |
|           this.rollCompetence(compToUse, { tryTarget: false, arme: arme })
 | |
|         }
 | |
|       });
 | |
|       return
 | |
|     }
 | |
| 
 | |
|     Targets.selectOneTargetToken(target => {
 | |
|       if (Targets.isTargetEntite(target)) {
 | |
|         ui.notifications.warn(`Vous ne pouvez pas attaquer une entité non incarnée avec votre ${arme.name}!!!!`);
 | |
|         return
 | |
|       }
 | |
| 
 | |
|       const competence = this.getCompetence(compToUse)
 | |
|       if (competence.isCompetencePossession()) {
 | |
|         return RdDPossession.onAttaquePossession(target, this, competence);
 | |
|       }
 | |
|       RdDCombat.rddCombatTarget(target, this, token).attaque(competence, arme);
 | |
|     })
 | |
|   }
 | |
| 
 | |
|   verifierForceMin(item) { }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   async encaisser() { await RdDEncaisser.encaisser(this) }
 | |
| 
 | |
|   async encaisserDommages(dmg, attacker = undefined, show = undefined, attackerToken = undefined, defenderToken = undefined) {
 | |
|     if (attacker && !await attacker.accorder(this, 'avant-encaissement')) {
 | |
|       return
 | |
|     }
 | |
|     if (!Misc.isOwnerPlayer(this)) {
 | |
|       return RdDBaseActor.remoteActorCall({
 | |
|         tokenId: attackerToken?.id ?? this.token?.id,
 | |
|         actorId: this.id,
 | |
|         method: 'encaisserDommages', args: [dmg, attacker, show, attackerToken, defenderToken]
 | |
|       })
 | |
|     }
 | |
| 
 | |
|     const armure = await this.computeArmure(dmg)
 | |
|     if (ReglesOptionnelles.isUsing('validation-encaissement-gr')) {
 | |
|       await this.encaisserDommagesValidationGR(dmg, armure, show, attackerToken, defenderToken);
 | |
|     }
 | |
|     else {
 | |
|       const jet = await RdDUtility.jetEncaissement(this, dmg, armure, { showDice: SHOW_DICE });
 | |
|       await this.$onEncaissement(jet, show, attackerToken, defenderToken)
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   async encaisserDommagesValidationGR(dmg, armure, show, attackerToken, defenderToken) {
 | |
|     if (!game.user.isGM) {
 | |
|       RdDBaseActor.remoteActorCall({
 | |
|         tokenId: this.token?.id,
 | |
|         actorId: this.id,
 | |
|         method: 'encaisserDommagesValidationGR', args: [dmg, armure, show, attackerToken, defenderToken]
 | |
|       })
 | |
|     } else {
 | |
|       DialogValidationEncaissement.validerEncaissement(this, dmg, armure,
 | |
|         jet => this.$onEncaissement(jet, show, attackerToken, defenderToken));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   async $onEncaissement(jet, show, attackerToken, defenderToken) {
 | |
|     await this.onAppliquerJetEncaissement(jet, attackerToken);
 | |
|     await this.$afficherEncaissement(jet, show, defenderToken);
 | |
|   }
 | |
| 
 | |
|   async onAppliquerJetEncaissement(encaissement, attackerToken) { }
 | |
| 
 | |
|   async $afficherEncaissement(encaissement, show, defenderToken) {
 | |
|     foundry.utils.mergeObject(encaissement, {
 | |
|       alias: defenderToken?.name ?? this.getAlias(),
 | |
|       hasPlayerOwner: this.hasPlayerOwner,
 | |
|       show: show ?? {}
 | |
|     }, { overwrite: false });
 | |
| 
 | |
|     await ChatUtility.createChatWithRollMode(
 | |
|       {
 | |
|         roll: encaissement.roll,
 | |
|         content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-resultat-encaissement.hbs', encaissement)
 | |
|       },
 | |
|       this
 | |
|     )
 | |
| 
 | |
|     if (!encaissement.hasPlayerOwner && encaissement.endurance != 0) {
 | |
|       encaissement = foundry.utils.duplicate(encaissement)
 | |
|       encaissement.isGM = true
 | |
|       ChatMessage.create({
 | |
|         whisper: ChatUtility.getGMs(),
 | |
|         content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-resultat-encaissement.hbs', encaissement)
 | |
|       });
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   async encaisserRecul(force, dmgArme = 0) {
 | |
|     const diffRecul = this.getTaille() - force - dmgArme
 | |
|     const rolled = await RdDResolutionTable.roll(10, diffRecul)
 | |
|     if (rolled.isSuccess) {
 | |
|       return 'encaisse'
 | |
|     }
 | |
|     if (rolled.isETotal || (await this.rollEquilibre(diffRecul)).isEchec) {
 | |
|       await this.setEffect(STATUSES.StatusProne, true)
 | |
|       return 'chute'
 | |
|     }
 | |
|     return 'recul'
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   async rollEquilibre(diff) {
 | |
|     // TODO: accrobatie optionnelle sur jet d'équilibre?
 | |
|     if (ReglesOptionnelles.isSet('acrobatie-pour-recul')) {
 | |
|       diff += Math.max(0, this.getCompetence('acrobatie')?.system.niveau ?? 0)
 | |
|     }
 | |
|     return await RdDResolutionTable.roll(this.getAgilite(), diff);
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   async accorder(entite, when = 'avant-encaissement') {
 | |
|     if (when != game.settings.get(SYSTEM_RDD, "accorder-entite-cauchemar")
 | |
|       || entite == undefined
 | |
|       || !entite.isEntite([ENTITE_INCARNE])
 | |
|       || entite.isEntiteAccordee(this)) {
 | |
|       return true
 | |
|     }
 | |
|     const rolled = await RdDResolutionTable.roll(this.getReveActuel(), - Number(entite.getNiveau()))
 | |
|     const rollData = {
 | |
|       alias: this.getAlias(),
 | |
|       rolled: rolled,
 | |
|       entite: entite.name,
 | |
|       selectedCarac: this.system.carac.reve
 | |
|     };
 | |
| 
 | |
|     if (rolled.isSuccess) {
 | |
|       await entite.setEntiteReveAccordee(this)
 | |
|     }
 | |
| 
 | |
|     await RdDRollResult.displayRollData(rollData, this, 'chat-resultat-accorder-cauchemar.hbs')
 | |
|     await this.appliquerAjoutExperience(rollData, true)
 | |
|     return rolled.isSuccess;
 | |
|   }
 | |
| 
 | |
|   isEntiteAccordee(attacker) { return true }
 | |
| 
 | |
|   async setEntiteReveAccordee(actor) {
 | |
|     ui.notifications.error("Impossible de s'accorder à " + this.getAlias() + ": ce n'est pas une entité incarnée");
 | |
|   }
 | |
| 
 | |
| }
 |