From 7ed9a4a12bab25d695fa23876d016a6e7be9b661 Mon Sep 17 00:00:00 2001
From: Vincent Vandemeulebrouck <vincent.vandeme@gmail.com>
Date: Sun, 26 Jan 2025 21:29:01 +0100
Subject: [PATCH] Preparation callbacks pour nouveaux Rolls

---
 module/actor.js                 | 106 ++++++++++++++++++++++++--------
 module/actor/base-actor-reve.js |  59 ++++++++----------
 module/rdd-combat.js            |  48 ++++++++++-----
 module/rdd-commands.js          |   3 +-
 module/rdd-empoignade.js        |  18 +++---
 module/rdd-possession.js        |   7 +--
 module/rdd-resolution-table.js  |  15 -----
 module/rdd-roll-result.js       |  17 +++++
 module/rdd-roll.js              |  17 +++--
 9 files changed, 178 insertions(+), 112 deletions(-)
 create mode 100644 module/rdd-roll-result.js

diff --git a/module/actor.js b/module/actor.js
index 4a07167e..ccd4c690 100644
--- a/module/actor.js
+++ b/module/actor.js
@@ -42,6 +42,7 @@ import { RdDItemTete } from "./item/tete.js";
 import { DialogSelect } from "./dialog-select.js";
 import { PAS_DE_DRACONIC, POSSESSION_SANS_DRACONIC } from "./item/base-items.js";
 import { RdDItemRace } from "./item/race.js";
+import { RdDRollResult } from "./rdd-roll-result.js";
 
 export const MAINS_DIRECTRICES = ['Droitier', 'Gaucher', 'Ambidextre']
 
@@ -1390,7 +1391,7 @@ export class RdDActor extends RdDBaseActorSang {
     ethylismeData.doses = ethylisme.nb_doses;
 
     await this.update({ 'system.compteurs.ethylisme': ethylisme });
-    await RdDResolutionTable.displayRollData(ethylismeData, this, 'chat-resultat-ethylisme.html');
+    await RdDRollResult.displayRollData(ethylismeData, this, 'chat-resultat-ethylisme.html');
   }
 
   /* -------------------------------------------- */
