diff --git a/module/rdd-commands.js b/module/rdd-commands.js
new file mode 100644
index 00000000..409ae4ee
--- /dev/null
+++ b/module/rdd-commands.js
@@ -0,0 +1,185 @@
+/* -------------------------------------------- */
+
+import { ChatUtility } from "./chat-utility.js";
+import { Misc } from "./misc.js";
+import { RdDResolutionTable } from "./rdd-resolution-table.js";
+import { RdDRollResolution } from "./rdd-roll-resolution.js";
+import { RdDRollTables } from "./rdd-rolltables.js";
+import { RdDUtility } from "./rdd-utility.js";
+import { TMRUtility } from "./tmr-utility.js";
+
+const rddRollNumeric = /(\d+)\s*([\+\-]?\d+)?\s*(s)?/;
+
+/* -------------------------------------------- */
+export class RdDCommands {
+
+  static init() {
+    if (!game.system.rdd.commands) {
+      const rddCommands = new RdDCommands();
+      rddCommands.registerCommand({ path: ["/aide"], descr: "Affiche l'aide pour toutes les commandes", func: (content, msg, params) => rddCommands.help(msg) });
+      rddCommands.registerCommand({ path: ["/help"], descr: "Affiche l'aide pour toutes les commandes", func: (content, msg, params) => rddCommands.help(msg) });
+      rddCommands.registerCommand({ path: ["/table", "queues"], descr: "Tire une Queue de Dragon", func: (content, msg, params) => RdDRollTables.getQueue() });
+      rddCommands.registerCommand({ path: ["/table", "ombre"], descr: "Tire une Ombre de Dragon", func: (content, msg, params) => RdDRollTables.getOmbre() });
+      rddCommands.registerCommand({ path: ["/table", "tetehr"], descr: "Tire une Tête de Dragon pour Hauts Revants", func: (content, msg, params) => RdDRollTables.getTeteHR() });
+      rddCommands.registerCommand({ path: ["/table", "tete"], descr: "Tire une Tête de Dragon", func: (content, msg, params) => RdDRollTables.getTete() });
+      rddCommands.registerCommand({ path: ["/table", "souffle"], descr: " Tire un Souffle de Dragon", func: (content, msg, params) => RdDRollTables.getSouffle() });
+      rddCommands.registerCommand({ path: ["/table", "tarot"], descr: "Tire une carte du Tarot Draconique", func: (content, msg, params) => RdDRollTables.getTarot() });
+      rddCommands.registerCommand({ path: ["/table", "tmr"], descr: "Tire une case aléatoire des Terre médiane", func: (content, msg, params) => TMRUtility.getTMRAleatoire() });
+
+      rddCommands.registerCommand({ path: ["/tmra"], descr: "Tire une case aléatoire des Terre médiane", func: (content, msg, params) => TMRUtility.getTMRAleatoire() });
+      rddCommands.registerCommand({ path: ["/tmrr"], descr: "Syntaxe: <strong>/tmrr case jet</strong><br>Détermine quelle est la rencontre dans la case pour le jet<br>Example: <strong>/tmrr forêt 50</strong>", func: (content, msg, params) => rddCommands.getRencontreTMR(params) });
+      rddCommands.registerCommand({
+        path: ["/rdd"], descr: `Effectue un jet de dés dans la table de résolution. Examples:
+      <br><strong>/rdd</strong> ouvre la table de résolution
+      <br><strong>/rdd 10 3</strong> effectue un jet 10 à +3
+      <br><strong>/rdd 10 +2</strong> effectue un jet 10 à +2
+      <br><strong>/rdd 15 -2</strong> effectue un jet 15 à -2
+      <br><strong>/rdd 15 0 s</strong> effectue un jet 15 à 0, avec significative requise
+      `, func: (content, msg, params) => rddCommands.rollRdd(msg, params)
+      });
+
+      rddCommands.registerCommand({ path: ["/payer"], descr: `Permet de payer un montant. Exemples:
+      <br><strong>/payer 5s 10d</strong> permet d'envoyer un message pour payer 5 sols et 10 deniers
+      <br><strong>/payer 10d</strong> permet d'envoyer un message pour payer 10 deniers
+      `, func: (content, msg, params) => RdDUtility.afficherDemandePayer(params[0], params[1])});
+      game.system.rdd.commands = rddCommands;
+    }
+  }
+
+  constructor() {
+    this.commandsTable = {};
+  }
+
+  registerCommand(command) {
+    this._addCommand(this.commandsTable, command.path, command);
+  }
+
+  _addCommand(targetTable, path, command) {
+    if (!this._validateCommand(targetTable, path, command)) {
+      return;
+    }
+    const term = path[0];
+    if (path.length == 1) {
+      targetTable[term] = command;
+    }
+    else {
+      if (!targetTable[term]) {
+        targetTable[term] = { subTable: {} };
+      }
+      this._addCommand(targetTable[term].subTable, path.slice(1), command)
+    }
+  }
+
+  _validateCommand(targetTable, path, command) {
+    if (path.length > 0 && path[0] && command.descr && (path.length != 1 || targetTable[path[0]] == undefined)) {
+      return true;
+    }
+    console.warn("RdDCommands._validateCommand failed ", targetTable, path, command);
+    return false;
+  }
+
+
+  /* -------------------------------------------- */
+  /* Manage chat commands */
+  processChatCommand(commandLine, content, msg) {
+    // Setup new message's visibility
+    let rollMode = game.settings.get("core", "rollMode");
+    if (["gmroll", "blindroll"].includes(rollMode)) msg["whisper"] = ChatMessage.getWhisperRecipients("GM");
+    if (rollMode === "blindroll") msg["blind"] = true;
+    msg["type"] = 0;
+
+    let command = commandLine[0];
+    let params = commandLine.slice(1);
+
+    return this.process(command, params, content, msg);
+  }
+
+  process(command, params, content, msg) {
+    return this._processCommand(this.commandsTable, command, params, content, msg);
+  }
+
+  _processCommand(commandsTable, name, params, content = '', msg = {}, path = "") {
+    let command = commandsTable[name];
+    path = path + name + " ";
+    if (command && command.subTable) {
+      if (params[0]) {
+        return this._processCommand(command.subTable, params[0], params.slice(1), content, msg, path)
+      }
+      else {
+        this.help(msg, command.subTable);
+        return true;
+      }
+    }
+    if (command && command.func) {
+      if (command.func(content, msg, params) === false) {
+        this._displayHelp(msg, `${path}: ${command.descr}`);
+      }
+      return true;
+    }
+    return false;
+  }
+
+  /* -------------------------------------------- */
+  help(msg, table = undefined) {
+    let list = []
+    this._buildSubTableHelp(list, '', table || this.commandsTable);
+    msg.whisper = [game.user._id];
+    msg.content = 'Commandes disponibles<ul class="alterne-list"><li class="list-item">' + list.reduce((a, b) => a + '</li><li class="list-item">' + b) + '</li></ul>';
+    ChatMessage.create(msg);
+  }
+
+  /* -------------------------------------------- */
+  _buildSubTableHelp(list, path, table) {
+    for (let [name, command] of Object.entries(table)) {
+      if (command) {
+        if (command.subTable) {
+          this._buildSubTableHelp(list, path + name + " ", command.subTable);
+        } else {
+          list.push(`<strong>${path}${name}</strong>: ${command.descr}`);
+        }
+      }
+    }
+    return list;
+  }
+
+
+  getRencontreTMR(params) {
+    if (params.length == 2) {
+      return TMRUtility.getRencontre(params[0], params[1])
+    }
+    else {
+      return false;
+    }
+  }
+
+  async rollRdd(msg, params) {
+    if (params.length == 0) {
+      RdDRollResolution.open();
+    }
+    else {
+      let flatParams = params.reduce((a, b) => `${a} ${b}`);
+      const numericParams = flatParams.match(rddRollNumeric);
+      if (numericParams) {
+        const carac = Misc.toInt(numericParams[1]);
+        const diff = Misc.toInt(numericParams[2] || 0);
+        const significative = numericParams[3] == 's'
+        await this.rollRdDNumeric(msg, carac, diff, significative);
+        return;
+      }
+    }
+  }
+
+  async rollRdDNumeric(msg, carac, diff, significative = false) {
+    let rollData = {
+      caracValue: carac,
+      finalLevel: diff,
+      showDice: true,
+      needSignificative: significative,
+      show: { title: "Table de résolution", points: true }
+    };
+    await RdDResolutionTable.rollData(rollData);
+    msg.content = await RdDResolutionTable.explainRollDataV2(rollData);
+    ChatUtility.chatWithRollMode(msg, game.user.name);
+  }
+}
+
diff --git a/module/rdd-main.js b/module/rdd-main.js
index a6be6ccb..8ef650e9 100644
--- a/module/rdd-main.js
+++ b/module/rdd-main.js
@@ -19,6 +19,7 @@ import { TMRUtility } from "./tmr-utility.js";
 import { RdDCalendrier } from "./rdd-calendrier.js";
 import { RdDResolutionTable } from "./rdd-resolution-table.js";
 import { RdDTokenHud } from "./rdd-token-hud.js";
