diff --git a/module/actor.js b/module/actor.js index 5e91f3c7..8b95cfe0 100644 --- a/module/actor.js +++ b/module/actor.js @@ -223,8 +223,18 @@ export class RdDActor extends Actor { } /* -------------------------------------------- */ getReveActuel() { - return Misc.toInt(this.system.reve?.reve?.value ?? this.carac.reve.value); + switch(this.type) { + case 'personnage': + return Misc.toInt(this.system.reve?.reve?.value ?? this.carac.reve.value); + case 'creature': + case 'entite': + return Misc.toInt(this.system.carac.reve?.value) + case 'vehicule': + default: + return 0; + } } + /* -------------------------------------------- */ getChanceActuel() { return Misc.toInt(this.system.compteurs.chance?.value ?? 10); @@ -2535,48 +2545,43 @@ export class RdDActor extends Actor { } /* -------------------------------------------- */ - async rollCompetence(idOrName) { - let rollData = { competence: this.getCompetence(idOrName) } - + async rollCompetence(idOrName, options = {tryTarget: true}) { + let rollData = { + carac: this.system.carac, + competence: this.getCompetence(idOrName) + } if (rollData.competence.type == 'competencecreature') { - if (rollData.competence.system.iscombat) { - if (rollData.competence.system.ispossession) { - RdDPossession.onAttaquePossession(this, rollData.competence) - return - } - else if (RdDCombat.getTarget()) { - const arme = RdDItemCompetenceCreature.toActionArme(rollData.competence) - RdDCombat.createUsingTarget(this)?.attaque(competence, arme) - return + if (rollData.competence.system.iscombat && options.tryTarget) { + const target = RdDCombat.getTarget(); + if (target) { + if (rollData.competence.system.ispossession) { + RdDPossession.onAttaquePossession(target, this, rollData.competence) + } + else { + const arme = RdDItemCompetenceCreature.armeNaturelle(rollData.competence) + RdDCombat.rddCombatTarget(this, target).attaque(competence, arme) + } + return; } } - // Fake competence pour créature + // Transformer la competence de créature RdDItemCompetenceCreature.setRollDataCreature(rollData) - } else { - rollData.carac = this.system.carac } console.log("rollCompetence !!!", rollData); - const dialog = await RdDRoll.create(this, rollData, { html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html' }, { name: 'jet-competence', label: 'Jet ' + Grammar.apostrophe('de', rollData.competence.name), callbacks: [ this.createCallbackExperience(), this.createCallbackAppelAuMoral(), - { action: r => this._competenceResult(r) } + { action: r => this.$onRollCompetence(r) } ] }); dialog.render(true); } /* -------------------------------------------- */ - conjurerPossession(possession) { - let draconic = this.getDraconicOuPossession(); - RdDPossession.onAttaquePossession(this, draconic, possession) - } - - /* -------------------------------------------- */ - async _competenceResult(rollData) { + async $onRollCompetence(rollData) { await RdDResolutionTable.displayRollData(rollData, this, 'chat-resultat-competence.html') } @@ -3217,26 +3222,42 @@ export class RdDActor extends Actor { /* -------------------------------------------- */ rollArme(arme) { - let competence = this.getCompetence(arme.system.competence) - if (arme || (competence.type == 'competencecreature' && competence.system.iscombat)) { - if (competence.system.ispossession) { - RdDPossession.onAttaquePossession(this, competence); - } else { - RdDCombat.createUsingTarget(this)?.attaque(competence, arme); - } - } else { - this.rollCompetence(competence.name); + const target = RdDCombat.getTarget(); + if (!target) { + RdDConfirm.confirmer({ + settingConfirmer: "confirmer-combat-sans-cible", + content: `

Voulez vous faire un jet de compétence ${arme.system.competence} sans choisir de cible valide? +
Tous les jets de combats devront être gérés à la main +

`, + title: 'Ne pas utiliser les automatisation de combat', + buttonLabel: "Pas d'automatisation", + onAction: async () => { + this.rollCompetence(arme.system.competence, {tryTarget: false}) + } + }); + return; } + if (RdDCombat.isTargetEntite(target)){ + ui.notifications.warn(`Vous ne pouvez pas attaquer une entité non incarnée avec votre ${arme.name}!!!!`); + return; + } + // if (RdDCombat.isTargetEntite(targets[0])) { + // ui.notifications.warn("Vous ne pouvez pas cibler une entité non incarnée!"); + // return; + // } + + const competence = this.getCompetence(arme.system.competence) + if (competence.system.ispossession) { + return RdDPossession.onAttaquePossession(target, this, competence); + } + RdDCombat.rddCombatTarget(this, target).attaque(competence, arme); } /* -------------------------------------------- */ - _getTarget() { - if (game.user.targets && game.user.targets.size == 1) { - for (let target of game.user.targets) { - return target; - } - } - return undefined; + conjurerPossession(possession) { + // TODO: choix de la compétence de draconic ou de possession + let draconic = this.getDraconicOuPossession(); + RdDPossession.onConjurerPossession(this, draconic, possession) } /* -------------------------------------------- */ diff --git a/module/item-arme.js b/module/item-arme.js index fdd86ac2..22118f78 100644 --- a/module/item-arme.js +++ b/module/item-arme.js @@ -28,7 +28,7 @@ export class RdDItemArme extends Item { switch (arme ? arme.type : '') { case 'arme': return arme; case 'competencecreature': - return RdDItemCompetenceCreature.toActionArme(arme); + return RdDItemCompetenceCreature.armeNaturelle(arme); } return RdDItemArme.mainsNues(); } diff --git a/module/item-competencecreature.js b/module/item-competencecreature.js index 1c59a285..e4311271 100644 --- a/module/item-competencecreature.js +++ b/module/item-competencecreature.js @@ -1,4 +1,4 @@ -import { Misc } from "./misc.js"; + import { RdDCombatManager } from "./rdd-combat.js"; /* -------------------------------------------- */ @@ -12,12 +12,12 @@ export class RdDItemCompetenceCreature extends Item { rollData.competence.system.categorie = "creature" rollData.selectedCarac = rollData.carac.carac_creature if (rollData.competence.system.iscombat) { - rollData.arme = RdDItemCompetenceCreature.toActionArme(rollData.competence); + rollData.arme = RdDItemCompetenceCreature.armeNaturelle(rollData.competence); } } /* -------------------------------------------- */ - static toActionArme(competencecreature) { + static armeNaturelle(competencecreature) { if (RdDItemCompetenceCreature.isCompetenceAttaque(competencecreature)) { // si c'est un Item compétence: cloner pour ne pas modifier lma compétence let arme = (competencecreature instanceof Item) ? competencecreature.clone(): competencecreature; diff --git a/module/rdd-combat.js b/module/rdd-combat.js index 08c56b6a..ffdec171 100644 --- a/module/rdd-combat.js +++ b/module/rdd-combat.js @@ -222,7 +222,7 @@ export class RdDCombatManager extends Combat { static listActionsCreature(competences) { return competences.filter(it => RdDItemCompetenceCreature.isCompetenceAttaque(it)) - .map(it => RdDItemCompetenceCreature.toActionArme(it)); + .map(it => RdDItemCompetenceCreature.armeNaturelle(it)); } static listActionsPossessions(actor) { @@ -470,51 +470,49 @@ export class RdDCombat { } /* -------------------------------------------- */ - static createUsingTarget(attacker) { - const target = RdDCombat.getTarget() - if (target == undefined) { - ui.notifications.warn((game.user.targets?.size ?? 0) > 1 - ? "Vous devez choisir une seule cible à attaquer!" - : "Vous devez choisir une cible à attaquer!"); - } - else { - const defender = target?.actor; - const defenderTokenId = target?.id; - if (defender.type == 'entite' && defender.system.definition.typeentite == ENTITE_NONINCARNE) { - ui.notifications.warn("Vous ne pouvez pas cibler une entité non incarnée !!!!"); - } else { - return this.create(attacker, defender, defenderTokenId, target) - } - } + static rddCombatTarget(attacker, target) { + const defender = target?.actor; + const defenderTokenId = target?.id; + return new RdDCombat(attacker, defender, defenderTokenId, target) + } + + static isTargetEntite(target) { + return target?.actor.type == 'entite' && target?.actor.system.definition.typeentite == ENTITE_NONINCARNE; } /* -------------------------------------------- */ static getTarget() { - if (game.user.targets && game.user.targets.size == 1) { - for (let target of game.user.targets) { - return target; + const targets = game.user.targets; + switch (targets?.size ?? 0) { + case 1: + for (let t of targets) { + return t; + } + default: + ui.notifications.warn("Vous devez choisir une cible (et une seule) à attaquer!"); + return; + } + } + + /* -------------------------------------------- */ + static rddCombatForAttackerAndDefender(attackerId, defenderTokenId) { + const attacker = game.actors.get(attackerId); + let defender = defenderTokenId ? canvas.tokens.get(defenderTokenId)?.actor : undefined; + let target = undefined + if (!defenderTokenId || !defender) { + target = RdDCombat.getTarget() + if (!target) { + return; + } + defenderTokenId = target.id; + defender = target.actor; + if (!defenderTokenId || !defender) { + return; } } - return undefined; - } - - /* -------------------------------------------- */ - static create(attacker, defender, defenderTokenId, target = undefined) { return new RdDCombat(attacker, defender, defenderTokenId, target) } - /* -------------------------------------------- */ - static createForAttackerAndDefender(attackerId, defenderTokenId) { - const attacker = game.actors.get(attackerId); - if (defenderTokenId) { - const defenderToken = canvas.tokens.get(defenderTokenId); - const defender = defenderToken.actor; - - return RdDCombat.create(attacker, defender, defenderTokenId); - } - return RdDCombat.createUsingTarget(attacker) - } - /* -------------------------------------------- */ static onMsgEncaisser(msg) { let defender = canvas.tokens.get(msg.defenderTokenId).actor; @@ -523,7 +521,7 @@ export class RdDCombat { let attacker = msg.attackerId ? game.actors.get(msg.attackerId) : undefined; defender.encaisserDommages(attackerRoll, attacker); - const rddCombat = RdDCombat.createForAttackerAndDefender(msg.attackerId, msg.defenderTokenId); + const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(msg.attackerId, msg.defenderTokenId); rddCombat?.removeChatMessageActionsPasseArme(attackerRoll.passeArme); } } @@ -532,7 +530,7 @@ export class RdDCombat { static onMsgDefense(msg) { let defenderToken = canvas.tokens.get(msg.defenderTokenId); if (defenderToken && Misc.isUniqueConnectedGM()) { - const rddCombat = RdDCombat.createForAttackerAndDefender(msg.attackerId, msg.defenderTokenId); + const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(msg.attackerId, msg.defenderTokenId); rddCombat?.removeChatMessageActionsPasseArme(msg.defenderRoll.passeArme); rddCombat?._chatMessageDefense(msg.paramChatDefense, msg.defenderRoll); } @@ -559,11 +557,10 @@ export class RdDCombat { '#echec-total-attaque', ]) { html.on("click", button, event => { - const rddCombat = RdDCombat.createForAttackerAndDefender( + const rddCombat = RdDCombat.rddCombatForAttackerAndDefender( event.currentTarget.attributes['data-attackerId']?.value, event.currentTarget.attributes['data-defenderTokenId']?.value); if (rddCombat) { - rddCombat.onEvent(button, event); event.preventDefault(); } diff --git a/module/rdd-possession.js b/module/rdd-possession.js index 1683d435..1bda5b76 100644 --- a/module/rdd-possession.js +++ b/module/rdd-possession.js @@ -27,7 +27,133 @@ export class RdDPossession { } /* -------------------------------------------- */ - static updateEtatPossession(possession) { + static async onAttaquePossession(target, attacker, competence, possession = undefined) { + const defender = target.actor; + possession = duplicate(possession ?? this.searchPossessionFromEntite(attacker, defender) ?? (await this.createPossession(attacker, defender))); + + this.$updateEtatPossession(possession) + let rollData = { + mode: "possession", + isECNIDefender: false, + competence: competence, + possession: possession, + attacker: attacker, + defender: defender + }; + if (attacker.isCreature()) { + RdDItemCompetenceCreature.setRollDataCreature(rollData) + } + + await RdDPossession.$rollAttaquePossession(attacker, rollData); + } + + /* -------------------------------------------- */ + static async onConjurerPossession(attacker, competence, possession) { + possession = duplicate(possession); + this.$updateEtatPossession(possession) + let rollData = { + mode: "possession", + isECNIDefender: true, + competence: competence, + possession: possession, + attacker: attacker, + defender: game.actors.get(possession.system.possesseurid) + }; + await RdDPossession.$rollAttaquePossession(attacker, rollData); + } + + /* -------------------------------------------- */ + static async onDefensePossession(attackerId, defenderId, possessionId) { + let attacker = game.actors.get(attackerId) + let possession = attacker?.getPossession(possessionId) + defenderId = defenderId ?? possession?.system.possesseurid ?? undefined + let defender = game.actors.get(defenderId) + possession = possession ?? defender?.getPossession(possessionId) ?? undefined; + + if (!possession) { + ui.notifications.warn("Une erreur s'est produite : Aucune possession trouvée !!") + return + } + // Update for draconic roll + let rollData = { + mode: "conjuration", + isECNIDefender: defender.type == "entite", + possession: duplicate(possession), + attacker: attacker, + defender: defender, + competence: defender.getDraconicOuPossession(), + selectedCarac: defender.system.carac.reve, + forceCarac: { 'reve-actuel': { label: "Rêve Actuel", value: defender.getReveActuel() } } + } + rollData.competence.system.defaut_carac = 'reve-actuel' + + await RdDPossession.$rollDefensePossesion(defender, rollData); + } + + /* -------------------------------------------- */ + static async $rollAttaquePossession(attacker, rollData) { + const dialog = await RdDRoll.create(attacker, rollData, + { + html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html' + }, { + name: 'jet-possession', + label: rollData.isECNIDefender ? 'Conjurer la possession' : 'Possession', + callbacks: [ + { condition: r => (r.rolled.isSuccess), action: async (r) => await this.$onRollPossession(r, true) }, + { condition: r => (r.rolled.isEchec), action: async (r) => await this.$onRollPossession(r, false) }, + ] + }); + dialog.render(true); + } + + /* -------------------------------------------- */ + static async $rollDefensePossesion(defender, rollData) { + const dialog = await RdDRoll.create(defender, rollData, + { + html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-defense-possession.html' + }, + { + name: 'conjurer', + label: 'Conjurer une Possession', + callbacks: [ + { action: async (r) => await this.$onRollConjuration(r) } + ] + } + ); + dialog.render(true); + } + + /* -------------------------------------------- */ + static async $onRollPossession(rollData, isSuccess) { + rollData.possession.isSuccess = isSuccess; + this.$updateEtatPossession(rollData.possession); + await RdDResolutionTable.displayRollData(rollData, this, 'chat-resultat-possession.html'); + } + + /* -------------------------------------------- */ + static async $onRollConjuration(rollData) { + let actor = game.actors.get(rollData.possession.system.possedeid) + if (!rollData.rolled.isSuccess) { + if (rollData.isECNIDefender) { + rollData.possession.system.compteur-- + } else { + rollData.possession.system.compteur++ + } + let update = { _id: rollData.possession._id, "system.compteur": rollData.possession.system.compteur } + await actor.updateEmbeddedDocuments('Item', [update]) + } + + this.$updateEtatPossession(rollData.possession) + + await RdDResolutionTable.displayRollData(rollData, this, 'chat-resultat-possession.html') + if (rollData.possession.isPosseder || rollData.possession.isConjurer) { + // conjuration + actor.deleteEmbeddedDocuments("Item", [rollData.possession._id]) + } + } + + /* -------------------------------------------- */ + static $updateEtatPossession(possession) { possession.ptsConjuration = 0 possession.ptsPossession = 0 console.log("Possession", possession) @@ -47,111 +173,6 @@ export class RdDPossession { } } - /* -------------------------------------------- */ - static async resultConjuration(rollData) { - let actor = game.actors.get(rollData.possession.system.possedeid) - if (!rollData.rolled.isSuccess) { - if (rollData.isECNIDefender) { - rollData.possession.system.compteur-- - } else { - rollData.possession.system.compteur++ - } - let update = { _id: rollData.possession._id, "system.compteur": rollData.possession.system.compteur } - await actor.updateEmbeddedDocuments('Item', [update]) - } - - this.updateEtatPossession(rollData.possession) - await RdDResolutionTable.displayRollData(rollData, this, 'chat-resultat-possession.html') - if (rollData.possession.isPosseder || rollData.possession.isConjurer) { - actor.deleteEmbeddedDocuments("Item", [rollData.possession._id]) - } - } - - /* -------------------------------------------- */ - static async onDefensePossession(attackerId, defenderId, possessionId) { - let attacker = game.actors.get(attackerId) - let defender = game.actors.get(defenderId) - let possession = attacker.getPossession(possessionId) ?? defender.getPossession(possessionId) ; - if (!possession) { - ui.notifications.warn("Une erreur s'est produite : Aucune possession trouvée !!") - return - } - // Update for draconic roll - let rollData = { - mode: "conjuration", - isECNIDefender: defender.type == "entite", - possession: duplicate(possession), - attacker: attacker, - defender: defender, - competence: defender.getDraconicOuPossession(), - selectedCarac: defender.system.carac.reve, - forceCarac: { 'reve-actuel': { label: "Rêve Actuel", value: defender.getReveActuel() } } - } - rollData.competence.system.defaut_carac = 'reve-actuel' - - - const dialog = await RdDRoll.create(defender, rollData, - { - html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-defense-possession.html' - }, - { - name: 'conjurer', - label: 'Conjurer une Possession', - callbacks: [ - { action: async r => await this.resultConjuration(r) } - ] - } - ); - dialog.render(true) - } - - /* -------------------------------------------- */ - static async onAttaquePossession(attacker, competence, possession = undefined) { - const target = RdDCombat.getTarget() - if (target == undefined) { - ui.notifications.warn((game.user.targets?.size ?? 0) > 1 - ? "Vous devez choisir une seule cible à posséder!" - : "Vous devez choisir une cible à posséder!"); - return; - } - - const defender = target.actor; - possession = duplicate(possession ?? this.searchPossessionFromEntite(attacker, defender) ??(await this.createPossession(attacker, defender))); - - this.updateEtatPossession(possession) - let rollData = { - mode: "possession", - isECNIDefender: defender.type == "entite", - competence: competence, - possession: possession, - attacker: attacker, - defender: defender - }; - if (attacker.isCreature()) { - RdDItemCompetenceCreature.setRollDataCreature(rollData) - } - - const dialog = await RdDRoll.create(attacker, rollData, - { - html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html' - }, { - name: 'jet-possession', - label: rollData.isECNIDefender ? 'Conjurer la possession' : 'Possession', - callbacks: [ - { condition: r => (r.rolled.isSuccess), action: async r => await this._onRollPossession(r, true) }, - { condition: r => (r.rolled.isEchec), action: async r => await this._onRollPossession(r, false) }, - ] - }); - dialog.render(true) - } - - /* -------------------------------------------- */ - static async _onRollPossession(rollData, isSuccess) { - rollData.possession.isSuccess = isSuccess; - this.updateEtatPossession(rollData.possession); - await RdDResolutionTable.displayRollData(rollData, this, 'chat-resultat-possession.html'); - } - /* -------------------------------------------- */ static async createPossession(attacker, defender) { let possessionData = { diff --git a/module/settings/regles-optionelles.js b/module/settings/regles-optionelles.js index d9094180..6253b238 100644 --- a/module/settings/regles-optionelles.js +++ b/module/settings/regles-optionelles.js @@ -18,6 +18,7 @@ const listeReglesOptionelles = [ { group: 'Règles générales', name: 'appliquer-fatigue', descr: "Appliquer les règles de fatigue"}, { group: 'Règles générales', name: 'afficher-colonnes-reussite', descr: "Afficher le nombre de colonnes de réussite ou d'échec", default: false }, + { group: 'Confirmations', name: 'confirmer-combat-sans-cible', descr: "Confirmer avant une attaque sans cible", scope: "client"}, { group: 'Confirmations', name: 'confirmation-tmr', descr: "Confirmer pour monter dans les TMR", scope: "client"}, { group: 'Confirmations', name: 'confirmation-refouler', descr: "Confirmer avant de refouler", scope: "client"}, { group: 'Confirmations', name: 'confirmation-vider', descr: "Confirmer pour vider l'équipement", scope: "client"},