Merge branch 'dormir-chateau-dormant' into 'master'

Dormir chateau dormant

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!37
This commit is contained in:
Leratier Bretonnien 2020-11-25 07:33:22 +00:00
commit 74729f99a6
8 changed files with 177 additions and 56 deletions

View File

@ -42,9 +42,9 @@ export class RdDActorCreatureSheet extends ActorSheet {
data.data.caracSum = sum; data.data.caracSum = sum;
data.data.carac.taille.isTaille = true; // To avoid button link; data.data.carac.taille.isTaille = true; // To avoid button link;
data.data.nbLegeres = this.actor.GetNumberBlessures(data.data.blessures.legeres.liste ); data.data.nbLegeres = this.actor.countBlessures(data.data.blessures.legeres.liste );
data.data.nbGraves = this.actor.GetNumberBlessures(data.data.blessures.graves.liste ); data.data.nbGraves = this.actor.countBlessures(data.data.blessures.graves.liste );
data.data.nbCritiques = this.actor.GetNumberBlessures(data.data.blessures.critiques.liste ); data.data.nbCritiques = this.actor.countBlessures(data.data.blessures.critiques.liste );
data.data.competencecreature = data.itemsByType["competencecreature"]; data.data.competencecreature = data.itemsByType["competencecreature"];

View File

@ -41,9 +41,9 @@ export class RdDActorHumanoideSheet extends ActorSheet {
data.data.caracSum = sum; data.data.caracSum = sum;
data.data.carac.taille.isTaille = true; // To avoid button link; data.data.carac.taille.isTaille = true; // To avoid button link;
data.data.nbLegeres = this.actor.GetNumberBlessures(data.data.blessures.legeres.liste ); data.data.nbLegeres = this.actor.countBlessures(data.data.blessures.legeres.liste );
data.data.nbGraves = this.actor.GetNumberBlessures(data.data.blessures.graves.liste ); data.data.nbGraves = this.actor.countBlessures(data.data.blessures.graves.liste );
data.data.nbCritiques = this.actor.GetNumberBlessures(data.data.blessures.critiques.liste ); data.data.nbCritiques = this.actor.countBlessures(data.data.blessures.critiques.liste );
data.data.competencecreature = data.itemsByType["competencecreature"]; data.data.competencecreature = data.itemsByType["competencecreature"];
RdDUtility.filterItemsPerTypeForSheet(data ); RdDUtility.filterItemsPerTypeForSheet(data );

View File

@ -85,9 +85,9 @@ export class RdDActorSheet extends ActorSheet {
} }
data.data.carac.taille.isTaille = true; // To avoid button link; data.data.carac.taille.isTaille = true; // To avoid button link;
data.data.nbLegeres = this.actor.GetNumberBlessures(data.data.blessures.legeres.liste ); data.data.nbLegeres = this.actor.countBlessures(data.data.blessures.legeres.liste );
data.data.nbGraves = this.actor.GetNumberBlessures(data.data.blessures.graves.liste ); data.data.nbGraves = this.actor.countBlessures(data.data.blessures.graves.liste );
data.data.nbCritiques = this.actor.GetNumberBlessures(data.data.blessures.critiques.liste ); data.data.nbCritiques = this.actor.countBlessures(data.data.blessures.critiques.liste );
// Mise à jour de l'encombrement total // Mise à jour de l'encombrement total
this.actor.computeEncombrementTotal(); this.actor.computeEncombrementTotal();
@ -251,6 +251,9 @@ export class RdDActorSheet extends ActorSheet {
html.find('.dormir-une-heure').click((event) => { html.find('.dormir-une-heure').click((event) => {
this.actor.dormir(1); this.actor.dormir(1);
}); });
html.find('.dormir-chateau-dormant').click((event) => {
this.actor.dormirChateauDormant();
});
// Display info about queue // Display info about queue
html.find('.queuesouffle-label a').click((event) => { html.find('.queuesouffle-label a').click((event) => {

View File

@ -127,7 +127,7 @@ export class RdDActor extends Actor {
/* -------------------------------------------- */ /* -------------------------------------------- */
async performRoll(rollData) { async performRoll(rollData) {
let rolled = await RdDResolutionTable.roll(rollData.carac, rollData.finalLevel); let rolled = await RdDResolutionTable.roll(rollData.caracValue, rollData.finalLevel);
//rolled.isPart = true; // Pour tester le particulières //rolled.isPart = true; // Pour tester le particulières
rollData.rolled = rolled; // garder le résultat rollData.rolled = rolled; // garder le résultat
console.log("performRoll", rollData, rolled) console.log("performRoll", rollData, rolled)
@ -331,6 +331,100 @@ export class RdDActor extends Actor {
return explications return explications
} }
async dormirChateauDormant() {
let message = {
whisper: ChatUtility.getWhisperRecipientsAndGMs( this.name ),
content : ""
};
const blessures = duplicate(this.data.data.blessures);
console.log("dormirChateauDormant", blessures)
await this._recupererBlessures(message, "legere", blessures.legeres.liste.filter(b => b.active), []);
await this._recupererBlessures(message, "grave", blessures.graves.liste.filter(b => b.active), blessures.legeres.liste);
await this._recupererBlessures(message,"legere", blessures.critiques.liste.filter(b => b.active), blessures.graves.liste);
await this.update( {"data.blessures": blessures } );
await this._recupererVie(message);
await this.transformerStress(message);
await this.retourSeuilDeReve(message);
message.content = "A la fin Chateau Dormant, " + message.content +"<br>Un nouveau jour se lève";
ChatMessage.create( message );
}
async _recupererBlessures(message, type, liste, moindres) {
let count = 0;
const definitions = RdDUtility.getDefinitionsBlessures();
let definition = definitions.find( d => d.type == type);
for (let blessure of liste) {
if (blessure.jours >= definition.facteur) {
let rolled = await this._jetRecuperationConstitution(Misc.toInt(blessure.soins_complets), message);
blessure.soins_complets = 0;
if (rolled.isSuccess && this._retrograderBlessure(type, blessure, moindres)) {
message.content += " -- une blessure " + type + " cicatrise";
count++;
}
else if (rolled.isETotal) {
message.content += " -- une blessure " + type + " s'infecte (temps de guérison augmenté de " + definition.facteur + " jours, perte de vie)";
blessure.jours = 0;
await this.santeIncDec("vie", -1);
}
else {
message.content += " -- une blessure " + type + " reste stable";
}
}
else {
blessure.jours++;
}
}
}
_retrograderBlessure(type, blessure, blessuresMoindres)
{
if (type != "legere") {
let retrograde = blessuresMoindres.find(b => !b.active);
if (!retrograde) {
return false;
}
mergeObject(retrograde, { "active": true, "premiers_soins": 0, "soins_complets": 0, "jours": 0, "localisation": blessure.localisation });
}
mergeObject(blessure, { "active": false, "premiers_soins": 0, "soins_complets": 0, "jours": 0, "localisation": "" });
return true;
}
async _recupererVie(message) {
let blessures = [].concat(this.data.data.blessures.legeres.liste).concat(this.data.data.blessures.graves.liste).concat(this.data.data.blessures.critiques.liste);
let nbBlessures = blessures.filter(b => b.active);
let vieManquante = this.data.data.sante.vie.max - this.data.data.sante.vie.value;
if (nbBlessures == 0 && vieManquante>0) {
let bonusSoins = 0;
for (let b of blessures)
{
bonusSoins = Math.max(bonusSoins, Misc.toInt(b.soins_complets));
}
let rolled = await this._jetRecuperationConstitution(bonusSoins, message)
if (rolled.isSuccess) {
const gain = Math.min(rolled.isPart ? 2 : 1, vieManquante);
message.content += " -- récupération de vie: " + gain;
await this.santeIncDec("vie", gain);
}
else if (rolled.isETotal) {
message.content += " -- perte de vie: 1";
await this.santeIncDec("vie", -1);
}
else{
message.content +=" -- vie stationnaire ";
}
}
}
async _jetRecuperationConstitution(bonusSoins, message = undefined) {
let difficulte = Misc.toInt(bonusSoins) + Math.min(0, this.data.data.sante.vie.value - this.data.data.sante.vie.max);
let rolled = await RdDResolutionTable.roll(this.data.data.carac.constitution.value, difficulte);
if (message) {
message.content += RdDResolutionTable.explain(rolled).replace(/Jet :/, "Constitution :");
}
return rolled;
}
/* -------------------------------------------- */ /* -------------------------------------------- */
async dormir(heures=1) { async dormir(heures=1) {
let message = { let message = {
@ -396,9 +490,9 @@ export class RdDActor extends Actor {
/* -------------------------------------------- */ /* -------------------------------------------- */
async recuperationReve(message) { async recuperationReve(message) {
const seuil = this.data.data.reve.seuil.value; const seuil = this.data.data.reve.seuil.value;
const reve = this.getReveActuel(); const reveActuel = this.getReveActuel();
if (reve >= seuil) { if (reveActuel >= seuil) {
message.content += "<br>Vous avez suffisament rêvé (seuil " + seuil + ", rêve actuel "+reve+")"; message.content += "<br>Vous avez suffisament rêvé (seuil " + seuil + ", rêve actuel "+reveActuel+")";
} }
else { else {
let deRecuperation = await RdDDice.deDraconique(); let deRecuperation = await RdDDice.deDraconique();
@ -407,14 +501,22 @@ export class RdDActor extends Actor {
{ {
// Rêve de Dragon ! // Rêve de Dragon !
message.content += "<br>Vous faites un <strong>Rêve de Dragon</strong> de " + deRecuperation + " Points de rêve"; message.content += "<br>Vous faites un <strong>Rêve de Dragon</strong> de " + deRecuperation + " Points de rêve";
message.content += this.combattreReveDeDragon(deRecuperation); message.content += await this.combattreReveDeDragon(deRecuperation);
} }
else{ else{
message.content += "<br>Vous récupérez " + deRecuperation + " Points de rêve"; message.content += "<br>Vous récupérez " + deRecuperation + " Points de rêve";
this.updatePointsDeReve(deRecuperation); await this.reveActuelIncDec(deRecuperation);
} }
} }
} }
async retourSeuilDeReve(message) {
const seuil = this.data.data.reve.seuil.value;
const reveActuel = this.getReveActuel();
if (reveActuel > seuil) {
message.content += "<br>Votre rêve redescend vers son seuil naturel (seuil " + seuil + ", nouveau rêve actuel "+(reveActuel-1)+")";
await this.reveActuelIncDec(-1);
}
}
/* -------------------------------------------- */ /* -------------------------------------------- */
async combattreReveDeDragon(force){ async combattreReveDeDragon(force){
@ -435,7 +537,7 @@ export class RdDActor extends Actor {
if (roll.isSuccess) { if (roll.isSuccess) {
message += "<br>Vous gagnez " + force + " points de Rêve"; message += "<br>Vous gagnez " + force + " points de Rêve";
this.updatePointDeSeuil(); this.updatePointDeSeuil();
this.updatePointsDeReve(force); this.reveActuelIncDec(force);
} }
if (roll.isPart) { if (roll.isPart) {
// TODO: Dialog pour choix entre HR opu général? // TODO: Dialog pour choix entre HR opu général?
@ -723,7 +825,7 @@ export class RdDActor extends Actor {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async updatePointsDeReve( value ) { async reveActuelIncDec( value ) {
let reve = duplicate(this.data.data.reve.reve); let reve = duplicate(this.data.data.reve.reve);
reve.value = Math.max(reve.value + value, 0); reve.value = Math.max(reve.value + value, 0);
await this.update( {"data.reve.reve": reve } ); await this.update( {"data.reve.reve": reve } );
@ -762,13 +864,9 @@ export class RdDActor extends Actor {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
GetNumberBlessures( blessuresListe ) countBlessures( blessuresListe )
{ {
let nbB = 0; return blessuresListe.filter(b => b.active).length
for ( let b of blessuresListe) {
nbB += ( b.active) ? 1 : 0;
}
return nbB;
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -817,8 +915,8 @@ export class RdDActor extends Actor {
let blessures = this.data.data.blessures; let blessures = this.data.data.blessures;
let diffVie = this.data.data.sante.vie.max - this.data.data.sante.vie.value; let diffVie = this.data.data.sante.vie.max - this.data.data.sante.vie.value;
let maxEndVie = this.data.data.sante.endurance.max - (diffVie * 2); let maxEndVie = this.data.data.sante.endurance.max - (diffVie * 2);
let nbGraves = this.GetNumberBlessures(blessures.graves.liste); let nbGraves = this.countBlessures(blessures.graves.liste);
let nbCritiques = this.GetNumberBlessures(blessures.critiques.liste); let nbCritiques = this.countBlessures(blessures.critiques.liste);
let maxEndGraves = Math.floor(this.data.data.sante.endurance.max / (2 * nbGraves)); let maxEndGraves = Math.floor(this.data.data.sante.endurance.max / (2 * nbGraves));
let maxEndCritiques = nbCritiques > 0 ? 1 : this.data.data.sante.endurance.max; let maxEndCritiques = nbCritiques > 0 ? 1 : this.data.data.sante.endurance.max;
return Math.max(0, Math.min(maxEndVie, maxEndGraves, maxEndCritiques)); return Math.max(0, Math.min(maxEndVie, maxEndGraves, maxEndCritiques));
@ -905,23 +1003,34 @@ export class RdDActor extends Actor {
/* -------------------------------------------- */ /* -------------------------------------------- */
async stressTest() { async stressTest() {
let stressRoll = this._stressRoll(); const message = {
let compteurs = duplicate(this.data.data.compteurs); content: "",
let convertion = Math.floor(compteurs.stress.value * stressRoll.factor);
compteurs.experience.value += convertion;
compteurs.stress.value = Math.max(compteurs.stress.value - convertion - 1, 0);
ChatMessage.create({
content: "Vous avez transformé " + convertion + " points de Stress en Expérience" + stressRoll.comment,
whisper: ChatMessage.getWhisperRecipients(game.user.name) whisper: ChatMessage.getWhisperRecipients(game.user.name)
}); };
await this.transformerStress(message);
ChatMessage.create(message);
}
async transformerStress(message) {
const stress = Misc.toInt(this.data.data.compteurs.stress.value);
if (stress<=0) {
return;
}
let stressRoll = await this._stressRoll();
let convertis = Math.floor(stress * stressRoll.factor);
let compteurs = duplicate(this.data.data.compteurs);
compteurs.experience.value += convertis;
compteurs.stress.value = Math.max(stress - convertis - 1, 0);
message.content += "<br>Vous transformez " + convertis + " points de Stress en Expérience" + stressRoll.comment;
await this.update({ "data.compteurs": compteurs }); await this.update({ "data.compteurs": compteurs });
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async _stressRoll() { async _stressRoll() {
let result = await RdDResolutionTable.roll(this.data.data.carac.reve.value, 0); let reveActuel = this.getReveActuel();
let result = await RdDResolutionTable.roll(reveActuel, 0);
console.log("_stressRoll", result); console.log("_stressRoll", result);
switch (result.code) { switch (result.code) {
case "sign": return { factor: 0.75, comment: " (75%): " + result.quality + " - " + result.roll + " sur " + result.score + "%" } case "sign": return { factor: 0.75, comment: " (75%): " + result.quality + " - " + result.roll + " sur " + result.score + "%" }
@ -931,7 +1040,7 @@ export class RdDActor extends Actor {
case "etotal": return { factor: 0, comment: " (0%): " + result.quality + " - " + result.roll + " sur " + result.score + "%" } case "etotal": return { factor: 0, comment: " (0%): " + result.quality + " - " + result.roll + " sur " + result.score + "%" }
case "part": case "part":
{ {
let second = await RdDResolutionTable.roll(this.data.data.carac.reve.value, 0); let second = await RdDResolutionTable.roll(reveActuel, 0);
console.log("_stressRoll", second); console.log("_stressRoll", second);
switch (second.code) { switch (second.code) {
case "part": case "sign": case "part": case "sign":

View File

@ -61,8 +61,8 @@ export class RdDResolutionTable {
/* -------------------------------------------- */ /* -------------------------------------------- */
static build() { static build() {
let table = [] let table = []
for (var carac = 0; carac <= 30; carac++) { for (var caracValue = 0; caracValue <= 60; caracValue++) {
table[carac] = this._computeRow(carac); table[caracValue] = this._computeRow(caracValue);
} }
return table; return table;
} }
@ -78,18 +78,18 @@ export class RdDResolutionTable {
static explain(rolled) { static explain(rolled) {
let message = "<br>Jet : <strong>" + rolled.roll + "</strong> sur " + rolled.score + "%"; let message = "<br>Jet : <strong>" + rolled.roll + "</strong> sur " + rolled.score + "%";
if (rolled.carac != null && rolled.finalLevel!= null) { if (rolled.caracValue != null && rolled.finalLevel!= null) {
message += " (" + rolled.carac + " à " + Misc.toSignedString(rolled.finalLevel) + ")"; message += " (" + rolled.caracValue + " à " + Misc.toSignedString(rolled.finalLevel) + ")";
} }
return message; return message;
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static async roll(carac, finalLevel) { static async roll(caracValue, finalLevel) {
let chances = this.computeChances(carac, finalLevel); let chances = this.computeChances(caracValue, finalLevel);
chances.showDice = true; chances.showDice = true;
let rolled = await this.rollChances(chances); let rolled = await this.rollChances(chances);
rolled.carac = carac; rolled.caracValue = caracValue;
rolled.finalLevel = finalLevel; rolled.finalLevel = finalLevel;
return rolled; return rolled;
} }
@ -105,14 +105,14 @@ export class RdDResolutionTable {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static computeChances(carac, difficulte) { static computeChances(caracValue, difficulte) {
if (difficulte < -16) { if (difficulte < -16) {
return duplicate(levelImpossible); return duplicate(levelImpossible);
} }
if (difficulte < -10) { if (difficulte < -10) {
return duplicate(levelDown.find(levelData => levelData.level == difficulte)); return duplicate(levelDown.find(levelData => levelData.level == difficulte));
} }
return duplicate(this.resolutionTable[carac][difficulte + 10]); return duplicate(this.resolutionTable[caracValue][difficulte + 10]);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -142,13 +142,13 @@ export class RdDResolutionTable {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static _computeRow(carac) { static _computeRow(caracValue) {
let dataRow = [ let dataRow = [
this._computeCell(-10, Math.max(Math.floor(carac / 4), 1)), this._computeCell(-10, Math.max(Math.floor(caracValue / 4), 1)),
this._computeCell(-9, Math.max(Math.floor(carac / 2), 1)) this._computeCell(-9, Math.max(Math.floor(caracValue / 2), 1))
] ]
for (var diff = -8; diff <= 22; diff++) { for (var diff = -8; diff <= 22; diff++) {
dataRow[diff + 10] = this._computeCell(diff, Math.max(Math.floor(carac * (diff + 10) / 2), 1)); dataRow[diff + 10] = this._computeCell(diff, Math.max(Math.floor(caracValue * (diff + 10) / 2), 1));
} }
return dataRow; return dataRow;
} }
@ -205,8 +205,8 @@ export class RdDResolutionTable {
let table = $("<table class='table-resolution'/>") let table = $("<table class='table-resolution'/>")
.append(this._buildHTMLHeader(this.resolutionTable[0], minLevel, maxLevel)); .append(this._buildHTMLHeader(this.resolutionTable[0], minLevel, maxLevel));
for (var carac = minCarac; carac <= maxCarac; carac++) { for (var caracValue = minCarac; caracValue <= maxCarac; caracValue++) {
table.append(this._buildHTMLRow(this.resolutionTable[carac], carac, caracValue, levelValue, minLevel, maxLevel)); table.append(this._buildHTMLRow(this.resolutionTable[caracValue], caracValue, caracValue, levelValue, minLevel, maxLevel));
} }
return table; return table;
} }

View File

@ -67,7 +67,7 @@ export class RdDRollDialog extends Dialog {
let rollLevel = RdDRollDialog._computeFinalLevel(rollData); let rollLevel = RdDRollDialog._computeFinalLevel(rollData);
rollData.finalLevel = rollLevel; rollData.finalLevel = rollLevel;
rollData.carac = rollData.selectedCarac.value rollData.caracValue = caracValue
// Sort management // Sort management
if ( rollData.selectedSort ) { if ( rollData.selectedSort ) {

View File

@ -75,6 +75,12 @@ const table2func = { "queues": {descr: "queues : Tire une queue de Dragon", fun
"souffle": { descr: "souffle: Tire un Souffle de Dragon", func: RdDRollTables.getSouffle}, "souffle": { descr: "souffle: Tire un Souffle de Dragon", func: RdDRollTables.getSouffle},
"tarot" : { descr: "tarot: Tire une carte de Tarot Dracnique", func: RdDRollTables.getTarot} }; "tarot" : { descr: "tarot: Tire une carte de Tarot Dracnique", func: RdDRollTables.getTarot} };
const definitionsBlessures = [
{ type: "legere", facteur: 2 },
{ type: "grave", facteur : 4 },
{ type: "critique", facteur : 6 }
]
const definitionsEncaissement = { const definitionsEncaissement = {
"mortel": [ "mortel": [
{ minimum: undefined, maximum: 0, endurance: "0", vie: "0", legeres: 0, graves: 0, critiques: 0 }, { minimum: undefined, maximum: 0, endurance: "0", vie: "0", legeres: 0, graves: 0, critiques: 0 },
@ -289,6 +295,9 @@ export class RdDUtility {
return ajustementsConditions; return ajustementsConditions;
} }
static getDefinitionsBlessures() {
return definitionsBlessures;
}
/* -------------------------------------------- */ /* -------------------------------------------- */
static isTronc( compName ) static isTronc( compName )
{ {

View File

@ -60,7 +60,7 @@
<div> <div>
<span class="encaisser-direct"><a title="Encaisser des dommages"><img class="button-img" src="icons/svg/bones.svg" alt="Encaisser des dommages"/></a></span> <span class="encaisser-direct"><a title="Encaisser des dommages"><img class="button-img" src="icons/svg/bones.svg" alt="Encaisser des dommages"/></a></span>
<span class="dormir-une-heure"><a title="Dormir une heure"><img class="button-img" src="icons/svg/sleep.svg" alt="Dormir une heure"/></a></span> <span class="dormir-une-heure"><a title="Dormir une heure"><img class="button-img" src="icons/svg/sleep.svg" alt="Dormir une heure"/></a></span>
<span class="chateau-dormant"><a title="Chateau Dormant"><img class="button-img" src="systems/foundryvtt-reve-de-dragon/icons/heures/hd12.svg" alt="Chateau Dormant"/></a></span> <span class="dormir-chateau-dormant"><a title="Chateau Dormant"><img class="button-img" src="systems/foundryvtt-reve-de-dragon/icons/heures/hd12.svg" alt="Chateau Dormant"/></a></span>
<span class="monte-tmr"><a title="Montée dans les Terres M&eacute;dianes !"><img class="button-img" src="systems/foundryvtt-reve-de-dragon/styles/img/ui/icon-tmr-normal.svg" alt="Montée dans les Terres M&eacute;dianes !"/></a></span> <span class="monte-tmr"><a title="Montée dans les Terres M&eacute;dianes !"><img class="button-img" src="systems/foundryvtt-reve-de-dragon/styles/img/ui/icon-tmr-normal.svg" alt="Montée dans les Terres M&eacute;dianes !"/></a></span>
<span class="monte-tmr-rapide"><a title="Montée accélérée dans les Terres M&eacute;dianes !"><img class="button-img" src="systems/foundryvtt-reve-de-dragon/styles/img/ui/icon-tmr-rapide.svg" alt="Montée accélérée dans les Terres M&eacute;dianes !"/></a></span> <span class="monte-tmr-rapide"><a title="Montée accélérée dans les Terres M&eacute;dianes !"><img class="button-img" src="systems/foundryvtt-reve-de-dragon/styles/img/ui/icon-tmr-rapide.svg" alt="Montée accélérée dans les Terres M&eacute;dianes !"/></a></span>
<span class="visu-tmr"><a title="Regarder les Terres M&eacute;dianes"><img class="button-img" src="systems/foundryvtt-reve-de-dragon/styles/img/ui/icon-tmr-view.svg" alt="Regarder les Terres M&eacute;dianes"/></a></span> <span class="visu-tmr"><a title="Regarder les Terres M&eacute;dianes"><img class="button-img" src="systems/foundryvtt-reve-de-dragon/styles/img/ui/icon-tmr-view.svg" alt="Regarder les Terres M&eacute;dianes"/></a></span>