diff --git a/module/actor.js b/module/actor.js index 84bc05c0..d90a164b 100644 --- a/module/actor.js +++ b/module/actor.js @@ -31,8 +31,9 @@ export class RdDActor extends Actor { static async create(data, options) { // Case of compendium global import - if (data instanceof Array) + 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) { return super.create(data, options); @@ -126,15 +127,18 @@ export class RdDActor extends Actor { } /* -------------------------------------------- */ - async performRoll(rollData) { - let rolled = await RdDResolutionTable.roll(rollData.caracValue, rollData.finalLevel); - //rolled.isPart = true; // Pour tester le particulières - rollData.rolled = rolled; // garder le résultat - //console.log("performRoll", rollData, rolled) - if ( !rollData.attackerRoll) // Store in the registry if not a defense roll - game.system.rdd.rollDataHandler[this.data._id] = rollData; + async performRoll(rollData, attacker = undefined) { - if (rolled.isPart && rollData.arme && !rollData.attackerRoll) { // Réussite particulière avec attaque -> choix ! + // garder le résultat + rollData.rolled = await RdDResolutionTable.roll(rollData.caracValue, rollData.finalLevel); + + + //console.log("performRoll", rollData) + if ( !rollData.attackerRoll) {// Store in the registry if not a defense roll + game.system.rdd.rollDataHandler[this.data._id] = rollData; + } + + if (rollData.rolled.isPart && rollData.arme && !rollData.attackerRoll) { // Réussite particulière avec attaque -> choix ! let message = "Réussite particulière en attaque"; message = message + "
Attaquer en Force"; // Finesse et Rapidité seulement en mêlée et si la difficulté libre est de -1 minimum @@ -144,19 +148,18 @@ export class RdDActor extends Actor { } ChatMessage.create( {content : message, whisper: ChatMessage.getWhisperRecipients( this.name ) } ); } else { - this.continueRoll(rollData); + this.continueRoll(rollData, attacker); } } /* -------------------------------------------- */ - async continueRoll(rollData) { + async continueRoll(rollData, attacker = undefined) { let rolled = rollData.rolled; - let result = rolled.roll; let quality = rolled.quality // Manage weapon categories when parrying (cf. page 115 ) let need_resist = false; // Do we need to make resistance roll for defender ? - if (rollData.arme && rollData.attackerRoll) { // Manage parade depeding on weapon type, and change roll results + if (rollData.arme && rollData.attackerRoll) { // Manage parade depending on weapon type, and change roll results let attCategory = RdDUtility.getArmeCategory(rollData.attackerRoll.arme); let defCategory = RdDUtility.getArmeCategory(rollData.arme); if (defCategory == "bouclier") @@ -166,7 +169,7 @@ export class RdDActor extends Actor { if (attCategory.match("epee") && (defCategory == "hache" || defCategory == "lance")) need_resist = true; } - if (this.data.type != 'entite' && (this.data.data.sante.sonne.value || rollData.particuliereAttaque == "finesse")) { + if (!this.isEntiteCauchemar() && (this.data.data.sante.sonne.value || rollData.particuliereAttaque == "finesse")) { rollData.needSignificative = true; } @@ -183,18 +186,21 @@ export class RdDActor extends Actor { explications = "" // In case of fight, replace the message per dommages + localization. it indicates if result is OK or not if (rollData.attackerRoll) { // Defense case ! - if (rollData.needSignificative && rolled.isSign ) { - explications += "
Attaque parée/esquivée !"; - } else if ( !rollData.needSignificative && rolled.isSuccess) { + if (rolled.isSign || (!rollData.needSignificative && rolled.isSuccess)) { explications += "
Attaque parée/esquivée !"; } else { explications += "
Esquive/Parade échouée, encaissement !"; if (rollData.needSignificative) - explications += "Significative nécessaire!"; - encaisser = true; + explications += " Significative nécessaire!"; } + encaisser = rollData.needSignificative ? !rolled.isSign : !rolled.isSuccess; } else { // This is the attack roll! if (rolled.isSuccess) { + let target = this.getTarget(); + if (await this.targetEntiteNonAccordee(target, 'avant-defense')) { + return; + } + // Message spécial pour la rapidité, qui reste difficile à gérer automatiquement if ( rollData.particuliereAttaque == 'rapidite') { ChatMessage.create( { content: "Vous avez attaqué en Rapidité. Ce cas n'est pas géré autmatiquement, donc suivez les directives de votre MJ pour gérer ce cas.", @@ -203,14 +209,14 @@ export class RdDActor extends Actor { rollData.domArmePlusDom = this._calculBonusDommages(rollData.selectedCarac, rollData.arme, rollData.particuliereAttaque == 'force' ); rollData.degats = new Roll("2d10").roll().total + rollData.domArmePlusDom; rollData.loc = RdDUtility.getLocalisation(); - for (let target of game.user.targets) { - rollData.mortalite = (rollData.mortalite) ? rollData.mortalite : "mortel";// Force default - rollData.mortalite = (target.actor.data.type == 'entite') ? "cauchemar" : rollData.mortalite; - console.log("Mortalité : ", rollData.mortalite, target.actor.data.type); + + if (target) + { + rollData.mortalite = RdDActor._calculMortaliteEncaissement(rollData, target); defenseMsg = RdDUtility.buildDefenseChatCard(this, target, rollData); explications += "
Cible : " + target.actor.data.name; } - explications += "
Dégâts : " + rollData.degats + "
Localisation : " + rollData.loc.label; + explications += "
Encaissement : " + rollData.degats + "
Localisation : " + rollData.loc.label; } else { explications = "
Echec ! Pas de dégâts"; } @@ -256,10 +262,16 @@ export class RdDActor extends Actor { // Get damages! if (encaisser) { - this.encaisserDommages(rollData.attackerRoll); + this.encaisserDommages(rollData.attackerRoll, attacker); } } + static _calculMortaliteEncaissement(rollData, target) { + const mortalite = target.actor.isEntiteCauchemar() ? "cauchemar" : (rollData.mortalite ? rollData.mortalite : "mortel"); + console.log("Mortalité : ", mortalite, target.actor.data.type); + return mortalite; + } + /* -------------------------------------------- */ _calculBonusDommages(carac, arme, isForce=false) { if ( arme.name.toLowerCase() == "esquive") return 0; // Specific case management @@ -919,6 +931,9 @@ export class RdDActor extends Actor { async santeIncDec(name, inc, isCritique = false) { const sante = duplicate(this.data.data.sante); let data = sante[name]; + if (data==undefined) { + return; + } let minValue = 0; if (this.type == 'personnage') { // TODO: les animaux/humanoïdes on théoriquement aussi un sconst, mais la SPA n'est pas passé par là @@ -1221,7 +1236,7 @@ export class RdDActor extends Actor { } /* -------------------------------------------- */ - async rollCompetence( name, armeItem=undefined, attackerRoll=undefined ) { + async rollCompetence( name, armeItem=undefined, attackerRoll=undefined, attacker = undefined) { let competence = RdDUtility.findCompetence( this.data.items, name); console.log("rollCompetence !!!", competence, armeItem, attackerRoll); // Common rollData values @@ -1275,11 +1290,23 @@ export class RdDActor extends Actor { let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-competence.html', rollData); if (rollData.arme) { - new RdDRollDialog("arme", html, rollData, this ).render(true); + if (await this.targetEntiteNonAccordee(this.getTarget(), 'avant-attaque')) { + return; + } + new RdDRollDialog("arme", html, rollData, this, attacker).render(true); } else { - new RdDRollDialog("competence", html, rollData, this ).render(true); + new RdDRollDialog("competence", html, rollData, this, attacker).render(true); } } + + getTarget() { + if (game.user.targets && game.user.targets.size == 1) { + for (let target of game.user.targets) { + return target; + } + } + return undefined; + } /* -------------------------------------------- */ async equiperObjet( itemID ) @@ -1321,7 +1348,13 @@ export class RdDActor extends Actor { } /* -------------------------------------------- */ - async encaisserDommages( attackerRoll ) { + async encaisserDommages( attackerRoll, attacker = undefined ) { + + + if (attacker && !await attacker.accorder(this, 'avant-encaissement')) { + return; + } + console.log("encaisserDommages", attackerRoll ) const armure = this.computeArmure( attackerRoll.loc, attackerRoll.domArmePlusDom); let degatsReel = attackerRoll.degats - armure; @@ -1356,17 +1389,17 @@ export class RdDActor extends Actor { /* -------------------------------------------- */ - parerAttaque( attackerRoll, armeId ) + parerAttaque( attackerRoll, armeId, attacker = undefined ) { let armeItem = this.getOwnedItem(armeId); // Item.data.data ! console.log("Going to PARY !!!!!!!!!", armeItem, attackerRoll.diffLibre); - this.rollCompetence( armeItem.data.data.competence, armeItem.data, attackerRoll ); + this.rollCompetence( armeItem.data.data.competence, armeItem.data, attackerRoll, attacker); } /* -------------------------------------------- */ - esquiverAttaque( attackerRoll ) + esquiverAttaque( attackerRoll, attacker = undefined ) { - this.rollCompetence( "esquive", undefined, attackerRoll ); + this.rollCompetence( "esquive", undefined, attackerRoll, attacker ); } /* -------------------------------------------- */ @@ -1376,7 +1409,73 @@ export class RdDActor extends Actor { return data; } + + + /* -- entites -- */ + /* retourne true si on peut continuer, false si on ne peut pas continuer */ + async targetEntiteNonAccordee(target, when='avant-encaissement') + { + if (target) + { + return !await this.accorder(target.actor, when); + } + return false; + } + async accorder(entite, when = 'avant-encaissement') + { + if (when != game.settings.get("foundryvtt-reve-de-dragon", "accorder-entite-cauchemar") + || !entite.isEntiteCauchemar() + || entite.isEntiteCauchemarAccordee(this)) { + return true; + } + + let rolled = await RdDResolutionTable.roll( this.getReveActuel(), - Number(entite.data.data.carac.niveau.value)); + + let message = { + content: "Jet de points actuels de rêve à " + rolled.finalLevel + RdDResolutionTable.explain(rolled) + "
", + whisper: ChatMessage.getWhisperRecipients(this.name) + }; + + if (rolled.isSuccess) { + await entite.setEntiteReveAccordee(this); + message.content += this.name + " s'est accordé avec " + entite.name; + } + else { + message.content+= this.name + " n'est pas accordé avec " + entite.name; + } + + ChatMessage.create( message ); + return rolled.isSuccess; + } + + isEntiteCauchemar() + { + return this.data.type == 'entite'; + } + + isEntiteCauchemarAccordee(attaquant) + { + if (!this.isEntiteCauchemar()) { return true; } + let resonnance = this.data.data.sante.resonnance; + return (resonnance.actors.find(it => it == attaquant._id)); + } + + async setEntiteReveAccordee(attaquant) + { + if (!this.isEntiteCauchemar()) { + ui.notifications.error("Impossible de s'accorder à " + this.name + ": ce n'est pas une entite de cauchemer/rêve"); + return; + } + let resonnance = duplicate(this.data.data.sante.resonnance); + if (resonnance.actors.find(it => it == attaquant._id)){ + // déjà accordé + return; + } + resonnance.actors.push(attaquant._id); + await this.update( {"data.sante.resonnance": resonnance}); + return; + } } diff --git a/module/rdd-main.js b/module/rdd-main.js index bbc31000..f2a7d055 100644 --- a/module/rdd-main.js +++ b/module/rdd-main.js @@ -105,9 +105,23 @@ Hooks.once("init", async function() { rollDataHandler: {}, TMRUtility: TMRUtility } - // Create specific settings + game.settings.register("foundryvtt-reve-de-dragon", "accorder-entite-cauchemar", { + name: "Accorder le rêve aux entités", + hint: "A quel moment les personnages doivent accorder leur rêve aux entités de cauchemar", + scope: "world", + config: true, + type: String, + choices: { // If choices are defined, the resulting setting will be a select menu + "avant-attaque": "Avant l'attaque", + "avant-defense": "Avant la défense", + "avant-encaissement": "Avant l'encaissement", + }, + default: "avant-encaissement" + }); + + // Create specific settings // game.settings.register("foundryvtt-reve-de-dragon", "configuration", { - // name: "configuration", + // name: "configuration", // scope: "world", // config: false, // type: Object @@ -119,7 +133,7 @@ Hooks.once("init", async function() { config: true, default: false, type: Boolean - }); + }); //game.settings.get("","") to retrieve it and game.settings.set("","", ) /** diff --git a/module/rdd-roll-dialog.js b/module/rdd-roll-dialog.js index 18594137..db7854ce 100644 --- a/module/rdd-roll-dialog.js +++ b/module/rdd-roll-dialog.js @@ -9,7 +9,7 @@ import { RdDResolutionTable } from "./rdd-resolution-table.js"; export class RdDRollDialog extends Dialog { /* -------------------------------------------- */ - constructor(mode, html, rollData, actor) { + constructor(mode, html, rollData, actor, attacker = undefined) { let myButtons if (mode == "sort") { @@ -47,12 +47,13 @@ export class RdDRollDialog extends Dialog { this.mode = mode this.rollData = rollData this.actor = actor + this.attacker = attacker } /* -------------------------------------------- */ performRollSort(html, isSortReserve = false) { this.rollData.isSortReserve = isSortReserve; - this.actor.performRoll(this.rollData); + this.actor.performRoll(this.rollData, attacker); } /* -------------------------------------------- */ diff --git a/module/rdd-utility.js b/module/rdd-utility.js index eff98f86..76a8885a 100644 --- a/module/rdd-utility.js +++ b/module/rdd-utility.js @@ -809,7 +809,7 @@ export class RdDUtility { rollData.attackerid = attackerid; rollData.defenderTokenId = defenderTokenId; let defenderToken = canvas.tokens.get( defenderTokenId ); - defenderToken.actor.encaisserDommages( rollData ); + defenderToken.actor.encaisserDommages( rollData, game.actors.get(attackerid)); } else { // Emit message for GM game.socket.emit("system.foundryvtt-reve-de-dragon", { msg: "msg_encaisser", @@ -824,7 +824,7 @@ export class RdDUtility { let defenderToken = canvas.tokens.get(event.currentTarget.attributes['data-defenderTokenId'].value ); let armeId = event.currentTarget.attributes['data-armeid'].value; let rollData = game.system.rdd.rollDataHandler[attackerid]; - defenderToken.actor.parerAttaque( rollData, armeId ); + defenderToken.actor.parerAttaque( rollData, armeId, game.actors.get(attackerid)); }); html.on("click", '#esquiver-button', event => { @@ -833,7 +833,7 @@ export class RdDUtility { let defenderToken = canvas.tokens.get(event.currentTarget.attributes['data-defenderTokenId'].value ); let rollData = game.system.rdd.rollDataHandler[attackerid]; //console.log("Esquive !", rollData, defenderActor); - defenderToken.actor.esquiverAttaque( rollData ); + defenderToken.actor.esquiverAttaque( rollData, game.actors.get(attackerid)); }); html.on("click", '#particuliere-attaque', event => { diff --git a/system.json b/system.json index c3b80e93..aa52ecb2 100644 --- a/system.json +++ b/system.json @@ -5,7 +5,7 @@ "version": "1.1.0", "minimumCoreVersion": "0.7.5", "compatibleCoreVersion": "0.7.7", - "templateVersion": 52, + "templateVersion": 53, "author": "LeRatierBretonnien", "esmodules": [ "module/rdd-main.js", "module/hook-renderChatLog.js" ], "styles": ["styles/simple.css"], diff --git a/template.json b/template.json index af5bebec..bc4d142c 100644 --- a/template.json +++ b/template.json @@ -50,6 +50,9 @@ "value": 10, "label": "Endurance", "derivee": false + }, + "resonnance": { + "actors" : [] } }, "compteurs": {