@@ -1479,7 +1480,6 @@ export class RdDActor extends RdDBaseActorSang {
   /* -------------------------------------------- */
   createCallbackExperience() {
     return {
-      condition: r => r.rolled.isPart && r.finalLevel < 0 && game.settings.get("core", "rollMode") != 'selfroll',
       action: r => this.appliquerAjoutExperience(r)
     };
   }
@@ -1487,7 +1487,6 @@ export class RdDActor extends RdDBaseActorSang {
   /* -------------------------------------------- */
   createCallbackAppelAuMoral() { /* Si l'appel au moral est utilisé, on l'affiche dans le chat et on diminue éventuellement le moral */
     return {
-      condition: r => r.use.appelAuMoral && game.settings.get("core", "rollMode") != 'selfroll',
       action: r => this._appliquerAppelMoral(r)
     };
   }
@@ -1558,7 +1557,10 @@ export class RdDActor extends RdDBaseActorSang {
 
   /* -------------------------------------------- */
   async appliquerAjoutExperience(rollData, hideChatMessage = 'show') {
-    if (!Misc.hasConnectedGM()) {
+    if (!rollData.rolled.isPart ||
+      rollData.finalLevel >= 0 ||
+      game.settings.get("core", "rollMode") == 'selfroll' ||
+	  !Misc.hasConnectedGM()) {
       return
     }
     hideChatMessage = hideChatMessage == 'hide' || (Misc.isRollModeHiddenToPlayer() && !game.user.isGM)
@@ -1582,7 +1584,9 @@ export class RdDActor extends RdDBaseActorSang {
 
   /* -------------------------------------------- */
   async _appliquerAppelMoral(rollData) {
-    if (!rollData.use.moral) return;
+    if (!rollData.use.moral || game.settings.get("core", "rollMode") == 'selfroll'){
+      return
+    }
     if (rollData.rolled.isEchec ||
       (rollData.ajustements.diviseurSignificative && (rollData.rolled.roll * rollData.ajustements.diviseurSignificative > rollData.score))) {
       rollData.perteMoralEchec = rollData.moral <= -3 ? 'dissolution' : 'perte';
@@ -1657,10 +1661,7 @@ export class RdDActor extends RdDBaseActorSang {
         diffLibre: RdDItemSort.getDifficulte(sorts[0], -7), // Per default at startup
         coutreve: Array(30).fill().map((item, index) => 1 + index),
       },
-      callbackAction: async r => {
-        await this._rollUnSortResult(r);
-        if (!r.isSortReserve) this.tmrApp?.close();
-      }
+      callbacks: [{ action: r => this._rollUnSortResult(r) }]
     });
     this.tmrApp?.setTMRPendingAction(dialog);
   }
@@ -1764,12 +1765,44 @@ export class RdDActor extends RdDBaseActorSang {
     reveActuel = Math.max(reveActuel - rollData.depenseReve, 0);
     await this.update({ "system.reve.reve.value": reveActuel });
 
-    // Final chat message
-    await RdDResolutionTable.displayRollData(rollData, this, 'chat-resultat-sort.html');
+    await RdDRollResult.displayRollData(rollData, this, 'chat-resultat-sort.html');
 
     if (reveActuel == 0) { // 0 points de reve
       ChatMessage.create({ content: this.name + " est réduit à 0 Points de Rêve, et tombe endormi !" });
     }
+    if (!rollData.isSortReserve) {
+      this.tmrApp?.close();
+    }
+  }
+
+  /**
+   * Méthode pour faire un jet prédéterminer sans ouvrir la fenêtre de dialogue
+   * @param {*} caracName 
+   * @param {*} compName 
+   * @param {*} diff 
+   * @param {*} options 
+   * @returns 
+   */
+  async doRollCaracCompetence(caracName, compName, diff, options = { title: "" }) {
+    const carac = this.getCaracByName(caracName);
+    if (!carac) {
+      ui.notifications.warn(`${this.name} n'a pas de caractéristique correspondant à ${caracName}`)
+      return;
+    }
+    const competence = this.getCompetence(compName);
+    let rollData = {
+      alias: this.getAlias(),
+      caracValue: Number(carac.value),
+      selectedCarac: carac,
+      competence: competence,
+      diffLibre: diff,
+      show: { title: options?.title ?? '' }
+    };
+    RollDataAjustements.calcul(rollData, this);
+    await RdDResolutionTable.rollData(rollData);
+    this._gererExperience(rollData);
+    await RdDRollResult.displayRollData(rollData, this)
+    return rollData.rolled;
   }
 
   /* -------------------------------------------- */
@@ -1823,6 +1856,27 @@ export class RdDActor extends RdDBaseActorSang {
     return undefined;
   }
 
+  async rollCaracCompetence(caracName, compName, diff, options = { title: "" }) {
+    RdDEmpoignade.checkEmpoignadeEnCours(this)
+    const competence = this.getCompetence(compName);
+    await this.openRollDialog({
+      name: 'jet-competence',
+      label: 'Jet ' + Grammar.apostrophe('de', competence.name),
+      template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html',
+      rollData: {
+        alias: this.getAlias(),
+        carac: this.system.carac,
+        selectedCarac: this.getCaracByName(caracName),
+        selectedCaracName: caracName,
+        diffLibre: diff,
+        competence: competence,
+        show: { title: options?.title ?? '' }
+      },
+	  // TODO:
+      callbacks: [{ action: r => this.$onRollCompetence(r, options) }]
+    });
+  }
+
   /* -------------------------------------------- */
   async rollTache(id, options = {}) {
     RdDEmpoignade.checkEmpoignadeEnCours(this)
@@ -1831,7 +1885,7 @@ export class RdDActor extends RdDBaseActorSang {
     compData.system.defaut_carac = tacheData.system.carac; // Patch !
 
     await this.openRollDialog({
-      name: 'jet-competence',
+          name: 'jet-competence',
       label: 'Jet de Tâche ' + tacheData.name,
       template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html',
       rollData: {
@@ -1844,7 +1898,7 @@ export class RdDActor extends RdDBaseActorSang {
           [tacheData.system.carac]: foundry.utils.duplicate(this.system.carac[tacheData.system.carac])
         }
       },
-      callbackAction: async r => await this._tacheResult(r, options)
+      callbacks: [{ action: r => this._tacheResult(r, options) }]
     });
   }
 
@@ -1867,7 +1921,7 @@ export class RdDActor extends RdDBaseActorSang {
     await this.updateEmbeddedDocuments('Item', [rollData.tache]);
     await this.santeIncDec("fatigue", rollData.tache.system.fatigue);
 
-    await RdDResolutionTable.displayRollData(rollData, this, 'chat-resultat-tache.html');
+    await RdDRollResult.displayRollData(rollData, this, 'chat-resultat-tache.html');
     if (options?.onRollAutomate) {
       options.onRollAutomate(rollData);
     }
@@ -1894,12 +1948,12 @@ export class RdDActor extends RdDBaseActorSang {
     }
 
     await this.openRollDialog({
-      name: `jet-${artData.art}`,
+          name: `jet-${artData.art}`,
       label: `${artData.verbe} ${oeuvre.name}`,
-      template: `systems/foundryvtt-reve-de-dragon/templates/dialog-roll-${oeuvre.type}.html`,
+          template: `systems/foundryvtt-reve-de-dragon/templates/dialog-roll-${oeuvre.type}.html`,
       rollData: artData,
-      callbackAction: callbackAction
-    });
+      callbacks: [{ action: callbackAction }],
+    })
   }
 
   /* -------------------------------------------- */
@@ -1908,7 +1962,7 @@ export class RdDActor extends RdDBaseActorSang {
     const baseQualite = (artData.rolled.isSuccess ? niveau : artData.competence.system.niveau);
     artData.qualiteFinale = Math.min(baseQualite, niveau) + artData.rolled.ptQualite;
 
-    await RdDResolutionTable.displayRollData(artData, this.name, `chat-resultat-${artData.art}.html`);
+    await RdDRollResult.displayRollData(artData, this.name, `chat-resultat-${artData.art}.html`);
   }
 
   /* -------------------------------------------- */
@@ -1985,7 +2039,7 @@ export class RdDActor extends RdDBaseActorSang {
       ui.notifications.info(`${platCuisine.system.quantite} rations de ${platCuisine.name} ont été ajoutés à votre équipement`);
     }
     cuisine.platCuisine = platCuisine;
-    await RdDResolutionTable.displayRollData(cuisine, this.name, `chat-resultat-${cuisine.art}.html`);
+    await RdDRollResult.displayRollData(cuisine, this.name, `chat-resultat-${cuisine.art}.html`);
   }
 
   async preparerNourriture(item) {
@@ -2080,7 +2134,7 @@ export class RdDActor extends RdDBaseActorSang {
       await this.createEmbeddedDocuments("Item", [RdDItemSigneDraconique.prepareSigneDraconiqueMeditation(meditationRoll.meditation, meditationRoll.rolled)]);
     }
 
-    await RdDResolutionTable.displayRollData(meditationRoll, this.name, 'chat-resultat-meditation.html');
+    await RdDRollResult.displayRollData(meditationRoll, this.name, 'chat-resultat-meditation.html');
   }
 
   /* -------------------------------------------- */
@@ -2167,7 +2221,7 @@ export class RdDActor extends RdDBaseActorSang {
       await ExperienceLog.add(this, XP_TOPIC.XPSORT, fromXp, toXp, `${rollData.competence.name} : signe draconique`);
     }
     await this.deleteEmbeddedDocuments("Item", [rollData.signe._id]);
-    await RdDResolutionTable.displayRollData(rollData, this.name, 'chat-resultat-lecture-signedraconique.html');
+    await RdDRollResult.displayRollData(rollData, this.name, 'chat-resultat-lecture-signedraconique.html');
     this.tmrApp.close();
   }
 
@@ -2178,13 +2232,13 @@ export class RdDActor extends RdDBaseActorSang {
       label: 'Appel à la chance',
       template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-carac.html',
       rollData: { selectedCarac: this.getCaracByName('chance-actuelle'), surprise: '' },
-      callbackAction: async r => await this._appelChanceResult(r, onSuccess, onEchec)
+      callbacks: [{ action: r => this.$appelChanceResult(r, onSuccess, onEchec) }]
     });
   }
 
   /* -------------------------------------------- */
