From ce6680d5e8b14ac5b5fa8fdf22763d0d24068965 Mon Sep 17 00:00:00 2001 From: Vincent Vandemeulebrouck Date: Wed, 16 Dec 2020 00:42:52 +0100 Subject: [PATCH 1/3] Filtrage des armes de tir&lancer Pas disponibles pour une parade --- module/rdd-combat.js | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/module/rdd-combat.js b/module/rdd-combat.js index 15853905..bb9d9908 100644 --- a/module/rdd-combat.js +++ b/module/rdd-combat.js @@ -203,8 +203,7 @@ export class RdDCombat { let content = "" + this.defender.name + " doit se défendre :"; // parades - let filterArmesParade = this._getFilterArmesParade(rollData.competence.data.categorie); - for (const arme of this.defender.data.items.filter(filterArmesParade)) { + for (const arme of this._filterArmesParade(this.defender.data.items, rollData.competence.data.categorie)) { content += "
Parer avec " + arme.name + ""; } @@ -240,13 +239,22 @@ export class RdDCombat { } /* -------------------------------------------- */ - _getFilterArmesParade(categorie) { + _filterArmesParade(items, categorie) { switch (categorie) { case 'tir': case 'lancer': - return arme => arme.type == "arme" && arme.data.competence.toLowerCase().match("bouclier"); + return items.filter(item => item.data.competence.toLowerCase().match("bouclier")); default: - return arme => (arme.type == "arme" && RdDItemCompetence.isCompetenceMelee(arme.data.competence)) || (arme.type == "competencecreature" && arme.data.isparade) + return items.filter(item => { + if (item.type == 'competencecreature') { + return item.data.isparade; + } + if (item.type == 'arme') { + const comp = this.defender.getCompetence(item.data.competence); + return comp && comp.data.categorie == 'melee'; + } + return false; + }); } } /* -------------------------------------------- */ From c8a2d69e0fd9b950fe5652a32fceeed73b22a181 Mon Sep 17 00:00:00 2001 From: Vincent Vandemeulebrouck Date: Wed, 16 Dec 2020 02:54:28 +0100 Subject: [PATCH 2/3] Gestion des jets de moral #40 --- icons/moral-heureux.svg | 56 +++++++++++++++++++++++++++++++++++++ icons/moral-malheureux.svg | 57 ++++++++++++++++++++++++++++++++++++++ icons/moral-neutre.svg | 57 ++++++++++++++++++++++++++++++++++++++ module/actor-sheet.js | 12 ++++++++ module/actor.js | 43 ++++++++++++++++++++++++++-- templates/actor-sheet.html | 14 ++++++---- 6 files changed, 232 insertions(+), 7 deletions(-) create mode 100644 icons/moral-heureux.svg create mode 100644 icons/moral-malheureux.svg create mode 100644 icons/moral-neutre.svg diff --git a/icons/moral-heureux.svg b/icons/moral-heureux.svg new file mode 100644 index 00000000..60be4967 --- /dev/null +++ b/icons/moral-heureux.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/icons/moral-malheureux.svg b/icons/moral-malheureux.svg new file mode 100644 index 00000000..d4b32e93 --- /dev/null +++ b/icons/moral-malheureux.svg @@ -0,0 +1,57 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/icons/moral-neutre.svg b/icons/moral-neutre.svg new file mode 100644 index 00000000..e9361418 --- /dev/null +++ b/icons/moral-neutre.svg @@ -0,0 +1,57 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/module/actor-sheet.js b/module/actor-sheet.js index 5792026f..d0f05218 100644 --- a/module/actor-sheet.js +++ b/module/actor-sheet.js @@ -450,6 +450,18 @@ export class RdDActorSheet extends ActorSheet { this.actor.stressTest(); this.render(true); }); + html.find('#moral-malheureux').click((event) => { + this.actor.jetDeMoral('malheureuse'); + this.render(true); + }); + html.find('#moral-neutre').click((event) => { + this.actor.jetDeMoral('neutre'); + this.render(true); + }); + html.find('#moral-heureux').click((event) => { + this.actor.jetDeMoral('heureuse'); + this.render(true); + }); html.find('#ethylisme-test').click((event) => { this.actor.ethylismeTest(); this.render(true); diff --git a/module/actor.js b/module/actor.js index d2375ce2..1dcc6314 100644 --- a/module/actor.js +++ b/module/actor.js @@ -1189,6 +1189,45 @@ export class RdDActor extends Actor { this.update( { "data.blessures": blessures } ); } + async jetDeMoral(situation) { + let jetMoral = new Roll("1d20").roll(); + RdDDice.show(jetMoral); + let compteurs = duplicate(this.data.data.compteurs); + compteurs.moral.value = Misc.toInt(compteurs.moral.value); + const succes = jetMoral.total <= 10 + compteurs.moral.value; + let ajustementMoral = this._calculAjustementMoral(succes, compteurs.moral, situation); + if (ajustementMoral != 0) { + compteurs.moral.value += ajustementMoral; + if (compteurs.moral.value>3) { + // exaltation + compteurs.moral.value --; + compteurs.exaltation.value = Misc.toInt(compteurs.exaltation.value) + 1; + } + if (compteurs.moral.value<-3) { + // dissolution + compteurs.moral.value ++; + compteurs.dissolution.value = Misc.toInt(compteurs.dissolution.value) + 1; + } + await this.update( { 'data.compteurs': compteurs} ); + } + ChatMessage.create({ + whisper: ChatMessage.getWhisperRecipients(game.user.name), + content: "Jet de moral ("+ jetMoral.total + ")" + (succes? "réussi": "manqué") + " en situation "+situation+", vous "+(ajustementMoral>0? "gagnez du moral":ajustementMoral<0? "perdez du moral": "gardez votre moral") + }); + } + + _calculAjustementMoral(succes, moral, situation) + { + switch (situation) { + case 'heureuse': return succes ? 1 : 0; + case 'malheureuse':return succes ? 0 : -1; + case 'neutre': + if (succes && moral <= 0) return 1; + if (!succes && moral > 0) return -1; + } + return 0; + + } /* -------------------------------------------- */ async ethylismeTest() { let rollData = { @@ -1648,7 +1687,7 @@ export class RdDActor extends Actor { // Cas de désir lancinant, pas d'expérience sur particulière if ( this.checkDesirLancinant() ) { ChatMessage.create( { content: `Vous souffrez au moins d'un Désir Lancinant, vous ne pouvez pas gagner d'expérience sur une Particulière tant que le désir n'est pas assouvi`, - whisper: ChatMessage.getWhisperRecipients(game.user.name) } ).create(); + whisper: ChatMessage.getWhisperRecipients(game.user.name) } ); return { result:false, xpcarac:0, xpCompetence: 0 }; } @@ -1672,7 +1711,7 @@ export class RdDActor extends Actor { await this.update( {"data.carac": carac } ); } else { ChatMessage.create( { content: `Vous avez ${xpCarac} à répartir pour la caractérisque dérivée ${caracName}. Vous devez le faire manuellement.`, - whisper: ChatMessage.getWhisperRecipients(game.user.name) } ).create(); + whisper: ChatMessage.getWhisperRecipients(game.user.name) } ); } return { result:true, xpcarac:xpCarac, xpCompetence: xpComp }; //XP } diff --git a/templates/actor-sheet.html b/templates/actor-sheet.html index 61c7b8f8..e868b0e2 100644 --- a/templates/actor-sheet.html +++ b/templates/actor-sheet.html @@ -133,7 +133,7 @@
  • Chance actuelle - Utiliser + Utiliser
  • {{#each data.compteurs as |compteur key|}} {{#if compteur.isChance}} @@ -141,15 +141,19 @@
  • {{compteur.label}} - {{#if compteur.isStress}} + + {{#if compteur.isStress}} Transformer - {{else}} - {{#if (eq compteur.label 'Ethylisme')}} + {{else if (eq compteur.label 'Ethylisme')}} Jet d'Ethylisme + {{else if (eq compteur.label 'Moral')}} + Jet de moral situation malheureuse + Jet de moral situation neutre + Jet de moral situation heureuse {{else}} - {{/if}} {{/if}} +
  • {{/if}} {{/each}} From f3453a46e7439870ed26c73004fdc96d00dcf65f Mon Sep 17 00:00:00 2001 From: Vincent Vandemeulebrouck Date: Wed, 16 Dec 2020 23:02:15 +0100 Subject: [PATCH 3/3] 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"}} -
    - -
    -
    -     -