From f3453a46e7439870ed26c73004fdc96d00dcf65f Mon Sep 17 00:00:00 2001 From: Vincent Vandemeulebrouck Date: Wed, 16 Dec 2020 23:02:15 +0100 Subject: [PATCH] Ajouts combat #68 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - gestion de la feinte (sauf initiative) - gestion du bonus à l'attaque de la charge - dégats de corps à corps - bonus dégâts si surprise, début de gestion de surprise totale --- module/actor.js | 148 +------------------------- module/item-arme.js | 40 ++++--- module/rdd-bonus.js | 86 +++++++++++++++ module/rdd-combat.js | 174 ++++++++++++++----------------- module/rdd-resolution-table.js | 3 +- module/rdd-roll-encaisser.js | 4 +- module/rdd-roll.js | 90 ++++++++++------ module/rdd-tmr-dialog.js | 8 -- templates/dialog-competence.html | 52 +++++---- 9 files changed, 287 insertions(+), 318 deletions(-) create mode 100644 module/rdd-bonus.js diff --git a/module/actor.js b/module/actor.js index 1dcc6314..c30866b2 100644 --- a/module/actor.js +++ b/module/actor.js @@ -125,6 +125,7 @@ export class RdDActor extends Actor { /* -------------------------------------------- */ getBonusDegat() { + // TODO: gérer séparation et +dom créature/entité indépendament de la compétence return Misc.toInt(this.data.data.attributs.plusdom.value); } @@ -169,54 +170,13 @@ export class RdDActor extends Actor { } } - /* -------------------------------------------- */ - async performRoll(rollData, attacker = undefined) { - rollData.surprise = this.getSurprise(); - - // Manage weapon categories when parrying (cf. page 115 ) - if (rollData.arme && rollData.attackerRoll) { // Manage parade depending on weapon type, and change roll results - let attCategory = RdDItemArme.getCategorieArme(rollData.attackerRoll.arme); - let defCategory = RdDItemArme.getCategorieArme(rollData.arme); - if (defCategory == "bouclier") - rollData.needSignificative = false; - else if (attCategory != defCategory) - rollData.needSignificative = true; - // Do we need to make resistance roll for defender ? - if (attCategory.match("epee") && (defCategory == "hache" || defCategory == "lance")) - rollData.needResist = true; - } - if (!this.isEntiteCauchemar() && rollData.particuliereAttaque == "finesse") { - rollData.needSignificative = true; - } - - // garder le résultat - await RdDResolutionTable.rollData(rollData); - - //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 - if (rollData.selectedCarac.label == "Mêlée" && rollData.diffLibre < 0 ) { - message = message + "
Attaquer en Rapidité"; - message = message + "
Attaquer en Finesse"; - } - ChatMessage.create( {content : message, whisper: ChatMessage.getWhisperRecipients( this.name ) } ); - } else { - this.continueRoll(rollData, attacker); - } - } /* -------------------------------------------- */ async computeDeteriorationArme( rollData ) { const attackerRoll = rollData.attackerRoll; if (rollData.arme && attackerRoll) { // C'est une parade // Est-ce que l'attaque est une particulière, en force ou charge et que l'attaque n'en est pas une ? - if ( (rollData.needResist || attackerRoll.particuliereAttaque == 'force' || attackerRoll.isCharge) + if ( (rollData.needResist || attackerRoll.particuliereAttaque == 'force' || attackerRoll.tactique == 'charge') && !rollData.rolled.isPart ) { const dmg = attackerRoll.dmg.dmgArme + attackerRoll.dmg.dmgActor; let resistance = Misc.toInt(rollData.arme.data.resistance); @@ -252,7 +212,7 @@ export class RdDActor extends Actor { /* -------------------------------------------- */ async computeRecul( rollData, encaisser = undefined ) { // Calcul du recul (p. 132) if ( rollData.arme || encaisser ) { - if ( (rollData.attackerRoll.particuliereAttaque && rollData.attackerRoll.particuliereAttaque == 'force') || rollData.attackerRoll.isCharge) { + if ( (rollData.attackerRoll.particuliereAttaque && rollData.attackerRoll.particuliereAttaque == 'force') || rollData.attackerRoll.tactique == 'charge') { let reculNiveau = Misc.toInt(this.data.data.carac.taille.value) - (rollData.attackerRoll.forceValue+rollData.attackerRoll.arme.data.dommagesReels); let recul = await RdDResolutionTable.roll( 10, reculNiveau ); let msg = ""; @@ -273,103 +233,12 @@ export class RdDActor extends Actor { } } - /* -------------------------------------------- */ - async continueRoll(rollData, attacker = undefined) { - let rolled = rollData.rolled; - let quality = rolled.quality - - console.log(">>> ROLL", rollData, rolled); - this._appliquerAjoutExperience(rollData) - - let resumeCompetence = (rollData.competence) ? rollData.competence.name : (rollData.diffLibre + rollData.diffConditions); - let explications = "
Points de taches : " + rolled.ptTache + ", ajustement qualité: " + rolled.ptQualite; - - // Fight management ! - let defenseMsg; - let encaisser = false; - if (rollData.arme || (rollData.competence && rollData.competence.name.toLowerCase() == 'esquive') ) { - 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 (rolled.isSign || (!rollData.needSignificative && rolled.isSuccess)) { - await this.computeDeteriorationArme( rollData ); - explications += "
Attaque parée/esquivée !"; - } else { - explications += "
Esquive/Parade échouée, encaissement !"; - if (rollData.needSignificative) - explications += " Significative nécessaire!"; - } - encaisser = rollData.needSignificative ? !rolled.isSign : !rolled.isSuccess; - await this.computeRecul( rollData, encaisser ); - } 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.", - whisper: ChatMessage.getWhisperRecipients( this.name ) } ); - } - - rollData.dmg = RdDCombat.calculBonusDegats(rollData, this); - - if (target) - { - rollData.mortalite = RdDActor._calculMortaliteEncaissement(rollData, target); - defenseMsg = RdDUtility.buildDefenseChatCard(this, target, rollData); - explications += "
Cible : " + target.actor.data.name; - } - explications += "
Encaissement : " + rollData.degats + "
Localisation : " + rollData.dmg.loc.label; - } else { - explications = "
Echec ! Pas de dégâts"; - } - } - } - - // Save it for fight in the flags area - game.system.rdd.rollDataHandler[this.data._id] = duplicate(rollData); - - // Final chat message - let chatOptions = { - content: "Test : " + rollData.selectedCarac.label + " / " + resumeCompetence + "" - + "
Difficultés libre : " + rollData.diffLibre + " / conditions : " + Misc.toSignedString(rollData.diffConditions) +" / état : " + rollData.etat - + RdDResolutionTable.explain(rolled) - + explications - } - - ChatUtility.chatWithRollMode(chatOptions, this.name) - - // This an attack, generate the defense message - if (defenseMsg) { - defenseMsg.rollData = duplicate(rollData); - if (defenseMsg.toSocket) { - game.socket.emit("system.foundryvtt-reve-de-dragon", { - msg: "msg_defense", - data: defenseMsg - }); - if ( game.user.isGM ) { // Always push the message to the MJ - ChatMessage.create(defenseMsg); - } - } else { - defenseMsg.whisper = [game.user]; - ChatMessage.create(defenseMsg); - } - } - - // Get damages! - if (encaisser) { - this.encaisserDommages(rollData.attackerRoll, attacker); - } - } - /* -------------------------------------------- */ getSurprise() { if (this.isEntiteCauchemar()) { return ''; } + // TODO: gérer une liste de flags demi-surprise (avec icône sur le token)? if ( this.data.data.sante.sonne.value) { return 'demi'; } @@ -386,13 +255,6 @@ export class RdDActor extends Actor { return this.getSurprise() == 'totale'; } - /* -------------------------------------------- */ - 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; - } - /* -------------------------------------------- */ async dormirChateauDormant() { let message = { @@ -1905,7 +1767,7 @@ export class RdDActor extends Actor { armure: armure }).roll(); RdDDice.show(rollEncaissement); - let result = RdDUtility.computeBlessuresSante(rollEncaissement.total, attackerRoll.mortalite, attackerRoll.dmg.loc); + let result = RdDUtility.computeBlessuresSante(rollEncaissement.total, attackerRoll.dmg.mortalite, attackerRoll.dmg.loc); result.endurance = Math.max(result.endurance, -Number(this.data.data.sante.endurance.value)); await this.santeIncDec("vie", result.vie); await this.santeIncDec("endurance", result.endurance, (result.critiques > 0)); diff --git a/module/item-arme.js b/module/item-arme.js index cb5e587e..feedde73 100644 --- a/module/item-arme.js +++ b/module/item-arme.js @@ -44,26 +44,34 @@ export class RdDItemArme extends Item { /* -------------------------------------------- */ static armeUneOuDeuxMains(arme, aUneMain) { - arme.data.unemain = arme.data.unemain || !arme.data.deuxmains; - const uneOuDeuxMains = arme.data.unemain && arme.data.deuxmains; - const containsSlash = !Number.isInteger(arme.data.dommages) && arme.data.dommages.includes("/"); - if (containsSlash) { // Sanity check - arme = duplicate(arme); + if (arme) { + arme.data.unemain = arme.data.unemain || !arme.data.deuxmains; + const uneOuDeuxMains = arme.data.unemain && arme.data.deuxmains; + const containsSlash = !Number.isInteger(arme.data.dommages) && arme.data.dommages.includes("/"); + if (containsSlash) { // Sanity check + arme = duplicate(arme); - const tableauDegats = arme.data.dommages.split("/"); - if (aUneMain) - arme.data.dommagesReels = Number(tableauDegats[0]); - else // 2 mains - arme.data.dommagesReels = Number(tableauDegats[1]); - } - else { - arme.data.dommagesReels = Number(arme.data.dommages); - } + const tableauDegats = arme.data.dommages.split("/"); + if (aUneMain) + arme.data.dommagesReels = Number(tableauDegats[0]); + else // 2 mains + arme.data.dommagesReels = Number(tableauDegats[1]); + } + else { + arme.data.dommagesReels = Number(arme.data.dommages); + } - if (uneOuDeuxMains != containsSlash) { - ui.notifications.info("Les dommages de l'arme à 1/2 mains " + arme.name + " ne sont pas corrects (ie sous la forme X/Y)"); + if (uneOuDeuxMains != containsSlash) { + ui.notifications.info("Les dommages de l'arme à 1/2 mains " + arme.name + " ne sont pas corrects (ie sous la forme X/Y)"); + } } return arme; } + static mainsNues() { + return { + name: "Mains nues", + data: { unemain: true, deuxmains: false, dommages: 0, dommagesReels: 0, mortalite: 'non-mortel' } + } + } } diff --git a/module/rdd-bonus.js b/module/rdd-bonus.js new file mode 100644 index 00000000..88c6d1b2 --- /dev/null +++ b/module/rdd-bonus.js @@ -0,0 +1,86 @@ +import { RdDUtility } from "./rdd-utility.js"; + +const conditionsTactiques = [ + { type: '', descr: '', dmg: 0, attaque: 0, parade: 0, esquive: true }, + { type: 'charge', descr: 'Charge', dmg: 2, attaque: 4, parade: -4, esquive: false }, + { type: 'feinte', descr: 'Feinte', dmg: 1, attaque: 1, parade: 0, esquive: true }, + { type: 'pret', descr: 'prêt', dmg: 0, attaque: 0, parade: 0, esquive: true }, + { type: 'demi', descr: 'Demi-surprise', dmg: 1, attaque: 0, parade: 0, esquive: true }, + { type: 'totale', descr: 'Surprise totale', dmg: 10, attaque: 6, parade: 0, esquive: true }, +]; + +/* -------------------------------------------- */ +export class RdDBonus { + + /* -------------------------------------------- */ + static _find(condition) { + return conditionsTactiques.find(e => e.type == condition) || conditionsTactiques.find(e => e.type == 'pret'); + } + + /* -------------------------------------------- */ + static dmg(rollData, dmgActor, isCauchemar = false) { + let dmg = { total: 0, loc: RdDUtility.getLocalisation() }; + if (rollData.arme && rollData.arme.name.toLowerCase() == "esquive") { + // Specific case management + ui.notifications.warn("Calcul de bonus dégats sur eswquive"); + } else { + dmg.dmgArme = RdDBonus._dmgArme(rollData); + dmg.ignoreArmure = 0; // TODO: calculer pour arcs et arbaletes, gérer pour lmes créatures + dmg.dmgTactique = RdDBonus.dmgBonus(rollData.tactique); + dmg.dmgParticuliere = RdDBonus._dmgParticuliere(rollData); + dmg.dmgSurprise = RdDBonus.dmgBonus(rollData.surpriseDefenseur); + dmg.dmgActor = rollData.selectedCarac ? RdDBonus._dmgPerso(dmgActor, rollData.selectedCarac.label, dmg.dmgArme) : 0; + dmg.total = dmg.dmgSurprise + dmg.dmgTactique + dmg.dmgArme + dmg.dmgActor + dmg.dmgParticuliere; + dmg.mortalite = RdDBonus._calculMortalite(rollData, isCauchemar) + } + return dmg; + } + + /* -------------------------------------------- */ + static description(condition) { + return RdDBonus._find(condition).descr; + } + + /* -------------------------------------------- */ + static dmgBonus(condition) { + return RdDBonus._find(condition).dmg; + } + + /* -------------------------------------------- */ + static bonusAttaque(condition) { + return RdDBonus._find(condition).attaque; + } + + /* -------------------------------------------- */ + static _calculMortalite(rollData, isCauchemar) { + if (isCauchemar){ + return "cauchemar"; + }if (rollData.dmg && rollData.dmg.mortalite) { + return rollData.dmg.mortalite; + } + if (rollData.arme && rollData.arme.data.mortalite) { + return rollData.arme.data.mortalite; + } + return "mortel"; + } + + /* -------------------------------------------- */ + static _dmgArme(rollData) { + return rollData.arme ? parseInt(rollData.arme.data.dommages) : 0; + } + + /* -------------------------------------------- */ + static _dmgPerso(dmgActor, categorie, dmgArme) { + switch (categorie) { + case "Tir": return 0; + case "Lancer": return Math.max(0, Math.min(dmgArme, dmgActor)); + } + return dmgActor; + } + + /* -------------------------------------------- */ + static _dmgParticuliere(rollData) { + return rollData.particuliereAttaque == 'force' ? 5 : 0; + } + +} \ No newline at end of file diff --git a/module/rdd-combat.js b/module/rdd-combat.js index bb9d9908..a9c1b4fe 100644 --- a/module/rdd-combat.js +++ b/module/rdd-combat.js @@ -1,11 +1,10 @@ -import { RdDActor } from "./actor.js"; import { ChatUtility } from "./chat-utility.js"; import { RdDItemArme } from "./item-arme.js"; import { RdDItemCompetence } from "./item-competence.js"; import { Misc } from "./misc.js"; +import { RdDBonus } from "./rdd-bonus.js"; import { RdDResolutionTable } from "./rdd-resolution-table.js"; import { RdDRoll } from "./rdd-roll.js"; -import { RdDUtility } from "./rdd-utility.js"; export class RdDCombat { @@ -75,7 +74,6 @@ export class RdDCombat { ui.notifications.warn("Action automatisée impossible, le jet de l'attaquant a été perdu (suite à un raffraichissement?)") return; } - // TODO: enlever le ChatMessage? switch (button) { case '#particuliere-attaque': return await this.choixParticuliere(rollData, event.currentTarget.attributes['data-mode'].value); case '#parer-button': return this.parade(rollData, event.currentTarget.attributes['data-armeid'].value); @@ -84,6 +82,41 @@ export class RdDCombat { } } + + /* -------------------------------------------- */ + static isEchec(rollData) { + switch (rollData.surprise) { + case 'demi': return !rollData.rolled.isSign; + case 'totale': return true; + } + return rollData.rolled.isEchec; + } + + /* -------------------------------------------- */ + static isEchecTotal(rollData) { + if (rollData.arme && rollData.surprise == 'demi') { + return rollData.rolled.isEchec; + } + return rollData.rolled.isETotal; + } + + /* -------------------------------------------- */ + static isParticuliere(rollData) { + if (rollData.arme && rollData.surprise) { + return false; + } + return rollData.rolled.isPart; + } + + /* -------------------------------------------- */ + static isReussite(rollData) { + switch (rollData.surprise) { + case 'demi': return rollData.rolled.isSign; + case 'totale': return false; + } + return rollData.rolled.isSuccess; + } + /* -------------------------------------------- */ async attaque(competence, arme) { if (!await this.accorderEntite('avant-attaque')) { @@ -99,10 +132,10 @@ export class RdDCombat { label: 'Attaque: ' + (arme ? arme.name : competence.name), callbacks: [ this.attacker.createCallbackExperience(), - { condition: RdDResolutionTable.isParticuliere, action: r => this._onAttaqueParticuliere(r) }, - { condition: r => (RdDResolutionTable.isReussite(r) && !RdDResolutionTable.isParticuliere(r)), action: r => this._onAttaqueNormale(r) }, - { condition: RdDResolutionTable.isEchecTotal, action: r => this._onAttaqueEchecTotal(r) }, - { condition: RdDResolutionTable.isEchec, action: r => this._onAttaqueEchec(r) } + { condition: RdDCombat.isParticuliere, action: r => this._onAttaqueParticuliere(r) }, + { condition: r => (RdDCombat.isReussite(r) && !RdDCombat.isParticuliere(r)), action: r => this._onAttaqueNormale(r) }, + { condition: RdDCombat.isEchecTotal, action: r => this._onAttaqueEchecTotal(r) }, + { condition: RdDCombat.isEchec, action: r => this._onAttaqueEchec(r) } ] }); dialog.render(true); @@ -113,16 +146,21 @@ export class RdDCombat { let rollData = { coupsNonMortels: false, competence: competence, - surprise: this.attacker.getSurprise() + surprise: this.attacker.getSurprise(), + surpriseDefenseur: this.defender.getSurprise() }; if (this.attacker.isCreature()) { this._modifieRollDataCreature(rollData, competence); } - else { + else if (arme) { // Usual competence rollData.arme = RdDItemArme.armeUneOuDeuxMains(arme, RdDItemCompetence.isArmeUneMain(competence)); } + else { + // sans armes: à mains nues + rollData.arme = RdDItemArme.mainsNues(); + } return rollData; } @@ -139,6 +177,11 @@ export class RdDCombat { dommagesReels: competence.data.dommages } }; + + // rollData.dmg = { + // dmgArme: competence.data.dommages, + // total: competence.data.dommages + // }; } /* -------------------------------------------- */ @@ -172,13 +215,12 @@ export class RdDCombat { whisper: ChatMessage.getWhisperRecipients(this.attacker.name) }); } - rollData.dmg = RdDCombat.calculBonusDegats(rollData, this.attacker); + rollData.dmg = RdDBonus.dmg(rollData, this.attacker.getBonusDegat(), this.defender.isEntiteCauchemar()); if (this.target) { - rollData.mortalite = this._calculMortaliteEncaissement(rollData); explications += "
Cible : " + this.defender.data.name; } - explications += "
Encaissement à "+ Misc.toSignedString(rollData.dmg.total)+ " (" + rollData.dmg.loc.label+")"; + explications += "
Encaissement à " + Misc.toSignedString(rollData.dmg.total) + " (" + rollData.dmg.loc.label + ")"; // Save rollData for defender game.system.rdd.rollDataHandler[this.attackerId] = duplicate(rollData); @@ -202,14 +244,16 @@ export class RdDCombat { let content = "" + this.defender.name + " doit se défendre :"; - // parades - for (const arme of this._filterArmesParade(this.defender.data.items, rollData.competence.data.categorie)) { - content += "
Parer avec " + arme.name + ""; - } - - // esquive - if (rollData.competence.data.categorie == 'melee' || rollData.competence.data.categorie == "lancer" || rollData.competence.data.categorie == 'competencecreature') { - content += "
Esquiver"; + if (this.defender.getSurprise() != 'totale') { + // parades + for (const arme of this._filterArmesParade(this.defender.data.items, rollData.competence.data.categorie)) { + content += "
Parer avec " + arme.name + ""; + } + + // esquive + if (rollData.competence.data.categorie == 'melee' || rollData.competence.data.categorie == "lancer" || rollData.competence.data.categorie == 'competencecreature') { + content += "
Esquiver"; + } } // encaisser @@ -257,13 +301,6 @@ export class RdDCombat { }); } } - /* -------------------------------------------- */ - _calculMortaliteEncaissement(rollData) { - const mortalite = this.defender.isEntiteCauchemar() ? "cauchemar" : (rollData.mortalite ? rollData.mortalite : "mortel"); - console.log("Mortalité : ", mortalite, this.defender.data.type); - return mortalite; - } - _onAttaqueEchecTotal(rollData) { console.log("RdDCombat.onEchecTotal >>>", rollData); @@ -306,10 +343,10 @@ export class RdDCombat { label: 'Parade: ' + (arme ? arme.name : rollData.competence.name), callbacks: [ this.defender.createCallbackExperience(), - { condition: RdDResolutionTable.isParticuliere, action: r => this._onParadeParticuliere(r) }, - { condition: RdDResolutionTable.isReussite, action: r => this._onParadeNormale(r) }, - { condition: RdDResolutionTable.isEchecTotal, action: r => this._onParadeEchecTotal(r) }, - { condition: RdDResolutionTable.isEchec, action: r => this._onParadeEchec(r) } + { condition: RdDCombat.isParticuliere, action: r => this._onParadeParticuliere(r) }, + { condition: RdDCombat.isReussite, action: r => this._onParadeNormale(r) }, + { condition: RdDCombat.isEchecTotal, action: r => this._onParadeEchecTotal(r) }, + { condition: RdDCombat.isEchec, action: r => this._onParadeEchec(r) } ] }); dialog.render(true); @@ -334,6 +371,7 @@ export class RdDCombat { competence: competence, arme: arme.data, surprise: this.defender.getSurprise(), + surpriseDefenseur: this.defender.getSurprise(), needSignificative: this._needSignificative(attackerRoll) || RdDItemArme.needParadeSignificative(armeAttaque, armeParade), needResist: this._needResist(armeAttaque, armeParade), carac: this.defender.data.data.carac @@ -401,7 +439,7 @@ export class RdDCombat { console.log("RdDCombat._onParadeEchec >>>", rollData); let explications = "
Parade échouée, encaissement ! "; - explications += RdDCombat.descriptionSurprise(rollData.surprise); + explications += RdDBonus.description(rollData.surprise); if (rollData.needSignificative) { explications += " Significative nécessaire!"; } @@ -436,10 +474,10 @@ export class RdDCombat { label: 'Esquiver', callbacks: [ this.defender.createCallbackExperience(), - { condition: RdDResolutionTable.isParticuliere, action: r => this._onEsquiveParticuliere(r) }, - { condition: RdDResolutionTable.isReussite, action: r => this._onEsquiveNormale(r) }, - { condition: RdDResolutionTable.isEchecTotal, action: r => this._onEsquiveEchecTotal(r) }, - { condition: RdDResolutionTable.isEchec, action: r => this._onEsquiveEchec(r) }, + { condition: RdDCombat.isParticuliere, action: r => this._onEsquiveParticuliere(r) }, + { condition: RdDCombat.isReussite, action: r => this._onEsquiveNormale(r) }, + { condition: RdDCombat.isEchecTotal, action: r => this._onEsquiveEchecTotal(r) }, + { condition: RdDCombat.isEchec, action: r => this._onEsquiveEchec(r) }, ] }); dialog.render(true); @@ -452,6 +490,7 @@ export class RdDCombat { attackerRoll: attackerRoll, competence: competence, surprise: this.defender.getSurprise(), + surpriseDefenseur: this.defender.getSurprise(), needSignificative: this._needSignificative(attackerRoll), carac: this.defender.data.data.carac }; @@ -497,7 +536,7 @@ export class RdDCombat { console.log("RdDCombat._onEsquiveEchec >>>", rollData); let explications = "
Esquive échouée, encaissement ! "; - explications += RdDCombat.descriptionSurprise(rollData.surprise); + explications += RdDBonus.description(rollData.surprise); if (rollData.needSignificative) { explications += " Significative nécessaire!"; } @@ -567,65 +606,4 @@ export class RdDCombat { return rolled.isSuccess; } - /* -------------------------------------------- */ - static calculBonusDegats(rollData, actor) { - let dmg = { total: 0, loc: RdDUtility.getLocalisation() }; - if (rollData.arme.name.toLowerCase() == "esquive") { - // Specific case management - ui.notifications.warn("Calcul de bonus dégats sur eswquive") - return dmg; - } - dmg.dmgArme = RdDCombat._dmgArme(rollData); - dmg.ignoreArmure = 0; // TODO: calculer pour arcs et arbaletes, gérer pour lmes créatures - dmg.dmgTactique= RdDCombat._dmgTactique(rollData); - dmg.dmgParticuliere= RdDCombat._dmgParticuliere(rollData); - dmg.dmgSurprise= RdDCombat._dmgSurprise(rollData); - dmg.dmgActor = RdDCombat._dmgActor(actor.getBonusDegat(), rollData.selectedCarac.label, dmg.dmgArme); - dmg.total = dmg.dmgSurprise + dmg.dmgTactique + dmg.dmgArme + dmg.dmgActor + dmg.dmgParticuliere; - return dmg; - } - - static _dmgArme(rollData) { - return parseInt(rollData.arme.data.dommages); - } - - static _dmgActor(bonusDegat, categorie, dmgArme) { - switch (categorie) { - case "Tir": return 0; - case "Lancer": return Math.max(0, Math.min(dmgArme, bonusDegat)); - } - return bonusDegat; - } - - static _dmgTactique(rollData) { - return rollData.isCharge ? 2 : 0; - } - - static _dmgParticuliere(rollData) { - return rollData.particuliereAttaque == 'force' ? 5 : 0; - } - - - static _dmgSurprise(rollData) { - if (rollData.surprise) { - switch (rollData.surprise) { - case 'demi': return 1; - case 'totale': return 10; - } - } - return 0; - } - - /* -------------------------------------------- */ - static descriptionSurprise(surprise) { - if (surprise) { - switch (surprise) { - case 'demi': return 'demi-surprise'; - case 'totale': return 'surprise totale'; - } - } - return ''; - } - - } \ No newline at end of file diff --git a/module/rdd-resolution-table.js b/module/rdd-resolution-table.js index a2d3acf5..06cbe388 100644 --- a/module/rdd-resolution-table.js +++ b/module/rdd-resolution-table.js @@ -242,10 +242,9 @@ export class RdDResolutionTable { static buildHTMLResults( caracValue, levelValue) { let cell = this.computeChances( caracValue, levelValue); let descr = $(''); - descr.append("Réussite : "+cell.score+ " - Particulière : " + cell.part + " - Significative : " + cell.sign); + descr.append("Particulière : " + cell.part+ " - Significative : " + cell.sign + " - Réussite : "+cell.score); descr.append("
Echec Particulier : " + cell.epart + " - Echec Total : " + cell.etotal ); descr.append("
"); - return descr; } diff --git a/module/rdd-roll-encaisser.js b/module/rdd-roll-encaisser.js index 2464b388..3b7d8ddb 100644 --- a/module/rdd-roll-encaisser.js +++ b/module/rdd-roll-encaisser.js @@ -37,8 +37,8 @@ export class RdDEncaisser extends Dialog { dmg:{ total: Number(this.modifier), loc: { result: 0, label: "Corps" }, - }, - mortalite: mortalite + mortalite: mortalite + } }); } diff --git a/module/rdd-roll.js b/module/rdd-roll.js index 526a3d70..48d5993f 100644 --- a/module/rdd-roll.js +++ b/module/rdd-roll.js @@ -1,16 +1,17 @@ import { HtmlUtility } from "./html-utility.js"; import { RdDItemSort } from "./item-sort.js"; import { Misc } from "./misc.js"; +import { RdDBonus } from "./rdd-bonus.js"; import { RdDResolutionTable } from "./rdd-resolution-table.js"; /** * Extend the base Dialog entity to select roll parameters * @extends {Dialog} */ -/* -------------------------------------------- */ +/* -------------------------------------------- */ export class RdDRoll extends Dialog { - /* -------------------------------------------- */ + /* -------------------------------------------- */ static async create(actor, rollData, dialogConfig, ...actions) { RdDRoll._ensureCorrectActions(actions); @@ -25,7 +26,7 @@ export class RdDRoll extends Dialog { return new RdDRoll(actor, rollData, html, options, actions); } - /* -------------------------------------------- */ + /* -------------------------------------------- */ static _setDefaultOptions(actor, rollData) { let defaultRollData = { @@ -38,7 +39,7 @@ export class RdDRoll extends Dialog { diffLibre: 0, editLibre: true, editConditions: true, - forceValue : actor.getForceValue(), + forceValue: actor.getForceValue(), malusArmureValue: (actor.type == 'personnage ' && actor.data.data.attributs && actor.data.data.attributs.malusarmure) ? actor.data.data.attributs.malusarmure.value : 0, surencMalusFlag: actor.type == 'personnage ' ? (actor.data.data.compteurs.surenc.value < 0) : false, surencMalusValue: actor.type == 'personnage ' ? actor.data.data.compteurs.surenc.value : 0, @@ -48,10 +49,10 @@ export class RdDRoll extends Dialog { encValueForNatation: actor.encombrementTotal ? Math.floor(actor.encombrementTotal) : 0, ajustementAstrologique: actor.ajustementAstrologique() } - mergeObject(rollData, defaultRollData, { overwrite: false } ); + mergeObject(rollData, defaultRollData, { overwrite: false }); } - /* -------------------------------------------- */ + /* -------------------------------------------- */ static _ensureCorrectActions(actions) { if (actions.length == 0) { throw 'No action defined'; @@ -63,7 +64,7 @@ export class RdDRoll extends Dialog { }); } - /* -------------------------------------------- */ + /* -------------------------------------------- */ constructor(actor, rollData, html, options, actions) { let conf = { title: actions[0].label, @@ -81,7 +82,7 @@ export class RdDRoll extends Dialog { this.rollData = rollData; } - /* -------------------------------------------- */ + /* -------------------------------------------- */ async onAction(action, html) { await RdDResolutionTable.rollData(this.rollData); console.log("RdDRoll -=>", this.rollData, this.rollData.rolled); @@ -101,19 +102,26 @@ export class RdDRoll extends Dialog { this.bringToTop(); var rollData = this.rollData; + var actor = this.actor; function updateRollResult(rollData) { let caracValue = parseInt(rollData.selectedCarac.value) let rollLevel = RdDRoll._computeFinalLevel(rollData); - + rollData.dmg = rollData.attackerRoll ? rollData.attackerRoll.dmg : RdDBonus.dmg(rollData, actor.getBonusDegat()); rollData.finalLevel = rollLevel; - rollData.caracValue = caracValue + rollData.caracValue = caracValue; + rollData.diffConditions = RdDBonus.bonusAttaque(rollData.surpriseDefenseur); + rollData.coupsNonMortels = (rollData.attackerRoll ? rollData.attackerRoll.dmg.mortalite : rollData.dmg.mortalite) == 'non-mortel'; + let dmgText = Misc.toSignedString(rollData.dmg.total); + if (rollData.coupsNonMortels) { + dmgText = '(' + dmgText + ')'; + } HtmlUtility._showControlWhen(".etat-general", !RdDRoll._isIgnoreEtatGeneral(rollData)); // Sort management if (rollData.selectedSort) { - rollData.bonus = RdDItemSort.getCaseBonus( rollData.selectedSort, rollData.coord ), + rollData.bonus = RdDItemSort.getCaseBonus(rollData.selectedSort, rollData.coord), //console.log("Toggle show/hide", rollData.selectedSort); HtmlUtility._showControlWhen("#div-sort-difficulte", RdDItemSort.isDifficulteVariable(rollData.selectedSort)) HtmlUtility._showControlWhen("#div-sort-ptreve", RdDItemSort.isCoutVariable(rollData.selectedSort)) @@ -122,6 +130,9 @@ export class RdDRoll extends Dialog { // Mise à jour valeurs $("#roll-param").text(rollData.selectedCarac.value + " / " + Misc.toSignedString(rollData.finalLevel)); $("#compdialogTitle").text(RdDRoll._getTitle(rollData)); + $('#coupsNonMortels').prop('checked', rollData.coupsNonMortels); + $("#dmg-arme-actor").text(dmgText); + $("#defenseur-surprise").text(RdDBonus.description(rollData.surpriseDefenseur)); $(".table-resolution").remove(); $("#resolutionTable").append(RdDResolutionTable.buildHTMLTableExtract(caracValue, rollLevel)); $(".span-valeur").remove(); @@ -168,7 +179,7 @@ export class RdDRoll extends Dialog { html.find('#sort').change((event) => { let sortKey = Misc.toInt(event.currentTarget.value); this.rollData.selectedSort = rollData.sortList[sortKey]; // Update the selectedCarac - this.rollData.bonus = RdDItemSort.getCaseBonus( rollData.selectedSort, rollData.coord ); + this.rollData.bonus = RdDItemSort.getCaseBonus(rollData.selectedSort, rollData.coord); RdDItemSort.setCoutReveReel(rollData.selectedSort); //console.log("RdDRollSelectDialog - Sort selection", rollData.selectedSort); updateRollResult(rollData); @@ -186,10 +197,12 @@ export class RdDRoll extends Dialog { updateRollResult(rollData); }); html.find('#coupsNonMortels').change((event) => { - this.rollData.mortalite = event.currentTarget.checked ? "non-mortel" : "mortel"; + this.rollData.dmg.mortalite = event.currentTarget.checked ? "non-mortel" : "mortel"; + updateRollResult(rollData); }); - html.find('#isCharge').change((event) => { - this.rollData.isCharge = event.currentTarget.checked; + html.find('#tactique-combat').change((event) => { + this.rollData.tactique = event.currentTarget.value; + updateRollResult(rollData); }); html.find('#surencMalusApply').change((event) => { this.rollData.surencMalusApply = event.currentTarget.checked; @@ -209,11 +222,38 @@ export class RdDRoll extends Dialog { static _computeFinalLevel(rollData) { const etat = RdDRoll._isIgnoreEtatGeneral(rollData) ? 0 : Misc.toInt(rollData.etat); const diffConditions = Misc.toInt(rollData.diffConditions); - let malusEnc = (rollData.surencMalusApply) ? rollData.surencMalusValue : 0; - let diffLibre = Misc.toInt(rollData.diffLibre); - let malusEncNatation = (rollData.useEncForNatation) ? -rollData.encValueForNatation : 0; - let ajustementChance = rollData.selectedCarac.label.toLowerCase().includes('chance') ? rollData.ajustementAstrologique : 0; + const malusEnc = (rollData.surencMalusApply) ? rollData.surencMalusValue : 0; + const bonusTactique = RdDBonus.bonusAttaque(rollData.tactique); + const malusEncNatation = (rollData.useEncForNatation) ? -rollData.encValueForNatation : 0; + const ajustementChance = rollData.selectedCarac.label.toLowerCase().includes('chance') ? rollData.ajustementAstrologique : 0; // Gestion malus armure + const malusArmureValue = RdDRoll._computeMalusArmure(rollData); + + const diffLibre = RdDRoll._computeDiffLibre(rollData); + const diffCompetence = RdDRoll._computeDiffCompetence(rollData); + + return etat + diffCompetence + diffLibre + diffConditions + malusEnc + malusEncNatation + malusArmureValue + ajustementChance + bonusTactique; + } + + static _computeDiffCompetence(rollData) { + if (rollData.competence) { + return Misc.toInt(rollData.competence.data.niveau); + } + if (rollData.draconicList) { + return Misc.toInt(rollData.selectedDraconic.data.niveau); + } + return 0; + } + + static _computeDiffLibre(rollData) { + let diffLibre = Misc.toInt(rollData.diffLibre); + if (rollData.draconicList && rollData.selectedSort) { + return RdDItemSort.getDifficulte(rollData.selectedSort, diffLibre); + } + return diffLibre; + } + + static _computeMalusArmure(rollData) { let malusArmureValue = 0; if (rollData.malusArmureValue != 0 && (rollData.selectedCarac.label == "Agilité" || rollData.selectedCarac.label == "Dérobée")) { $("#addon-message").text("Malus armure appliqué : " + rollData.malusArmureValue); @@ -221,17 +261,7 @@ export class RdDRoll extends Dialog { } else { $("#addon-message").text(""); } - - let diffCompetence = 0; - if (rollData.competence) { - diffCompetence = Misc.toInt(rollData.competence.data.niveau); - } - else if (rollData.draconicList) { - diffCompetence = Misc.toInt(rollData.selectedDraconic.data.niveau); - diffLibre = RdDItemSort.getDifficulte(rollData.selectedSort, diffLibre); - } - - return etat + diffCompetence + diffLibre + diffConditions + malusEnc + malusEncNatation + malusArmureValue + ajustementChance; + return malusArmureValue; } /* -------------------------------------------- */ diff --git a/module/rdd-tmr-dialog.js b/module/rdd-tmr-dialog.js index 83cdd43d..815f4acf 100644 --- a/module/rdd-tmr-dialog.js +++ b/module/rdd-tmr-dialog.js @@ -285,14 +285,6 @@ export class RdDTMRDialog extends Dialog { return rencontre; } - /* -------------------------------------------- */ - performRoll(html) { - if (this.viewOnly) { - return; - } - this.actor.performRoll(this.rollData); - } - /* -------------------------------------------- */ updateValuesDisplay() { let ptsreve = document.getElementById("tmr-pointsreve-value"); diff --git a/templates/dialog-competence.html b/templates/dialog-competence.html index 67a5e024..6cfb1a2d 100644 --- a/templates/dialog-competence.html +++ b/templates/dialog-competence.html @@ -9,8 +9,13 @@ {{/each}} {{/select}} +
+ {{#if attackerRoll}} + + + {{else}} + {{/if}} -
{{#if arme}} -
- - - - -
-{{#unless attackerRoll}} -
- - -
-{{/unless}} +
+ {{#if attackerRoll}} + {{#if attackerRoll.tactique}} + + {{/if}} + + + {{else}} + + + + + + {{/if}} + {{#if surpriseDefenseur}} + + {{/if}} +
{{/if}} +
+ +     +
+ {{>"systems/foundryvtt-reve-de-dragon/templates/dialog-roll-surenc.html"}} {{>"systems/foundryvtt-reve-de-dragon/templates/dialog-roll-natation.html"}} -
- -
-
-     -