-  async _appelChanceResult(rollData, onSuccess, onEchec) {
-    await RdDResolutionTable.displayRollData(rollData, this, 'chat-resultat-appelchance.html')
+  async $appelChanceResult(rollData, onSuccess, onEchec) {
+    await RdDRollResult.displayRollData(rollData, this, 'chat-resultat-appelchance.html')
     if (rollData.rolled.isSuccess) {
       await this.setFlag(SYSTEM_RDD, 'utilisationChance', true);
       await this.chanceActuelleIncDec(-1);
@@ -2658,7 +2712,7 @@ export class RdDActor extends RdDBaseActorSang {
 
   /* -------------------------------------------- */
   async _alchimieResult(rollData) {
-    await RdDResolutionTable.displayRollData(rollData, this, 'chat-resultat-alchimie.html');
+    await RdDRollResult.displayRollData(rollData, this, 'chat-resultat-alchimie.html');
   }
 
   /* -------------------------------------------- */
diff --git a/module/actor/base-actor-reve.js b/module/actor/base-actor-reve.js
index 0190658b..c6dcaa8f 100644
--- a/module/actor/base-actor-reve.js
+++ b/module/actor/base-actor-reve.js
@@ -15,6 +15,7 @@ import { StatusEffects } from "../settings/status-effects.js";
 import { Targets } from "../targets.js";
 import { RdDConfirm } from "../rdd-confirm.js";
 import { RdDCarac } from "../rdd-carac.js";
+import { RdDRollResult } from "../rdd-roll-result.js";
 
 import { ChatUtility } from "../chat-utility.js";
 import { DialogValidationEncaissement } from "../dialog-validation-encaissement.js";
@@ -268,19 +269,11 @@ export class RdDBaseActorReve extends RdDBaseActor {
   }
 
   /* -------------------------------------------- */
-  async openRollDialog({ name, label, template, rollData, callbackAction }) {
+  async openRollDialog({ name, label, template, rollData, callbacks}) {
     const dialog = await RdDRoll.create(this, rollData,
       { html: template, close: async html => await this._onCloseRollDialog(html) },
-      {
-        name: name,
-        label: label,
-        callbacks: [
-          this.createCallbackExperience(),
-          this.createCallbackAppelAuMoral(),
-          { action: callbackAction }
-        ]
-      });
-    dialog.render(true);
+      { name: name, label: label, callbacks: [this.createCallbackExperience(), this.createCallbackAppelAuMoral()].concat(callbacks) })
+    dialog.render(true)
     return dialog
   }
 
@@ -299,7 +292,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
     const competence = this.getCompetence(compName);
     await this.openRollDialog({
       name: 'jet-competence',
-      label: competence? 'Jet ' + Grammar.apostrophe('de', competence.name) : `Jet sans compétence (${compName})`,
+      label: competence ? 'Jet ' + Grammar.apostrophe('de', competence.name) : `Jet sans compétence (${compName})`,
       template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html',
       rollData: {
         alias: this.getAlias(),
@@ -310,7 +303,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
         competence: competence,
         show: { title: options?.title ?? '' }
       },
-      callbackAction: async r => await this.$onRollCompetence(r, options)
+      callbacks:[async r => this.$onRollCompetence(r, options)]
     });
   }
   /**
@@ -353,16 +346,14 @@ export class RdDBaseActorReve extends RdDBaseActor {
     const selectedCaracName = ['apparence', 'perception', 'force', 'reve'].find(it => carac[it] != undefined)
 
     await this.openRollDialog({
-      name: `jet-${this.id}`,
-      label: `Jet de ${this.getAlias()}`,
-      template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll.html',
       rollData: {
+        alias: this.getAlias(),
         carac: carac,
         selectedCarac: carac[selectedCaracName],
         selectedCaracName: selectedCaracName,
         competences: this.itemTypes['competence']
       },
-      callbackAction: async r => await this.$onRollCaracResult(r)
+      callbacks: [{ action: r => this.$onRollCaracResult(r) }]
     })
   }
   /* -------------------------------------------- */
@@ -373,32 +364,38 @@ export class RdDBaseActorReve extends RdDBaseActor {
     foundry.utils.mergeObject(options, { resistance: false, diff: 0 }, { overwrite: false })
     RdDEmpoignade.checkEmpoignadeEnCours(this)
     let selectedCarac = this.getCaracByName(caracName)
-    console.log("selectedCarac", selectedCarac)
+    const title = 'Jet ' + Grammar.apostrophe('de', selectedCarac.label);
+    const jetResistance = options.resistance ? caracName : undefined;
     await this.openRollDialog({
       name: 'jet-' + caracName,
-      label: 'Jet ' + Grammar.apostrophe('de', selectedCarac.label),
+      label: title,
       template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-carac.html',
       rollData: {
+        alias: this.getAlias(),
         selectedCarac: selectedCarac,
         competences: this.itemTypes['competence'],
         diffLibre: options.diff ?? 0,
-        jetResistance: options.resistance ? caracName : undefined
+        jetResistance: jetResistance
       },
-      callbackAction: async r => await this.$onRollCaracResult(r)
+      callbacks: [{ action: r => this.$onRollCaracResult(r) }]
     });
   }
 
   /* -------------------------------------------- */
   async $onRollCaracResult(rollData) {
     // Final chat message
-    await RdDResolutionTable.displayRollData(rollData, this, 'chat-resultat-general.html');
+    await RdDRollResult.displayRollData(rollData, this, 'chat-resultat-general.html');
   }
 
   /* -------------------------------------------- */
   async rollCompetence(idOrName, options = { tryTarget: true, arme: undefined }) {
     RdDEmpoignade.checkEmpoignadeEnCours(this)
     const competence = this.getCompetence(idOrName);
-    let rollData = { carac: this.system.carac, competence: competence, arme: options.arme }
+    let rollData = {
+      carac: this.system.carac,
+      competence: competence,
+      arme: options.arme
+    }
     if (competence.type == ITEM_TYPES.competencecreature) {
       const token = RdDUtility.getSelectedToken(this)
       const arme = RdDItemCompetenceCreature.armeCreature(competence)
@@ -414,20 +411,20 @@ export class RdDBaseActorReve extends RdDBaseActor {
         return;
       }
       // Transformer la competence de créature
-      RdDItemCompetenceCreature.setRollDataCreature(rollData)
+      RdDItemCompetenceCreature.setRollDataCreature(this, rollData, competence)
     }
-
+    const dialogLabel = 'Jet ' + Grammar.apostrophe('de', competence.name);
     await this.openRollDialog({
       name: 'jet-competence',
-      label: 'Jet ' + Grammar.apostrophe('de', competence.name),
+      label: dialogLabel,
       template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html',
       rollData: rollData,
-      callbackAction: async r => await this.$onRollCompetence(r, options)
+      callbacks: [{ action: r => this.$onRollCompetence(r, options) }]
     });
   }
 
   async $onRollCompetence(rollData, options) {
-    await RdDResolutionTable.displayRollData(rollData, this, 'chat-resultat-competence.html')
+    await RdDRollResult.displayRollData(rollData, this, 'chat-resultat-competence.html')
     if (options?.onRollAutomate) {
       options.onRollAutomate(rollData);
     }
@@ -562,10 +559,8 @@ export class RdDBaseActorReve extends RdDBaseActor {
       await entite.setEntiteReveAccordee(this);
     }
 
-    await RdDResolutionTable.displayRollData(rollData, this, 'chat-resultat-accorder-cauchemar.html');
-    if (rolled.isPart) {
-      await this.appliquerAjoutExperience(rollData, true);
-    }
+    await RdDRollResult.displayRollData(rollData, this, 'chat-resultat-accorder-cauchemar.html');
+    await this.appliquerAjoutExperience(rollData, true);
     return rolled.isSuccess;
   }
 
diff --git a/module/rdd-combat.js b/module/rdd-combat.js
index f0301131..9b3e5a7c 100644
--- a/module/rdd-combat.js
+++ b/module/rdd-combat.js
@@ -13,6 +13,7 @@ import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
 import { STATUSES } from "./settings/status-effects.js";
 import { Targets } from "./targets.js";
 import { RdDEmpoignade } from "./rdd-empoignade.js";
+import { RdDRollResult } from "./rdd-roll-result.js";
 
 /* -------------------------------------------- */
 const premierRoundInit = [
@@ -361,7 +362,7 @@ export class RdDCombatManager extends Combat {
   }
 
   /* -------------------------------------------- */
-  static listActionsActorCombatant( actor) {
+  static listActionsActorCombatant(actor) {
     const possessions = actor.listActionsPossessions()
     const actions = possessions.length > 0
       ? possessions
@@ -742,16 +743,23 @@ export class RdDCombat {
           this.attacker.createCallbackExperience(),
           this.attacker.createCallbackAppelAuMoral(),
           { action: r => this.removeChatMessageActionsPasseArme(r.passeArme) },
-          { condition: r => arme && !RdDCombat.isParticuliere(r), action: r => this.attacker.incDecItemUse(arme._id) },
-          { condition: r => (RdDCombat.isReussite(r) && !RdDCombat.isParticuliere(r)), action: r => this._onAttaqueNormale(r) },
-          { condition: RdDCombat.isParticuliere, action: r => this._onAttaqueParticuliere(r) },
-          { condition: RdDCombat.isEchec, action: r => this._onAttaqueEchec(r) },
-          { condition: RdDCombat.isEchecTotal, action: r => this._onAttaqueEchecTotal(r) },
+          { action: r => this._increaseItemUse(r, arme) },
+          { action: r => this._onAttaqueNormale(r) },
+          { action: r => this._onAttaqueParticuliere(r) },
+          { action: r => this._onAttaqueEchec(r) },
+          { action: r => this._onAttaqueEchecTotal(r) },
         ]
       });
     dialog.render(true);
   }
 
+  _increaseItemUse(rollData, arme) {
+    if (!arme || RdDCombat.isParticuliere(rollData)) {
+      return
+    }
+    this.attacker.incDecItemUse(arme._id)
+  }
+
   /* -------------------------------------------- */
   _prepareAttaque(competence, arme) {
     let rollData = {
@@ -768,7 +776,7 @@ export class RdDCombat {
 
     if (this.attacker.isCreatureEntite()) {
       RdDItemCompetenceCreature.setRollDataCreature(rollData);
-    }
+  }
     else if (arme) {
       // Usual competence
       rollData.arme = RdDItemArme.armeUneOuDeuxMains(arme, RdDItemCompetence.isArmeUneMain(competence));
@@ -784,7 +792,9 @@ export class RdDCombat {
 
   /* -------------------------------------------- */
   async _onAttaqueParticuliere(rollData) {
-
+    if (!RdDCombat.isParticuliere(rollData)) {
+      return
+    }
     const isMeleeDiffNegative = (rollData.competence.type == 'competencecreature' || rollData.selectedCarac.label == "Mêlée") && rollData.diffLibre < 0;
     // force toujours, sauf empoignade
     // finesse seulement en mélée, pour l'empoignade, ou si la difficulté libre est de -1 minimum
@@ -822,6 +832,9 @@ export class RdDCombat {
 
   /* -------------------------------------------- */
   async _onAttaqueNormale(attackerRoll) {
+    if (!RdDCombat.isReussite(attackerRoll) || RdDCombat.isParticuliere(attackerRoll)) {
+      return
+    }
     console.log("RdDCombat.onAttaqueNormale >>>", attackerRoll);
 
     attackerRoll.dmg = RdDBonus.dmg(attackerRoll, this.attacker, this.defender.isEntite());
@@ -830,7 +843,7 @@ export class RdDCombat {
       cible: this.defender?.getAlias() ?? 'la cible',
       isRecul: (attackerRoll.particuliere == 'force' || attackerRoll.tactique == 'charge')
     }
-    await RdDResolutionTable.displayRollData(attackerRoll, this.attacker, 'chat-resultat-attaque.html');
+    await RdDRollResult.displayRollData(attackerRoll, this.attacker, 'chat-resultat-attaque.html');
 
     if (!await this.attacker.accorder(this.defender, 'avant-defense')) {
       return;
@@ -938,6 +951,9 @@ export class RdDCombat {
 
   /* -------------------------------------------- */
   async _onAttaqueEchecTotal(attackerRoll) {
+    if (!RdDCombat.isEchecTotal(attackerRoll)) {
+      return
+    }
     const choixEchecTotal = await ChatMessage.create({
       whisper: ChatUtility.getOwners(this.attacker),
       content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-attaque-etotal.html', {
@@ -965,8 +981,12 @@ export class RdDCombat {
 
   /* -------------------------------------------- */
   async _onAttaqueEchec(rollData) {
+    if (!RdDCombat.isEchec(rollData)) {
+      return
+    }
     console.log("RdDCombat.onAttaqueEchec >>>", rollData);
-    await RdDResolutionTable.displayRollData(rollData, this.attacker, 'chat-resultat-attaque.html');
+    await RdDRollResult.displayRollData(rollData, this.attacker, 'chat-resultat-attaque.html');
+
   }
 
   /* -------------------------------------------- */
@@ -1054,7 +1074,7 @@ export class RdDCombat {
 
     await this.computeRecul(defenderRoll);
     await this.computeDeteriorationArme(defenderRoll);
-    await RdDResolutionTable.displayRollData(defenderRoll, this.defender, 'chat-resultat-parade.html');
+    await RdDRollResult.displayRollData(defenderRoll, this.defender, 'chat-resultat-parade.html');
     this.removeChatMessageActionsPasseArme(defenderRoll.passeArme);
   }
 
@@ -1062,7 +1082,7 @@ export class RdDCombat {
   async _onParadeEchec(defenderRoll) {
     console.log("RdDCombat._onParadeEchec >>>", defenderRoll);
 
-    await RdDResolutionTable.displayRollData(defenderRoll, this.defender, 'chat-resultat-parade.html');
+    await RdDRollResult.displayRollData(defenderRoll, this.defender, 'chat-resultat-parade.html');
 
     this.removeChatMessageActionsPasseArme(defenderRoll.passeArme);
     this._sendMessageDefense(defenderRoll.attackerRoll, defenderRoll, { defense: true });
@@ -1129,7 +1149,7 @@ export class RdDCombat {
   /* -------------------------------------------- */
   async _onEsquiveNormale(defenderRoll) {
     console.log("RdDCombat._onEsquiveNormal >>>", defenderRoll);
-    await RdDResolutionTable.displayRollData(defenderRoll, this.defender, 'chat-resultat-esquive.html');
+    await RdDRollResult.displayRollData(defenderRoll, this.defender, 'chat-resultat-esquive.html');
     this.removeChatMessageActionsPasseArme(defenderRoll.passeArme);
   }
 
@@ -1137,7 +1157,7 @@ export class RdDCombat {
   async _onEsquiveEchec(defenderRoll) {
     console.log("RdDCombat._onEsquiveEchec >>>", defenderRoll);
 
-    await RdDResolutionTable.displayRollData(defenderRoll, this.defender, 'chat-resultat-esquive.html');
+    await RdDRollResult.displayRollData(defenderRoll, this.defender, 'chat-resultat-esquive.html');
 
     this.removeChatMessageActionsPasseArme(defenderRoll.passeArme);
     this._sendMessageDefense(defenderRoll.attackerRoll, defenderRoll, { defense: true })
diff --git a/module/rdd-commands.js b/module/rdd-commands.js
index 8968afcb..57e85600 100644
--- a/module/rdd-commands.js
+++ b/module/rdd-commands.js
@@ -18,6 +18,7 @@ import { FenetreRechercheTirage } from "./tirage/fenetre-recherche-tirage.js";
 import { TMRUtility } from "./tmr-utility.js";
 import { DialogFatigueVoyage } from "./voyage/dialog-fatigue-voyage.js";
 import { ChatUtility } from "./chat-utility.js";
+import { RdDRollResult } from "./rdd-roll-result.js";
 
 const rddRollNumeric = /^(\d+)\s*([\+\-]?\d+)?\s*(s)?/;
 
@@ -349,7 +350,7 @@ export class RdDCommands {
       show: { title: "Table de résolution" }
     };
     await RdDResolutionTable.rollData(rollData);
-    return RdDCommands._chatAnswer(msg, await RdDResolutionTable.buildRollDataHtml(rollData));
+    return RdDCommands._chatAnswer(msg, await RdDRollResult.buildRollDataHtml(rollData));
   }
 
   /* -------------------------------------------- */
diff --git a/module/rdd-empoignade.js b/module/rdd-empoignade.js
index cd49d881..08a92ba6 100644
--- a/module/rdd-empoignade.js
+++ b/module/rdd-empoignade.js
@@ -1,12 +1,10 @@
 /* -------------------------------------------- */
-import { RdDResolutionTable } from "./rdd-resolution-table.js";
 import { RdDRoll } from "./rdd-roll.js";
 import { RdDItemCompetenceCreature } from "./item-competencecreature.js";
 import { ChatUtility } from "./chat-utility.js";
 import { STATUSES } from "./settings/status-effects.js";
 import { ITEM_TYPES } from "./constants.js";
-
-/* -------------------------------------------- */
+import { RdDRollResult } from "./rdd-roll-result.js";
 
 /* -------------------------------------------- */
 export class RdDEmpoignade {
@@ -192,7 +190,7 @@ export class RdDEmpoignade {
     }
     if (empoignade.system.pointsemp >= 2) {
       if (!empoignade.system.ausol) {
-        let msg = await RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-entrainer.html');
+        let msg = await RdDRollResult.displayRollData(rollData, attacker, 'chat-empoignade-entrainer.html');
         RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
       }
     } else {
@@ -250,7 +248,7 @@ export class RdDEmpoignade {
     if (rollData.rolled.isPart) {
       rollData.particuliere = "finesse";
     }
-    let msg = await RdDResolutionTable.displayRollData(rollData, defender, 'chat-empoignade-resultat.html');
+    let msg = await RdDRollResult.displayRollData(rollData, defender, 'chat-empoignade-resultat.html');
     RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
   }
 
@@ -312,9 +310,9 @@ export class RdDEmpoignade {
       RdDEmpoignade.$updateEtatEmpoignade(empoignade)
     }
 
-    await RdDResolutionTable.displayRollData(rollData, rollData.defender, 'chat-empoignade-resultat.html')
+    await RdDRollResult.displayRollData(rollData, rollData.defender, 'chat-empoignade-resultat.html')
     if (empoignade.system.pointsemp >= 2) {
-      let msg = await RdDResolutionTable.displayRollData(rollData, rollData.attacker, 'chat-empoignade-entrainer.html');
+      let msg = await RdDRollResult.displayRollData(rollData, rollData.attacker, 'chat-empoignade-entrainer.html');
       RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
     }
   }
@@ -358,7 +356,7 @@ export class RdDEmpoignade {
     await attacker.setEffect(STATUSES.StatusProne, true);
     await defender.setEffect(STATUSES.StatusProne, true);
 
-    let msg = await RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-entrainer-sol.html');
+    let msg = await RdDRollResult.displayRollData(rollData, attacker, 'chat-empoignade-entrainer-sol.html');
     RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
   }
 
@@ -374,7 +372,7 @@ export class RdDEmpoignade {
     await defender.setEffect(STATUSES.StatusProne, true);
     await this.$deleteEmpoignade(empoignade)
 
-    let msg = await RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-projeter-sol.html');
+    let msg = await RdDRollResult.displayRollData(rollData, attacker, 'chat-empoignade-projeter-sol.html');
     RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
   }
 
@@ -401,7 +399,7 @@ export class RdDEmpoignade {
     if (perteMode == "endquart") {
       await defender.santeIncDec("endurance", -(3 * Math.floor(endValue / 4)));
     }
-    let msg = await RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-perte-endurance.html');
+    let msg = await RdDRollResult.displayRollData(rollData, attacker, 'chat-empoignade-perte-endurance.html');
     RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
   }
 
diff --git a/module/rdd-possession.js b/module/rdd-possession.js
index 483a9603..7df772e7 100644
--- a/module/rdd-possession.js
+++ b/module/rdd-possession.js
@@ -1,9 +1,8 @@
-/* -------------------------------------------- */
-import { RdDResolutionTable } from "./rdd-resolution-table.js";
 import { RdDRoll } from "./rdd-roll.js";
 import { RdDItemCompetenceCreature } from "./item-competencecreature.js";
 import { Targets } from "./targets.js";
 import { ITEM_TYPES } from "./constants.js";
+import { RdDRollResult } from "./rdd-roll-result.js";
 
 /* -------------------------------------------- */
 /* On part du principe qu'une entité démarre tjs 
@@ -131,7 +130,7 @@ export class RdDPossession {
     }
     const possession = (rollData.isECNIDefender ? rollData.attacker : rollData.defender).getPossession(rollData.possession.system.possessionid)
     RdDPossession.storePossessionAttaque(possession, rollData)
-    await RdDResolutionTable.displayRollData(rollData, rollData.defender, 'chat-resultat-possession.html');
+    await RdDRollResult.displayRollData(rollData, rollData.defender, 'chat-resultat-possession.html');
   }
 
   /* -------------------------------------------- */
@@ -171,7 +170,7 @@ export class RdDPossession {
     rollData.possession = possession
     RdDPossession.$updateEtatPossession(rollData.possession)
 
-    await RdDResolutionTable.displayRollData(rollData, rollData.attacker, 'chat-resultat-possession.html')
+    await RdDRollResult.displayRollData(rollData, rollData.attacker, 'chat-resultat-possession.html')
     if (rollData.possession.isPosseder || rollData.possession.isConjurer) {
       // conjuration
       await victime.deleteEmbeddedDocuments("Item", [rollData.possession._id])
diff --git a/module/rdd-resolution-table.js b/module/rdd-resolution-table.js
index d231370c..6e0e5219 100644
--- a/module/rdd-resolution-table.js
+++ b/module/rdd-resolution-table.js
@@ -54,7 +54,6 @@ export class RdDResolutionTable {
     return this._computeCell(level, percentage);
   }
 
-
   /* -------------------------------------------- */
   static _computeRow(caracValue) {
     let dataRow = [
@@ -89,24 +88,10 @@ export class RdDResolutionTable {
     return resultat;
   }
 
-  /* -------------------------------------------- */
-  static async displayRollData(rollData, actor = undefined, template = 'chat-resultat-general.html') {
-    return await ChatUtility.createChatWithRollMode(
-      { content: await RdDResolutionTable.buildRollDataHtml(rollData, template) },
-      actor
-    )
-  }
-
   static actorChatName(actor) {
     return actor ?? game.user.name;
   }
 
-  /* -------------------------------------------- */
-  static async buildRollDataHtml(rollData, template = 'chat-resultat-general.html') {
-    rollData.show = rollData.show || {};
-    return await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/${template}`, rollData);
-  }
-
   /* -------------------------------------------- */
   static async rollData(rollData) {
     rollData.rolled = await this.roll(rollData.caracValue, rollData.finalLevel, rollData);
diff --git a/module/rdd-roll-result.js b/module/rdd-roll-result.js
new file mode 100644
index 00000000..38049b8e
--- /dev/null
+++ b/module/rdd-roll-result.js
@@ -0,0 +1,17 @@
+import { ChatUtility } from "./chat-utility.js";
+
+export class RdDRollResult {
+
+  static async displayRollData(rollData, actor = undefined, template = 'chat-resultat-general.html') {
+    const chatMessage = await ChatUtility.createChatWithRollMode(
+      { content: await RdDRollResult.buildRollDataHtml(rollData, template) },
+      actor
+    )
+    return chatMessage
+  }
+
+  static async buildRollDataHtml(rollData, template = 'chat-resultat-general.html') {
+    rollData.show = rollData.show || {};
+    return await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/${template}`, rollData);
+  }
+}
diff --git a/module/rdd-roll.js b/module/rdd-roll.js
index 6c88cd8e..b8e437f9 100644
--- a/module/rdd-roll.js
+++ b/module/rdd-roll.js
@@ -259,7 +259,7 @@ export class RdDRoll extends Dialog {
       this.updateRollResult(html);
     });
   }
-
+  
   /* -------------------------------------------- */
   close() {
     if (this.rollData.canClose) {
@@ -269,15 +269,12 @@ export class RdDRoll extends Dialog {
   }
 
   async onAction(action) {
-    this.rollData.forceDiceResult = Number.parseInt(this.html.find("[name='force-dice-result']").val()) ?? -1;
-    await RdDResolutionTable.rollData(this.rollData);
-    console.log("RdDRoll -=>", this.rollData, this.rollData.rolled);
-    if (action.callbacks)
-      for (let callback of action.callbacks) {
-        if (callback.condition == undefined || callback.condition(this.rollData)) {
-          await callback.action(this.rollData);
-        }
-      }
+    this.rollData.forceDiceResult = Number.parseInt(this.html.find("[name='force-dice-result']").val()) ?? -1
+    await RdDResolutionTable.rollData(this.rollData)
+
+    for (let callback of action.callbacks) {
+      await callback.action(this.rollData)
+    }
   }
 
   async setSelectedSort(sort) {