+import { RdDCommands } from "./rdd-commands.js";
 
 /* -------------------------------------------- */
 /*  Foundry VTT Initialization                  */
@@ -212,14 +213,15 @@ Hooks.once("init", async function() {
 /* -------------------------------------------- */
 function messageDeBienvenue(){
   game.messages
-    .filter(it => it.user._id == game.user._id && it.data.content.match(/^Bienvenu(e)? dans le Rêve des Dragons/))
+    .filter(it => it.user._id == game.user._id &&  it.data.content.match(/^<div id="message-bienvenue-rdd/))
     .forEach(it => it.delete());
   ChatMessage.create( {
     user: game.user._id,
     whisper:  [game.user._id],
-    content : "Bienvenue dans le Rêve des Dragons !<br> " +
-      "Vous trouverez quelques informations pour démarrer dans ce document : @Compendium[foundryvtt-reve-de-dragon.rappel-des-regles.7uGrUHGdPu0EmIu2]{Documentation MJ/Joueurs}" }
-    );
+    content : `<div id="message-bienvenue-rdd"><span class="rdd-roll-part">Bienvenue dans le Rêve des Dragons !</span>
+    <br>Vous trouverez quelques informations pour démarrer dans ce document : @Compendium[foundryvtt-reve-de-dragon.rappel-des-regles.7uGrUHGdPu0EmIu2]{Documentation MJ/Joueurs}
+    <br>La commande <code>/aide</code> dans le chat permet de voir les commandes spécifiques à Rêve de Dragon.</div>
+    ` });
 }
 
 /* -------------------------------------------- */
@@ -232,6 +234,8 @@ Hooks.once("renderApplication", () => {
 /* -------------------------------------------- */
 Hooks.once("ready", function() {
 
+  // préparation des lignes de commandes 
+  RdDCommands.init();
   /* -------------------------------------------- */
   /* Affiche/Init le calendrier */
   let calendrier = new RdDCalendrier();
@@ -271,11 +275,15 @@ Hooks.on("preCreateToken", (scene, tokenData, options) => {
 /*  Foundry VTT Initialization                  */
 /* -------------------------------------------- */
 Hooks.on("chatMessage", (html, content, msg) => {
-  let regExp = /(\S+)/g;
-  let commands = content.match(regExp);
-
-  return RdDUtility.processChatCommand( commands, content, msg );
-} );
+  if (content[0] == '/') {
+    let regExp = /(\S+)/g;
+    let commands = content.toLowerCase().match(regExp);
+    if (game.system.rdd.commands.processChatCommand(commands, content, msg)){
+      return false;
+    }
+  }
+  return true;
+});
 
 /* -------------------------------------------- */
 Hooks.on("getCombatTrackerEntryContext", (html, options) => {
diff --git a/module/rdd-resolution-table.js b/module/rdd-resolution-table.js
index b65ad195..4fea285e 100644
--- a/module/rdd-resolution-table.js
+++ b/module/rdd-resolution-table.js
@@ -89,13 +89,22 @@ export class RdDResolutionTable {
   }
 
   static explainRollData(rollData) {
-    let message = "<br>Difficultés <strong>libre: " + rollData.diffLibre + "</strong> / conditions: " + Misc.toSignedString(rollData.diffConditions)
-      + " / état: " + rollData.etat;
-    message += RdDResolutionTable.explain(rollData.rolled)
-    if (rollData.selectedCarac == rollData.carac.volonte) {
-      message += " / moral: " + rollData.moral;
+    let parts = [];
+    if (rollData.diffLibre != undefined) {
+      parts.push(`<strong>libre: ${rollData.diffLibre}</strong>`);
     }
-    return message;
+    if (rollData.diffConditions != undefined) {
+      parts.push(`conditions: ${Misc.toSignedString(rollData.diffConditions)}`);
+    }
+    if (rollData.etat != undefined) {
+      parts.push(`état: ${rollData.etat}`);
+    }
+    if (rollData.selectedCarac != undefined && rollData.moral != undefined && rollData.selectedCarac.label == 'Volonté') {
+      parts.push(`moral: ${rollData.moral}`);
+    }
+    let message = parts.length > 0 ? "<br>Difficulté " + parts.reduce((a, b) => a + ' / ' + b) : "";
+    
+    return message+ RdDResolutionTable.explain(rollData.rolled)
   }
 
   /* -------------------------------------------- */
diff --git a/module/rdd-roll-resolution.js b/module/rdd-roll-resolution.js
index da37e502..3f22ef46 100644
--- a/module/rdd-roll-resolution.js
+++ b/module/rdd-roll-resolution.js
@@ -10,16 +10,17 @@ import { RdDResolutionTable } from "./rdd-resolution-table.js";
 export class RdDRollResolution extends Dialog {
 
   /* -------------------------------------------- */
-  static async open() {
-    let rollData = RdDRollResolution._prepareDefaultOptions();
+  static async open(rollData = {selectedCarac:10}) {
+    RdDRollResolution._setDefaultOptions(rollData);
     let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-roll-resolution.html', rollData);
     const dialog = new RdDRollResolution(rollData, html);
     dialog.render(true);
   }
 
   /* -------------------------------------------- */
-  static _prepareDefaultOptions() {
-    let rollData = {
+  static _setDefaultOptions(rollData) {
+
+    let defRollData = {
       ajustementsConditions: CONFIG.RDD.ajustementsConditions,
       difficultesLibres: CONFIG.RDD.difficultesLibres,
       etat: 0,
@@ -29,11 +30,14 @@ export class RdDRollResolution extends Dialog {
       diffConditions: 0,
       diffLibre: 0
     }
+    mergeObject(rollData, defRollData, {overwrite: false});
     for (let i = 1; i < 21; i++) {
       rollData.carac[i] = { type: "number", value: i, label: i }
+      if (rollData.selectedCarac == i) {
+        rollData.selectedCarac = rollData.carac[i];
+      }
+
     }
-    rollData.selectedCarac = rollData.carac[10];
-    return rollData;
   }
 
   /* -------------------------------------------- */
diff --git a/module/rdd-utility.js b/module/rdd-utility.js
index 46031101..bab53c0d 100644
--- a/module/rdd-utility.js
+++ b/module/rdd-utility.js
@@ -891,17 +891,6 @@ export class RdDUtility  {
     });
   }
 
-  /* -------------------------------------------- */
-  /* Display help for /table */
-  static displayHelpTable( msg )
-  {
-    msg.content = "";
-    for (let [name, tableData] of Object.entries(table2func)) {
-      msg.content += "<br>" + tableData.descr;
-    }
-    ChatMessage.create( msg );
-  }
-  
   /* -------------------------------------------- */
   static afficherDemandePayer(som1, som2) {
     som1 = (som1) ? som1.toLowerCase() : "0d";
@@ -922,38 +911,4 @@ export class RdDUtility  {
     msgPayer += "<a id='payer-button' data-somme-denier='"+sumtotald+"'>Payer</a>"
     ChatMessage.create( { content: msgPayer } );
   }
-
-  /* -------------------------------------------- */
-  /* Manage chat commands */
-  static processChatCommand( commands, content, msg ) {    
-    // Setup new message's visibility
-    let rollMode = game.settings.get("core", "rollMode");
-    if (["gmroll", "blindroll"].includes(rollMode)) msg["whisper"] = ChatMessage.getWhisperRecipients("GM");
-    if (rollMode === "blindroll") msg["blind"] = true;
-    msg["type"] = 0;
-
-    let command = commands[0];
-
-    // Roll on a table
-    if (command === "/table") {
-      if ( commands[1] ) {
-        let tableName = commands[1].toLowerCase();
-        table2func[tableName].func();
-      } else { 
-        this.displayHelpTable( msg );
-      }
-      return false
-    } else if (command === "/tmrr") {
-      TMRUtility.getRencontre(commands[1], commands[2] )
-      return false
-    } else if (command === "/tmra") {
-      TMRUtility.getTMRAleatoire( )
-      return false
-    } else if (command === "/payer") {
-      RdDUtility.afficherDemandePayer( commands[1], commands[2] )
-      return false
-    }
-
-    return true;
-  }
 }