Amélioration rencontre TMR
- mise en forme des messages - Ajout de messages poétiques - regroupement par rencontre de la gestion et des messages - séparation table de proba/type de rencontre - quelques fixes lors de tests (mes régressions?) - lors d'un déplacement avec un tourbillon, on n'a pas à vaincre les cases humides - pas de rencontre après un déplacement par une rencontre
This commit is contained in:
		| @@ -549,7 +549,7 @@ export class RdDActor extends Actor { | ||||
|       if (deRecuperation >= 7) { | ||||
|         // Rêve de Dragon ! | ||||
|         message.content += `<br>Vous faites un <strong>Rêve de Dragon</strong> de ${deRecuperation} Points de rêve`; | ||||
|         message.content += await this.combattreReveDeDragon(deRecuperation); | ||||
|         /*message.content += */await this.combattreReveDeDragon(deRecuperation); | ||||
|       } | ||||
|       else { | ||||
|         message.content += `<br>Vous récupérez ${deRecuperation} Points de rêve`; | ||||
| @@ -599,7 +599,11 @@ export class RdDActor extends Actor { | ||||
|     if (roll.isETotal) { | ||||
|       message += "<br>A cause de votre échec total, vous subissez une deuxième Queue de Dragon: " + await this.ajouterQueue(); | ||||
|     } | ||||
|     return message; | ||||
|     ChatMessage.create({ | ||||
|       whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name), | ||||
|       content: message | ||||
|     }); | ||||
|  | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
| @@ -1704,7 +1708,7 @@ export class RdDActor extends Actor { | ||||
|       competence: this.getBestDraconic(), | ||||
|       selectedSort: sortList[0], | ||||
|       coord: coord, | ||||
|       coordLabel: TMRUtility.getTMRDescription(coord).label, | ||||
|       coordLabel: TMRUtility.getTMR(coord).label, | ||||
|       diffLibre: sortList[0].data.difficulte, // Per default at startup | ||||
|       coutreve: Array(20).fill().map((item, index) => 1 + index) | ||||
|     } | ||||
|   | ||||
| @@ -9,6 +9,7 @@ import { RdDResolutionTable } from "./rdd-resolution-table.js"; | ||||
| import { RdDRollResolutionTable } from "./rdd-roll-resolution-table.js"; | ||||
| import { RdDRollTables } from "./rdd-rolltables.js"; | ||||
| import { RdDUtility } from "./rdd-utility.js"; | ||||
| import { TMRRencontres } from "./tmr-rencontres.js"; | ||||
| import { TMRUtility } from "./tmr-utility.js"; | ||||
|  | ||||
| const rddRollNumeric = /(\d+)\s*([\+\-]?\d+)?\s*(s)?/; | ||||
| @@ -187,7 +188,7 @@ export class RdDCommands { | ||||
|   /* -------------------------------------------- */ | ||||
|   async getRencontreTMR(params) { | ||||
|     if (params.length == 1 || params.length ==2) { | ||||
|       return TMRUtility.getRencontre(params[0], params[1]) | ||||
|       return TMRRencontres.rollRencontre(params[0], params[1]) | ||||
|     } | ||||
|     else { | ||||
|       return false; | ||||
|   | ||||
| @@ -26,6 +26,7 @@ import { RdDItemCompetence } from "./item-competence.js"; | ||||
| import { StatusEffects } from "./status-effects.js"; | ||||
| import { RddCompendiumOrganiser } from "./rdd-compendium-organiser.js"; | ||||
| import { ReglesOptionelles } from "./regles-optionelles.js"; | ||||
| import { TMRRencontres } from "./tmr-rencontres.js"; | ||||
|  | ||||
| /* -------------------------------------------- */ | ||||
| /*  Foundry VTT Initialization                  */ | ||||
| @@ -108,7 +109,10 @@ Hooks.once("init", async function () { | ||||
|   // preload handlebars templates | ||||
|   RdDUtility.preloadHandlebarsTemplates(); | ||||
|   // Create useful storage space | ||||
|   game.system.rdd = { TMRUtility: TMRUtility } | ||||
|   game.system.rdd = { | ||||
|     TMRUtility, | ||||
|     RdDUtility | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   game.settings.register("foundryvtt-reve-de-dragon", "accorder-entite-cauchemar", { | ||||
| @@ -225,6 +229,8 @@ Hooks.once("init", async function () { | ||||
|   RdDActor.init(); | ||||
|   RddCompendiumOrganiser.init(); | ||||
|   ReglesOptionelles.init(); | ||||
|   TMRUtility.init(); | ||||
|   TMRRencontres.init(); | ||||
| }); | ||||
|  | ||||
| /* -------------------------------------------- */ | ||||
|   | ||||
| @@ -2,21 +2,14 @@ | ||||
|  * Extend the base Dialog entity by defining a custom window to perform spell. | ||||
|  * @extends {Dialog} | ||||
|  */ | ||||
| import { RollDataAjustements } from "./rolldata-ajustements.js"; | ||||
| import { RdDUtility } from "./rdd-utility.js"; | ||||
| import { TMRUtility } from "./tmr-utility.js"; | ||||
| import { RdDRollTables } from "./rdd-rolltables.js"; | ||||
| import { tmrConstants } from "./tmr-utility.js"; | ||||
| import { RdDResolutionTable } from "./rdd-resolution-table.js"; | ||||
| import { RdDTMRRencontreDialog } from "./rdd-tmr-rencontre-dialog.js"; | ||||
|  | ||||
| /* -------------------------------------------- */ | ||||
| const tmrConstants = { | ||||
|   col1_y: 30, | ||||
|   col2_y: 55, | ||||
|   cellw: 55, | ||||
|   cellh: 55, | ||||
|   gridx: 28, | ||||
|   gridy: 28 | ||||
| } | ||||
| import { TMRRencontres } from "./tmr-rencontres.js"; | ||||
| import { ChatUtility } from "./chat-utility.js"; | ||||
|  | ||||
| /* -------------------------------------------- */ | ||||
| export class RdDTMRDialog extends Dialog { | ||||
| @@ -46,11 +39,11 @@ export class RdDTMRDialog extends Dialog { | ||||
|     this.nbFatigue = this.viewOnly ? 0 : 1; // 1 premier point de fatigue du à la montée | ||||
|     this.rencontresExistantes = duplicate(this.actor.data.data.reve.rencontre.list); | ||||
|     this.sortReserves = duplicate(this.actor.data.data.reve.reserve.list); | ||||
|     this.casesSpeciales = this.actor.data.items.filter( item => item.type == 'casetmr'); | ||||
|     this.casesSpeciales = this.actor.data.items.filter(item => item.type == 'casetmr'); | ||||
|     this.allTokens = []; | ||||
|     this.rencontreState = 'aucune'; | ||||
|     this.pixiApp = new PIXI.Application({ width: 720, height: 860 }); | ||||
|     if (!this.viewOnly){ | ||||
|     if (!this.viewOnly) { | ||||
|       this.actor.setStatusDemiReve(true); | ||||
|       this._tellToGM(this.actor.name + " monte dans les terres médianes (" + mode + ")"); | ||||
|     } | ||||
| @@ -61,7 +54,7 @@ export class RdDTMRDialog extends Dialog { | ||||
|     this.actor.santeIncDec("fatigue", this.nbFatigue).then(super.close()); // moving 1 cell costs 1 fatigue | ||||
|     this.actor.tmrApp = undefined; // Cleanup reference | ||||
|     this.actor.setStatusDemiReve(false); | ||||
|     if (!  this.viewOnly) { | ||||
|     if (!this.viewOnly) { | ||||
|       this._tellToGM(this.actor.name + " a quitté les terres médianes"); | ||||
|     } | ||||
|   } | ||||
| @@ -78,14 +71,14 @@ export class RdDTMRDialog extends Dialog { | ||||
|   displaySpecificCase() { | ||||
|     for (let caseTMR of this.casesSpeciales) { | ||||
|       console.log("SPEC CASE ", caseTMR); | ||||
|       if ( caseTMR.data.specific == 'trounoir') { | ||||
|         this._trackToken(this._tokenTrouNoir( caseTMR.data.coord )); | ||||
|       } else if ( caseTMR.data.specific == 'attache') { | ||||
|         this._trackToken(this._tokenTerreAttache( caseTMR.data.coord ));         | ||||
|       } else if ( caseTMR.data.specific == 'debordement') { | ||||
|         this._trackToken(this._tokenDebordement( caseTMR.data.coord ));         | ||||
|       } else if ( caseTMR.data.specific == 'maitrisee') { | ||||
|         this._trackToken(this._tokenMaitrisee( caseTMR.data.coord ));         | ||||
|       if (caseTMR.data.specific == 'trounoir') { | ||||
|         this._trackToken(this._tokenTrouNoir(caseTMR.data.coord)); | ||||
|       } else if (caseTMR.data.specific == 'attache') { | ||||
|         this._trackToken(this._tokenTerreAttache(caseTMR.data.coord)); | ||||
|       } else if (caseTMR.data.specific == 'debordement') { | ||||
|         this._trackToken(this._tokenDebordement(caseTMR.data.coord)); | ||||
|       } else if (caseTMR.data.specific == 'maitrisee') { | ||||
|         this._trackToken(this._tokenMaitrisee(caseTMR.data.coord)); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| @@ -120,10 +113,10 @@ export class RdDTMRDialog extends Dialog { | ||||
|     this.close(); | ||||
|   } | ||||
|   /* -------------------------------------------- */ | ||||
|   async refouler(data) { | ||||
|     this._tellToGM(this.actor.name + " a refoulé : " + this.currentRencontre.name ); | ||||
|   async refouler() { | ||||
|     this._tellToGM(this.actor.name + " a refoulé : " + this.currentRencontre.name); | ||||
|     await this.actor.deleteTMRRencontreAtPosition(); // Remove the stored rencontre if necessary | ||||
|     let result = await this.actor.ajouterRefoulement( this.currentRencontre.data.refoulement ); | ||||
|     await this.actor.ajouterRefoulement(this.currentRencontre.refoulement ?? 1); | ||||
|     this.updatePreviousRencontres(); | ||||
|     console.log("-> refouler", this.currentRencontre) | ||||
|     this.updateValuesDisplay(); | ||||
| @@ -131,11 +124,20 @@ export class RdDTMRDialog extends Dialog { | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   colorierZoneRencontre( locList) { | ||||
|   async ignorerRencontre() { | ||||
|     this._tellToGM(this.actor.name + " a ignoré : " + this.currentRencontre.name); | ||||
|     await this.actor.deleteTMRRencontreAtPosition(); // Remove the stored rencontre if necessary | ||||
|     this.updatePreviousRencontres(); | ||||
|     this.updateValuesDisplay(); | ||||
|     this.nettoyerRencontre(); | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   colorierZoneRencontre(locList) { | ||||
|     this.currentRencontre.graphics = []; // Keep track of rectangles to delete it | ||||
|     this.currentRencontre.locList = duplicate(locList); // And track of allowed location | ||||
|     for (let loc of locList) { | ||||
|       let rect = this._getCaseRectangleCoord( loc); | ||||
|       let rect = this._getCaseRectangleCoord(loc); | ||||
|       var rectDraw = new PIXI.Graphics(); | ||||
|       rectDraw.beginFill(0xFFFF00, 0.3); | ||||
|       // set the line style to have a width of 5 and set the color to red | ||||
| @@ -148,132 +150,118 @@ export class RdDTMRDialog extends Dialog { | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   async gererTourbillon( value ) { | ||||
|     this.nbFatigue += 1; | ||||
|     await this.actor.reveActuelIncDec( -value ); | ||||
|     if ( !this.currentRencontre.tourbillonDirection ) { | ||||
|       this.currentRencontre.tourbillonDirection = TMRUtility.getDirectionPattern(); | ||||
|     } | ||||
|     let tmrPos = this.actor.data.data.reve.tmrpos; | ||||
|     tmrPos.coord = TMRUtility.deplaceTMRSelonPattern( tmrPos.coord, this.currentRencontre.tourbillonDirection, value ); | ||||
|     await this.actor.update({ "data.reve.tmrpos": tmrPos }); | ||||
|     console.log("NEWPOS", tmrPos); | ||||
|   // garder la trace de l'état en cours | ||||
|   setStateRencontre(state) { | ||||
|     this.rencontreState = state; | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   async gererTourbillonRouge( ) { | ||||
|     this.nbFatigue += 1; | ||||
|     await this.actor.reveActuelIncDec( -2 ); // -2 pts de Reve a chaque itération | ||||
|     if ( !this.currentRencontre.tourbillonDirection ) { | ||||
|       this.currentRencontre.tourbillonDirection = TMRUtility.getDirectionPattern(); | ||||
|     } | ||||
|     let tmrPos = this.actor.data.data.reve.tmrpos; | ||||
|     tmrPos.coord = TMRUtility.deplaceTMRSelonPattern( tmrPos.coord, this.currentRencontre.tourbillonDirection, 4 ); // Depl. 4 cases. | ||||
|     await this.actor.update({ "data.reve.tmrpos": tmrPos }); | ||||
|     await this.actor.santeIncDec( "vie", -1); // Et -1 PV | ||||
|     console.log("TOURBILLON ROUGE", tmrPos); | ||||
|   async choisirCasePortee(coord, portee) { | ||||
|     // Récupère la liste des cases à portées | ||||
|     let locList = TMRUtility.getTMRPortee(coord, portee); | ||||
|     this.colorierZoneRencontre(locList); | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   /** Gère les rencontres avec du post-processing graphique (passeur, messagers, tourbillons, ...) */ | ||||
|   async rencontrePostProcess( rencontreData) { | ||||
|     if (!rencontreData) return; // Sanity check | ||||
|     this.rencontreState = rencontreData.state; // garder la trace de l'état en cours | ||||
|      | ||||
|     let locList | ||||
|     if ( this.rencontreState == 'passeur' || this.rencontreState == 'messager' ) { | ||||
|       // Récupère la liste des cases à portées | ||||
|       locList = TMRUtility.getTMRArea(this.actor.data.data.reve.tmrpos.coord, this.currentRencontre.force, tmrConstants ); | ||||
|      | ||||
|     } else if ( this.rencontreState == 'passeurfou' ) { // Cas spécial du passeur fou | ||||
|       let sortReserve = this.actor.data.data.reve.reserve[0]; | ||||
|       let tmrPos | ||||
|       if ( sortReserve ) { | ||||
|         tmrPos = sortReserve.coord; // Passeur fou positionne sur la case d'un ort en réserve (TODO : Choisir le plus loin) | ||||
|       } else { | ||||
|         let direction = TMRUtility.getDirectionPattern(); // Déplacement aléatoire de la force du Passeur Fou | ||||
|         tmrPos = TMRUtility.deplaceTMRSelonPattern(this.actor.data.data.reve.tmrpos.coord, direction, this.currentRencontre.force ); | ||||
|       } | ||||
|       await this.actor.update({ "data.reve.tmrpos": tmrPos });     | ||||
|  | ||||
|     } else if ( this.rencontreState == 'changeur' ) { | ||||
|       // Liste des cases de même type | ||||
|       locList = TMRUtility.getLocationTypeList( this.actor.data.data.reve.tmrpos.coord );  | ||||
|      | ||||
|     } else if ( this.rencontreState == 'reflet' ) { | ||||
|       this.nbFatigue += 1; | ||||
|  | ||||
|     } else if ( this.rencontreState == 'tourbillonblanc' ) { | ||||
|       await this.gererTourbillon(1); | ||||
|  | ||||
|     } else if ( this.rencontreState == 'tourbillonnoir' ) { | ||||
|       await this.gererTourbillon(2); | ||||
|  | ||||
|     } else if ( this.rencontreState == 'tourbillonrouge' ) { | ||||
|       await this.gererTourbillonRouge(); | ||||
|  | ||||
|     } else { | ||||
|       this.currentRencontre = undefined; // Cleanup, not used anymore | ||||
|     } | ||||
|      | ||||
|     if ( locList )  | ||||
|       this.colorierZoneRencontre( locList ); | ||||
|  | ||||
|   async choisirCaseType(type) { | ||||
|     const locList = TMRUtility.getListCoordTMR(type); | ||||
|     this.colorierZoneRencontre(locList); | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   checkQuitterTMR() { | ||||
|     if ( this.actor.data.data.reve.reve.value == 0) { | ||||
|       this._tellToGM("Vos Points de Rêve sont à 0 : vous quittez les Terres médianes !"); | ||||
|  | ||||
|     if (this.actor.isDead()) { | ||||
|       this._tellToGM("Vous êtes mort : vous quittez les Terres médianes !"); | ||||
|       this.close(); | ||||
|       return true; | ||||
|     } | ||||
|     if ( this.nbFatigue == this.actor.data.data.sante.fatigue.max ) { | ||||
|     const resteAvantInconscience = this.actor.getFatigueMax() - this.actor.getFatigueActuelle() - this.nbFatigue; | ||||
|     if (resteAvantInconscience <= 0) { | ||||
|       this._tellToGM("Vous vous écroulez de fatigue : vous quittez les Terres médianes !"); | ||||
|       this.close(); | ||||
|       this.quitterLesTMRInconscient(); | ||||
|       return true; | ||||
|     } | ||||
|     if ( this.actor.data.data.sante.vie.value == 0 ) { | ||||
|       this._tellToGM("Vous n'avez plus de Points de Vie : vous quittez les Terres médianes !"); | ||||
|       this.close(); | ||||
|     if (this.actor.getReveActuel() == 0) { | ||||
|       this._tellToGM("Vos Points de Rêve sont à 0 : vous quittez les Terres médianes !"); | ||||
|       this.quitterLesTMRInconscient(); | ||||
|       return true; | ||||
|     } | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   async quitterLesTMRInconscient() { | ||||
|     if (this.currentRencontre?.isPersistant) { | ||||
|       await this.refouler(); | ||||
|     } | ||||
|     this.close(); | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   async maitriser(data) { | ||||
|     this.actor.deleteTMRRencontreAtPosition(); // Remove the stored rencontre if necessary     | ||||
|   async maitriser() { | ||||
|     this.actor.deleteTMRRencontreAtPosition(); | ||||
|     this.updatePreviousRencontres(); | ||||
|  | ||||
|     const draconic = this.actor.getBestDraconic(); | ||||
|     const carac = this.actor.getReveActuel(); | ||||
|     const etatGeneral = this.actor.getEtatGeneral(); | ||||
|     const difficulte = draconic.data.niveau - this.currentRencontre.force + etatGeneral; | ||||
|     console.log("Maitriser", carac, draconic.data.niveau, this.currentRencontre.force, etatGeneral); | ||||
|  | ||||
|     let rolled = await RdDResolutionTable.roll(carac, difficulte); | ||||
|     let message = "<br><strong>Test : Rêve actuel / " + draconic.name + " / " + this.currentRencontre.name + "</strong>" + "<br>" | ||||
|       + RdDResolutionTable.explain(rolled); | ||||
|  | ||||
|     let rencontreData | ||||
|     if (rolled.isEchec) { | ||||
|       rencontreData =  await TMRUtility.processRencontreEchec(this.actor, this.currentRencontre, rolled, this); | ||||
|       message += rencontreData.message; | ||||
|       this._tellToGM("Vous avez <strong>échoué</strong> à maîtriser un " + this.currentRencontre.name + " de force " + this.currentRencontre.force + message); | ||||
|  | ||||
|       if (this.currentRencontre.data.quitterTMR) { // Selon les rencontres, quitter TMR ou pas | ||||
|         this.close(); | ||||
|       } | ||||
|     } else { | ||||
|       rencontreData = await TMRUtility.processRencontreReussite(this.actor, this.currentRencontre, rolled); | ||||
|       message += rencontreData.message; | ||||
|       this._tellToGM("Vous avez <strong>réussi</strong> à maîtriser un " + this.currentRencontre.name + " de force " + this.currentRencontre.force + message); | ||||
|     let rencontreData = { | ||||
|       actor: this.actor, | ||||
|       alias: this.actor.name, | ||||
|       reveDepart: this.actor.getReveActuel(), | ||||
|       competence: this.actor.getBestDraconic(), | ||||
|       rencontre: this.currentRencontre, | ||||
|       nbRounds: 1, | ||||
|       tmr: TMRUtility.getTMR(this.actor.data.data.reve.tmrpos.coord) | ||||
|     } | ||||
|  | ||||
|     await this.rencontrePostProcess( rencontreData ); | ||||
|     await this._tentativeMaitrise(rencontreData); | ||||
|   } | ||||
|  | ||||
|   async _tentativeMaitrise(rencontreData) { | ||||
|     console.log("-> matriser", rencontreData); | ||||
|  | ||||
|     rencontreData.reve = this.actor.getReveActuel(); | ||||
|     rencontreData.etat = this.actor.getEtatGeneral(); | ||||
|  | ||||
|     RollDataAjustements.calcul(rencontreData, this.actor); | ||||
|  | ||||
|     rencontreData.rolled = await RdDResolutionTable.roll(rencontreData.reve, RollDataAjustements.sum(rencontreData.ajustements)); | ||||
|  | ||||
|     let postProcess = await TMRRencontres.gererRencontre(this, rencontreData); | ||||
|  | ||||
|     ChatMessage.create({ | ||||
|       whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name), | ||||
|       content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-rencontre-tmr.html`, rencontreData) | ||||
|     }); | ||||
|  | ||||
|     if (postProcess) { | ||||
|       /** Gère les rencontres avec du post-processing (passeur, messagers, tourbillons, ...) */ | ||||
|       await postProcess(this, rencontreData); | ||||
|     } | ||||
|     else { | ||||
|       this.currentRencontre = undefined; | ||||
|     } | ||||
|  | ||||
|     console.log("-> matriser", this.currentRencontre); | ||||
|     this.updateValuesDisplay(); | ||||
|     if (this.checkQuitterTMR()) { | ||||
|       return; | ||||
|     } | ||||
|     else if (rencontreData.rolled.isEchec && rencontreData.rencontre.isPersistant) { | ||||
|       setTimeout(() => { | ||||
|         rencontreData.nbRounds++; | ||||
|         this.nbFatigue += 1; | ||||
|         this._tentativeMaitrise(rencontreData); | ||||
|         setTimeout(() => this._deleteTmrMessages(rencontreData.actor, rencontreData.nbRounds), 500); | ||||
|       }, 2000); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|     this.checkQuitterTMR(); | ||||
|     if ( this.rencontreState == 'reflet' || this.rencontreState == 'tourbillonblanc' || this.rencontreState == 'tourbillonnoir' ) | ||||
|       this.maitriser(); | ||||
|   _deleteTmrMessages(actor, nbRounds = -1) { | ||||
|     if (nbRounds < 0) { | ||||
|       ChatUtility.removeChatMessageContaining(`<h4 data-categorie="tmr" data-actor-id="${actor._id}"`); | ||||
|     } | ||||
|     else { | ||||
|       for (let i = 1; i < nbRounds; i++) { | ||||
|         ChatUtility.removeChatMessageContaining(`<h4 data-categorie="tmr" data-actor-id="${actor._id}" data-rencontre-round="${i}">`); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
| @@ -306,21 +294,19 @@ export class RdDTMRDialog extends Dialog { | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   async _jetDeRencontre(coordTMR, cellDescr) { | ||||
|  | ||||
|     let rencontre = this.rencontresExistantes.find(prev => prev.coord == coordTMR); | ||||
|     if (rencontre == undefined) { | ||||
|       let myRoll = new Roll("1d7").roll(); | ||||
|       if (myRoll.total == 7) { | ||||
|         let isSpecial = this.actor.isRencontreSpeciale(); | ||||
|         rencontre = await TMRUtility.rencontreTMRRoll(coordTMR, cellDescr, isSpecial ); | ||||
|       } else { | ||||
|         this._tellToUser(myRoll.total + ": Pas de rencontre en " + cellDescr.label + " (" + coordTMR + ")"); | ||||
|       } | ||||
|     } | ||||
|     if (TMRUtility.isForceRencontre()) { | ||||
|       return await TMRUtility.rencontreTMRRoll(coordTMR, cellDescr); | ||||
|     } | ||||
|     return rencontre; | ||||
|     let rencontre = this.rencontresExistantes.find(prev => prev.coord == coordTMR); | ||||
|     if (rencontre) { | ||||
|       return rencontre; | ||||
|     } | ||||
|     let myRoll = new Roll("1d7").evaluate(); | ||||
|     if (myRoll.total == 7) { | ||||
|       let isMauvaise = this.actor.isRencontreSpeciale(); | ||||
|       return await TMRUtility.rencontreTMRRoll(coordTMR, cellDescr, isMauvaise); | ||||
|     } | ||||
|     this._tellToUser(myRoll.total + ": Pas de rencontre en " + cellDescr.label + " (" + coordTMR + ")"); | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
| @@ -329,7 +315,7 @@ export class RdDTMRDialog extends Dialog { | ||||
|     ptsreve.innerHTML = this.actor.data.data.reve.reve.value; | ||||
|  | ||||
|     let tmrpos = document.getElementById("tmr-pos"); | ||||
|     let tmr = TMRUtility.getTMRDescription(this.actor.data.data.reve.tmrpos.coord); | ||||
|     let tmr = TMRUtility.getTMR(this.actor.data.data.reve.tmrpos.coord); | ||||
|     tmrpos.innerHTML = this.actor.data.data.reve.tmrpos.coord + " (" + tmr.label + ")"; | ||||
|  | ||||
|     let etat = document.getElementById("tmr-etatgeneral-value"); | ||||
| @@ -344,25 +330,26 @@ export class RdDTMRDialog extends Dialog { | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   async manageCaseSpeciale( cellDescr, coordTMR ) { | ||||
|     for( let caseTMR of this.casesSpeciales) { | ||||
|   async manageCaseSpeciale(cellDescr, coordTMR) { | ||||
|     for (let caseTMR of this.casesSpeciales) { | ||||
|       if (caseTMR.data.coord == coordTMR) { // Match ! | ||||
|         if (caseTMR.data.specific == 'trounoir') { | ||||
|           let newTMR = TMRUtility.getTMRAleatoire(); | ||||
|           let tmrPos = duplicate(this.actor.data.data.reve.tmrpos); | ||||
|           tmrPos.coord = newTMR; | ||||
|           await this.actor.update( { "data.reve.tmrpos": tmrPos } ); | ||||
|           ChatMessage.create( { | ||||
|           await this.actor.update({ "data.reve.tmrpos": tmrPos }); | ||||
|           ChatMessage.create({ | ||||
|             content: "Vous êtes rentré sur un Trou Noir : ré-insertion aléatoire.", | ||||
|             whisper: ChatMessage.getWhisperRecipients(game.user.name) } );    | ||||
|             whisper: ChatMessage.getWhisperRecipients(game.user.name) | ||||
|           }); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   isCaseMaitrisee( coordTMR) { | ||||
|     for( let caseTMR of this.casesSpeciales) { | ||||
|   isCaseMaitrisee(coordTMR) { | ||||
|     for (let caseTMR of this.casesSpeciales) { | ||||
|       if (caseTMR.data.coord == coordTMR && caseTMR.data.specific == 'maitrisee') { | ||||
|         return true; | ||||
|       } | ||||
| @@ -378,15 +365,16 @@ export class RdDTMRDialog extends Dialog { | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   async manageCaseHumide(cellDescr, coordTMR) { | ||||
|     if (this.viewOnly) { | ||||
|     if (this.viewOnly || this.currentRencontre) { | ||||
|       return; | ||||
|     } | ||||
|     let isHumide = this.actor.checkIsAdditionnalHumide(cellDescr, coordTMR); | ||||
|     if (cellDescr.type == "lac" || cellDescr.type == "fleuve" || cellDescr.type == "marais" || isHumide) { | ||||
|       if ( this.isCaseMaitrisee( coordTMR ) ) { | ||||
|         ChatMessage.create( { | ||||
|       if (this.isCaseMaitrisee(coordTMR)) { | ||||
|         ChatMessage.create({ | ||||
|           content: "Cette case humide est déja maitrisée grâce à votre Tête <strong>Quête des Eaux</strong>", | ||||
|           whisper: ChatMessage.getWhisperRecipients(game.user.name) } ); | ||||
|           whisper: ChatMessage.getWhisperRecipients(game.user.name) | ||||
|         }); | ||||
|         return; | ||||
|       } | ||||
|       // TODO: permettre de choisir la voie de draconic? | ||||
| @@ -398,7 +386,7 @@ export class RdDTMRDialog extends Dialog { | ||||
|       let rolled = await RdDResolutionTable.roll(carac, difficulte); | ||||
|  | ||||
|       // Gestion du souffle Double Résistance du Fleuve | ||||
|       if ( this.actor.isDoubleResistanceFleuve() ) { | ||||
|       if (this.actor.isDoubleResistanceFleuve()) { | ||||
|         let rolled2 = await RdDResolutionTable.roll(carac, difficulte); | ||||
|         if (rolled2.isEchec) | ||||
|           rolled = rolled; | ||||
| @@ -420,18 +408,18 @@ export class RdDTMRDialog extends Dialog { | ||||
|         + RdDResolutionTable.explain(rolled); | ||||
|  | ||||
|       if (rolled.isETotal) { | ||||
|         let souffle = await this.actor.ajouterSouffle({chat: false}); | ||||
|         let souffle = await this.actor.ajouterSouffle({ chat: false }); | ||||
|         explication += "<br>Vous avez fait un Echec Total. Vous subissez un Souffle de Dragon : " + souffle.name; | ||||
|         msg2MJ += "<br>Et a reçu un Souffle de Dragon : " + souffle.name; | ||||
|       } | ||||
|       if (rolled.isPart) { | ||||
|         explication += "<br>Vous avez fait une Réussite Particulière"; | ||||
|         this.actor._appliquerAjoutExperience({ rolled: rolled, seletedCarac: { label: 'reve'}, competence: draconic.name }) | ||||
|         this.actor._appliquerAjoutExperience({ rolled: rolled, seletedCarac: { label: 'reve' }, competence: draconic.name }) | ||||
|         msg2MJ += "<br>Et a fait une réussite particulière"; | ||||
|       } | ||||
|  | ||||
|       // Notification au MJ | ||||
|       ChatMessage.create( { content: msg2MJ, whisper: ChatMessage.getWhisperRecipients("GM") } ); | ||||
|       ChatMessage.create({ content: msg2MJ, whisper: ChatMessage.getWhisperRecipients("GM") }); | ||||
|       // Et au joueur (ca pourrait être un message de tchat d'ailleurs) | ||||
|       let humideDiag = new Dialog({ | ||||
|         title: "Case humide", | ||||
| @@ -445,9 +433,9 @@ export class RdDTMRDialog extends Dialog { | ||||
|     } | ||||
|   } | ||||
|   /* -------------------------------------------- */ | ||||
|   isReserveExtensible( coordTMR) { | ||||
|     for( let caseTMR of this.casesSpeciales) { | ||||
|       if (caseTMR.data.specific == 'reserve_extensible' && caseTMR.data.coord == coordTMR )  | ||||
|   isReserveExtensible(coordTMR) { | ||||
|     for (let caseTMR of this.casesSpeciales) { | ||||
|       if (caseTMR.data.specific == 'reserve_extensible' && caseTMR.data.coord == coordTMR) | ||||
|         return true; | ||||
|     } | ||||
|     return false; | ||||
| @@ -459,54 +447,56 @@ export class RdDTMRDialog extends Dialog { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     let sortReserveList  = TMRUtility.getSortReserveList( this.sortReserves, coordTMR ); | ||||
|     if (sortReserveList.length > 0 ) { | ||||
|       if ( this.actor.isReserveEnSecurite() || this.isReserveExtensible(coordTMR) ) { | ||||
|         let msg =  "Vous êtes sur une case avec un Sort en Réserve. Grâce à votre Tête <strong>Reserve en Sécurité</strong> ou <strong>Réserve Exensible</strong>, vous pouvez contrôler le déclenchement. Cliquez si vous souhaitez le déclencher : <ul>"; | ||||
|     let sortReserveList = TMRUtility.getSortReserveList(this.sortReserves, coordTMR); | ||||
|     if (sortReserveList.length > 0) { | ||||
|       if (this.actor.isReserveEnSecurite() || this.isReserveExtensible(coordTMR)) { | ||||
|         let msg = "Vous êtes sur une case avec un Sort en Réserve. Grâce à votre Tête <strong>Reserve en Sécurité</strong> ou <strong>Réserve Exensible</strong>, vous pouvez contrôler le déclenchement. Cliquez si vous souhaitez le déclencher : <ul>"; | ||||
|         for (let sortReserve of sortReserveList) { | ||||
|           msg += "<li><a class='chat-card-button' id='sort-reserve' data-actor-id='"+this.actor._id+"' data-tmr-coord='"+coordTMR+"' data-sort-id='"+sortReserve.sort._id+"'>"+sortReserve.sort.name+"</a></li>"; | ||||
|           msg += "<li><a class='chat-card-button' id='sort-reserve' data-actor-id='" + this.actor._id + "' data-tmr-coord='" + coordTMR + "' data-sort-id='" + sortReserve.sort._id + "'>" + sortReserve.sort.name + "</a></li>"; | ||||
|         } | ||||
|         msg += "</ol>"; | ||||
|         ChatMessage.create( { | ||||
|         ChatMessage.create({ | ||||
|           content: msg, | ||||
|           whisper: ChatMessage.getWhisperRecipients(game.user.name) } );   | ||||
|           whisper: ChatMessage.getWhisperRecipients(game.user.name) | ||||
|         }); | ||||
|       } else { | ||||
|         await this.processSortReserve( sortReserveList[0] ); | ||||
|         await this.processSortReserve(sortReserveList[0]); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   /* -------------------------------------------- */ | ||||
|   lancerSortEnReserve( coordTMR, sortId ) { | ||||
|     let sortReserveList  = TMRUtility.getSortReserveList( this.sortReserves, coordTMR ); | ||||
|     let sortReserve = sortReserveList.find( sortReserve => sortReserve.sort._id == sortId); | ||||
|   lancerSortEnReserve(coordTMR, sortId) { | ||||
|     let sortReserveList = TMRUtility.getSortReserveList(this.sortReserves, coordTMR); | ||||
|     let sortReserve = sortReserveList.find(sortReserve => sortReserve.sort._id == sortId); | ||||
|     //console.log("SORT RESA", sortReserveList, coordTMR, sortId, sortReserve); | ||||
|     if ( sortReserve) { | ||||
|       this.processSortReserve( sortReserve ); | ||||
|     if (sortReserve) { | ||||
|       this.processSortReserve(sortReserve); | ||||
|     } else { | ||||
|       ChatMessage.create( { | ||||
|       ChatMessage.create({ | ||||
|         content: "Une erreur est survenue : impossible de récupérer le sort en réserve demandé.", | ||||
|         whisper: ChatMessage.getWhisperRecipients(game.user.name) } );   | ||||
|         whisper: ChatMessage.getWhisperRecipients(game.user.name) | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   async processSortReserve( sortReserve ) { | ||||
|   async processSortReserve(sortReserve) { | ||||
|     await this.actor.deleteSortReserve(sortReserve); | ||||
|     this.updateSortReserve(); | ||||
|     console.log("declencheSortEnReserve", sortReserve) | ||||
|     const declenchementSort = "Vous avez déclenché le sort <strong>" + sortReserve.sort.name | ||||
|       + "</strong> en réserve en " + sortReserve.coord + " (" + TMRUtility.getTMRDescription(sortReserve.coord).label | ||||
|       + "</strong> en réserve en " + sortReserve.coord + " (" + TMRUtility.getTMR(sortReserve.coord).label | ||||
|       + ") avec " + sortReserve.sort.data.ptreve_reel + " points de Rêve"; | ||||
|     this._tellToGM(declenchementSort); | ||||
|     this.close(); | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   nettoyerRencontre( ) { | ||||
|     if ( !this.currentRencontre) return; // Sanity check | ||||
|     if ( this.currentRencontre.graphics) { | ||||
|   nettoyerRencontre() { | ||||
|     if (!this.currentRencontre) return; // Sanity check | ||||
|     if (this.currentRencontre.graphics) { | ||||
|       for (let drawRect of this.currentRencontre.graphics) { // Suppression des dessins des zones possibles | ||||
|         this.pixiApp.stage.removeChild( drawRect );  | ||||
|         this.pixiApp.stage.removeChild(drawRect); | ||||
|       } | ||||
|     } | ||||
|     this.currentRencontre = undefined; // Nettoyage de la structure | ||||
| @@ -514,21 +504,20 @@ export class RdDTMRDialog extends Dialog { | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   processClickPostRencontre( coord ) { | ||||
|     let deplacementType = "erreur"; | ||||
|   processClickPostRencontre(coord) { | ||||
|     if (this.rencontreState == 'passeur' || this.rencontreState == 'messager' || this.rencontreState == 'changeur') { | ||||
|       console.log("Searching", this.currentRencontre.locList, coord); | ||||
|       let isInArea = this.currentRencontre.locList.find(locCoord => locCoord == coord ); | ||||
|       if ( isInArea ) { // OK ! | ||||
|         deplacementType = (this.rencontreState == 'messager') ? 'messager' : 'saut'; | ||||
|       let isInArea = this.currentRencontre.locList.find(locCoord => locCoord == coord); | ||||
|       if (isInArea) { // OK ! | ||||
|         return (this.rencontreState == 'messager') ? 'messager' : 'saut'; | ||||
|       } | ||||
|     } | ||||
|     return deplacementType; | ||||
|     return "erreur"; | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   isTerreAttache( coordTMR ) { | ||||
|     for( let caseTMR of this.casesSpeciales) { | ||||
|   isTerreAttache(coordTMR) { | ||||
|     for (let caseTMR of this.casesSpeciales) { | ||||
|       if (caseTMR.data.specific == 'attache' && caseTMR.data.coord == coordTMR) { // Match ! | ||||
|         return true; | ||||
|       } | ||||
| @@ -537,10 +526,10 @@ export class RdDTMRDialog extends Dialog { | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   checkConnaissanceFleuve( currentTMR, nextTMR ) { | ||||
|     if ( this.actor.isConnaissanceFleuve() ) { | ||||
|   checkConnaissanceFleuve(currentTMR, nextTMR) { | ||||
|     if (this.actor.isConnaissanceFleuve()) { | ||||
|       //console.log(currentTMR, nextTMR ); | ||||
|       if ( TMRUtility.getTMRDescription(currentTMR).type == 'fleuve' && TMRUtility.getTMRDescription(nextTMR).type == 'fleuve') { | ||||
|       if (TMRUtility.getTMR(currentTMR).type == 'fleuve' && TMRUtility.getTMR(nextTMR).type == 'fleuve') { | ||||
|         return true; | ||||
|       } | ||||
|     } | ||||
| @@ -562,22 +551,22 @@ export class RdDTMRDialog extends Dialog { | ||||
|     console.log("deplacerDemiReve >>>>", cellx, celly); | ||||
|     let currentPos = TMRUtility.convertToCellCoord(myself.actor.data.data.reve.tmrpos.coord); | ||||
|     let coordTMR = TMRUtility.convertToTMRCoord(cellx, celly); | ||||
|     let currentTMR = TMRUtility.convertToTMRCoord( currentPos.x, currentPos.y); | ||||
|     let currentTMR = TMRUtility.convertToTMRCoord(currentPos.x, currentPos.y); | ||||
|  | ||||
|     // Validation de la case de destination (gestion du cas des rencontres qui peuvent téléporter) | ||||
|     let deplacementType = 'erreur'; | ||||
|     if ( myself.rencontreState == 'aucune') { // Pas de recontre en post-processing, donc deplacement normal | ||||
|       if ( !RdDTMRDialog._horsDePortee(currentPos, cellx, celly) || myself.isTerreAttache(coordTMR) || myself.checkConnaissanceFleuve(currentTMR,coordTMR ) ) { | ||||
|     if (myself.rencontreState == 'aucune') { // Pas de recontre en post-processing, donc deplacement normal | ||||
|       if (!RdDTMRDialog._horsDePortee(currentPos, cellx, celly) || myself.isTerreAttache(coordTMR) || myself.checkConnaissanceFleuve(currentTMR, coordTMR)) { | ||||
|         deplacementType = 'normal'; | ||||
|       } | ||||
|     } else { | ||||
|       deplacementType = myself.processClickPostRencontre( coordTMR ); | ||||
|       deplacementType = myself.processClickPostRencontre(coordTMR); | ||||
|     } | ||||
|     // Si le deplacement est valide | ||||
|     if ( deplacementType == 'normal' || deplacementType == 'saut') { | ||||
|       if ( myself.currentRencontre != 'normal' ) | ||||
|     if (deplacementType == 'normal' || deplacementType == 'saut') { | ||||
|       if (myself.currentRencontre != 'normal') | ||||
|         myself.nettoyerRencontre(); | ||||
|       let cellDescr = TMRUtility.getTMRDescription(coordTMR); | ||||
|       let cellDescr = TMRUtility.getTMR(coordTMR); | ||||
|  | ||||
|       await myself.manageCaseSpeciale(cellDescr, coordTMR); // Gestion cases spéciales type Trou noir, etc | ||||
|  | ||||
| @@ -591,12 +580,12 @@ export class RdDTMRDialog extends Dialog { | ||||
|       myself.updateValuesDisplay(); | ||||
|       game.socket.emit("system.foundryvtt-reve-de-dragon", { | ||||
|         msg: "msg_tmr_move", data: { | ||||
|           actorId:  myself.actor.data._id, | ||||
|           actorId: myself.actor.data._id, | ||||
|           tmrPos: tmrPos | ||||
|         } | ||||
|       }); | ||||
|  | ||||
|       if ( deplacementType == 'normal') { // Pas de rencontres après un saut de type passeur/changeur/... | ||||
|       if (deplacementType == 'normal') { // Pas de rencontres après un saut de type passeur/changeur/... | ||||
|         await myself.manageRencontre(coordTMR, cellDescr); | ||||
|       } | ||||
|       await myself.manageCaseHumide(cellDescr, coordTMR); | ||||
| @@ -609,7 +598,7 @@ export class RdDTMRDialog extends Dialog { | ||||
|        Si la case est le demi-rêve, ne pas lancer de sort. | ||||
|        Si un lancement de sort est en cours, trouver un moyen de réafficher cette fenêtre si on essaie de lancer un sort (ou bloquer le lancer de sort) | ||||
|       */ | ||||
|       await myself.actor.rollUnSort( coordTMR ); | ||||
|       await myself.actor.rollUnSort(coordTMR); | ||||
|       myself.nettoyerRencontre(); | ||||
|  | ||||
|     } else { | ||||
| @@ -621,16 +610,15 @@ export class RdDTMRDialog extends Dialog { | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   async forceDemiRevePositionView( coordTMR ) { | ||||
|   async forceDemiRevePositionView(coordTMR) { | ||||
|     this._updateDemiReve(this); | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   async forceDemiRevePosition( coordTMR ) { | ||||
|   async forceDemiRevePosition(coordTMR) { | ||||
|     await this.actor.updateCoordTMR(coordTMR); | ||||
|     this._updateDemiReve(this); | ||||
|     let cellDescr = TMRUtility.getTMRDescription(coordTMR); | ||||
|     await this.manageRencontre(coordTMR, cellDescr); | ||||
|     let cellDescr = TMRUtility.getTMR(coordTMR); | ||||
|     this.manageCaseHumide(cellDescr, coordTMR); | ||||
|     await this.declencheSortEnReserve(coordTMR); | ||||
|   } | ||||
| @@ -689,17 +677,17 @@ export class RdDTMRDialog extends Dialog { | ||||
|  | ||||
|     // Gestion du cout de montée en points de rêve | ||||
|     let reveCout = -1; | ||||
|     if (  this.actor.checkTeteDeplacementAccelere() ) { | ||||
|     if (this.actor.checkTeteDeplacementAccelere()) { | ||||
|       reveCout = -1; | ||||
|     } else { | ||||
|       reveCout = (this.tmrdata.isRapide) ? -2 : -1; | ||||
|     } | ||||
|     reveCout -= this.actor.checkMonteeLaborieuse(); | ||||
|     await this.actor.reveActuelIncDec( reveCout ); | ||||
|     await this.actor.reveActuelIncDec(reveCout); | ||||
|     // Le reste... | ||||
|     this.updateValuesDisplay(); | ||||
|     let coordTMR = this.actor.data.data.reve.tmrpos.coord; | ||||
|     let cellDescr = TMRUtility.getTMRDescription(coordTMR); | ||||
|     let cellDescr = TMRUtility.getTMR(coordTMR); | ||||
|     await this.manageRencontre(coordTMR, cellDescr); | ||||
|     this.manageCaseHumide(cellDescr, coordTMR); | ||||
|     // Mise à jour du nb de cases de Fatigue | ||||
| @@ -740,7 +728,7 @@ export class RdDTMRDialog extends Dialog { | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   _tokenTrouNoir( coord ) { | ||||
|   _tokenTrouNoir(coord) { | ||||
|     let sprite = new PIXI.Graphics(); | ||||
|     sprite.beginFill(0x050505, 0.8); | ||||
|     sprite.drawCircle(0, 0, (tmrConstants.cellw / 2) - 2); | ||||
| @@ -753,7 +741,7 @@ export class RdDTMRDialog extends Dialog { | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   _tokenDebordement( coord ) { | ||||
|   _tokenDebordement(coord) { | ||||
|     let sprite = new PIXI.Graphics(); | ||||
|     sprite.beginFill(0x0101FE, 0.3); | ||||
|     sprite.drawCircle(0, 0, (tmrConstants.cellw / 2) - 2); | ||||
| @@ -829,12 +817,12 @@ export class RdDTMRDialog extends Dialog { | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   /** Retourne les coordonnées x, h, w, h du rectangle d'une case donnée */ | ||||
|   _getCaseRectangleCoord( coord ) { | ||||
|     let coordXY = TMRUtility.convertToCellCoord( coord ); | ||||
|   _getCaseRectangleCoord(coord) { | ||||
|     let coordXY = TMRUtility.convertToCellCoord(coord); | ||||
|     let decallagePairImpair = (coordXY.x % 2 == 0) ? tmrConstants.col1_y : tmrConstants.col2_y; | ||||
|     let x = tmrConstants.gridx + (coordXY.x * tmrConstants.cellw) - (tmrConstants.cellw /2); | ||||
|     let y = tmrConstants.gridy + (coordXY.y * tmrConstants.cellh) - (tmrConstants.cellh /2) + decallagePairImpair; | ||||
|     return {x: x, y: y, w: tmrConstants.cellw, h: tmrConstants.cellh} | ||||
|     let x = tmrConstants.gridx + (coordXY.x * tmrConstants.cellw) - (tmrConstants.cellw / 2); | ||||
|     let y = tmrConstants.gridy + (coordXY.y * tmrConstants.cellh) - (tmrConstants.cellh / 2) + decallagePairImpair; | ||||
|     return { x: x, y: y, w: tmrConstants.cellw, h: tmrConstants.cellh } | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   | ||||
| @@ -2,17 +2,20 @@ | ||||
| export class RdDTMRRencontreDialog extends Dialog { | ||||
|    | ||||
|   /* -------------------------------------------- */ | ||||
|   constructor(html, tmrApp, rencontreData) { | ||||
|   constructor(html, tmrApp, rencontre) { | ||||
|     const dialogConf = { | ||||
|       title: "Rencontre en TMR!", | ||||
|       content: "Vous recontrez un " + rencontreData.name + " de force " + rencontreData.force + "<br>", | ||||
|       content: "Vous recontrez un " + rencontre.name + " de force " + rencontre.force + "<br>", | ||||
|       buttons: { | ||||
|         derober: { icon: '<i class="fas fa-check"></i>', label: "Se dérober", callback: () => { this.toClose = true; this.tmrApp.derober() } }, | ||||
|         refouler: { icon: '<i class="fas fa-check"></i>', label: "Refouler", callback: () => { this.toClose = true;this.tmrApp.refouler() } }, | ||||
|         maitiser: { icon: '<i class="fas fa-check"></i>', label: "Maîtriser", callback: () => { this.toClose = true;this.tmrApp.maitriser() } } | ||||
|         refouler: { icon: '<i class="fas fa-check"></i>', label: "Refouler", callback: () => { this.toClose = true; this.tmrApp.refouler() } }, | ||||
|         maitiser: { icon: '<i class="fas fa-check"></i>', label: "Maîtriser", callback: () => { this.toClose = true; this.tmrApp.maitriser() } } | ||||
|       }, | ||||
|       default: "derober" | ||||
|     } | ||||
|     if (rencontre.ignorer) { | ||||
|       dialogConf.buttons.ignorer = { icon: '<i class="fas fa-check"></i>', label: "Ignorer",  callback: () => { this.toClose = true; this.tmrApp.ignorerRencontre() }}; | ||||
|     } | ||||
|    | ||||
|     const dialogOptions = { | ||||
|       classes: ["tmrrencdialog"], | ||||
| @@ -22,7 +25,7 @@ export class RdDTMRRencontreDialog extends Dialog { | ||||
|     super(dialogConf, dialogOptions); | ||||
|  | ||||
|     this.toClose = false; | ||||
|     this.rencontreData = duplicate(rencontreData); | ||||
|     this.rencontreData = duplicate(rencontre); | ||||
|     this.tmrApp = tmrApp; | ||||
|     this.tmrApp.minimize(); | ||||
|   } | ||||
|   | ||||
| @@ -114,6 +114,12 @@ export const referenceAjustements = { | ||||
|   bonusCase: { | ||||
|     isUsed: (rollData, actor) => rollData.selectedSort && rollData.coord, | ||||
|     getDescr: (rollData, actor) => rollData.selectedSort && rollData.coord ? `Bonus de case: ${RdDItemSort.getCaseBonus(rollData.selectedSort, rollData.coord)}%` : '' | ||||
|   }, | ||||
|   rencontreTMR: { | ||||
|     isVisible: (rollData, actor) => rollData.tmr && rollData.rencontre.name, | ||||
|     isUsed: (rollData, actor) => rollData.tmr && rollData.rencontre.name, | ||||
|     getLabel: (rollData, actor) => rollData.rencontre?.name, | ||||
|     getValue: (rollData, actor) => - (rollData.rencontre?.force ?? 0) | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										487
									
								
								module/tmr-rencontres.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										487
									
								
								module/tmr-rencontres.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,487 @@ | ||||
| import { DeDraconique } from "./de-draconique.js"; | ||||
| import { Grammar } from "./grammar.js"; | ||||
| import { TMRUtility } from "./tmr-utility.js"; | ||||
| import { TMRType } from "./tmr-utility.js"; | ||||
|  | ||||
| /* -------------------------------------------- */ | ||||
| const typeRencontres = { | ||||
|  | ||||
|   messager: { | ||||
|     msgSucces: (data) => `Le ${data.rencontre.name} vous propose d'emmener le message de votre un sort à ${data.rencontre.force} cases ${data.tmr.label}.`, | ||||
|     msgEchec: (data) => `Le ${data.rencontre.name} est pressé et continue son chemin d'une traite sans vous accorder un regard.`, | ||||
|     postSucces: (tmrDialog, data) => { | ||||
|       tmrDialog.setStateRencontre(data.rencontre.type); | ||||
|       tmrDialog.choisirCasePortee(data.tmr.coord, data.rencontre.force); | ||||
|     }, | ||||
|     poesieSucces: { | ||||
|       reference: "La chevelure, Charles Baudelaire", | ||||
|       extrait: `J'irai là-bas où l'arbre et l'homme, pleins de sève, | ||||
|                 <br>Se pâment longuement sous l'ardeur des climats ; | ||||
|                 <br>Fortes tresses, soyez la houle qui m'enlève !` | ||||
|     }, | ||||
|     poesieEchec: { | ||||
|       reference: "Rêve de Dragon, Denis Gerfaud", | ||||
|       extrait: `En réalité, tous les éléments du rêve des Dragons expriment | ||||
|                 le Draconic : chaque pierre, chaque fleur, chaque goutte d'eau, | ||||
|                 chaque nuage est porteur d'un message dans la langue des Dragons`} | ||||
|   }, | ||||
|  | ||||
|   passeur: { | ||||
|     msgSucces: (data) => `Le ${data.rencontre.name} vous propose de vous transporter à ${data.rencontre.force} cases des ${data.tmr.label}.`, | ||||
|     msgEchec: (data) => `Le prix que demande le ${data.rencontre.name} est trop élevé, vous êtes réduit à poursuivre votre chemin par vos propres moyens.`, | ||||
|     postSucces: (tmrDialog, data) => { | ||||
|       tmrDialog.setStateRencontre(data.rencontre.type); | ||||
|       tmrDialog.choisirCasePortee(data.tmr.coord, data.rencontre.force); | ||||
|     }, | ||||
|     poesieSucces: { | ||||
|       reference: "Femmes damnées (2), Charles Baudelaire", | ||||
|       extrait: `Comme je descendais des Fleuves impassibles, | ||||
|                 <br>Je ne me sentis plus guidé par les haleurs : | ||||
|                 <br>Des Peaux-Rouges criards les avaient pris pour cibles, | ||||
|                 <br>Les ayant cloués nus aux poteaux de couleurs.`}, | ||||
|     poesieEchec: { | ||||
|       reference: "Le bateau ivre, Arthur Rimbaud", | ||||
|       extrait: `Loin des peuples vivants, errantes, condamnées, | ||||
|                 <br>A travers les déserts courez comme les loups ; | ||||
|                 <br>Faites votre destin, âmes désordonnées, | ||||
|                 <br>Et fuyez l'infini que vous portez en vous !`} | ||||
|   }, | ||||
|  | ||||
|   fleur: { | ||||
|     msgSucces: (data) => `Vous cueillez la ${data.rencontre.name}, son parfum vous apporte ${data.rencontre.force} points de Rêve.`, | ||||
|     msgEchec: (data) => `La ${data.rencontre.name} se fâne et disparaît entre vos doigts.`, | ||||
|     postSucces: (tmrDialog, data) => tmrDialog.actor.reveActuelIncDec(data.rencontre.force), | ||||
|     poesieSucces: { | ||||
|       reference: "L'Ennemi, Charles Baudelaire", | ||||
|       extrait: `Et qui sait si les fleurs nouvelles que je rêve | ||||
|                 <br>Trouveront dans ce sol lavé comme une grève | ||||
|                 <br>Le mystique aliment qui ferait leur vigueur ?`}, | ||||
|     poesieEchec: { | ||||
|       reference: "Une charogne, Charles Baudelaire", | ||||
|       extrait: `Et le ciel regardait la carcasse superbe | ||||
|                 <br>Comme une fleur s'épanouir. | ||||
|                 <br>La puanteur était si forte, que sur l'herbe | ||||
|                 <br>Vous crûtes vous évanouir.`}, | ||||
|   }, | ||||
|  | ||||
|   mangeur: { | ||||
|     msgSucces: (data) => `Le ${data.rencontre.name} claque de sa machoire dans le vide avant de fuir.`, | ||||
|     msgEchec: (data) => `Le ${data.rencontre.name} croque votre Rêve ! Il emporte ${data.rencontre.force} de vos points de rêve actuels`, | ||||
|     postEchec: (tmrDialog, data) => tmrDialog.actor.reveActuelIncDec(-data.rencontre.force), | ||||
|     poesieSucces: { | ||||
|       reference: "Conseil, Victor Hugo", | ||||
|       extrait: `Rois ! la bure est souvent jalouse du velours. | ||||
|                 <br>Le peuple a froid l'hiver, le peuple a faim toujours. | ||||
|                 <br>Rendez-lui son sort plus facile. | ||||
|                 <br>Le peuple souvent porte un bien rude collier. | ||||
|                 <br>Ouvrez l'école aux fils, aux pères l'atelier, | ||||
|                 <br>À tous vos bras, auguste asile !`}, | ||||
|     poesieEchec: { | ||||
|       reference: "El Desdichado, Gérard de Nerval", | ||||
|       extrait: `Suis-je Amour ou Phébus ?... Lusignan ou Biron ? | ||||
|                 <br>Mon front est rouge encor du baiser de la Reine ; | ||||
|                 <br>J'ai rêvé dans la Grotte où nage la sirène...`} | ||||
|   }, | ||||
|  | ||||
|   changeur: { | ||||
|     msgSucces: (data) => `Le ${data.rencontre.name} vaincu accepte de vous déplacer sur une autre ${TMRType[data.tmr.type]} de votre choix en échange de sa liberté.`, | ||||
|     msgEchec: (data) => { | ||||
|       data.newTMR = TMRUtility.getTMRAleatoire(data.tmr.type); | ||||
|       return `Le ${data.rencontre.name} vous embobine avec des promesses, et vous transporte en ${data.newTMR.label} sans attendre votre avis.`; | ||||
|     }, | ||||
|     postSucces: (tmrDialog, data) => { | ||||
|       tmrDialog.setStateRencontre(data.rencontre.type); | ||||
|       tmrDialog.choisirCaseType(data.tmr.type); | ||||
|     }, | ||||
|     postEchec: (tmrDialog, data) => tmrDialog.forceDemiRevePosition(data.newTMR.coord), | ||||
|     poesieSucces: { | ||||
|       reference: "Caligula - IIIème chant, Gérard de Nerval", | ||||
|       extrait: `Allez, que le caprice emporte | ||||
|                 <br>Chaque âme selon son désir, | ||||
|                 <br>Et que, close après vous, la porte | ||||
|                 <br>Ne se rouvre plus qu'au plaisir.`}, | ||||
|     poesieEchec: { | ||||
|       reference: "Rêve de Dragon, Denis Gerfaud", | ||||
|       extrait: `Les sages ont encore coutume de dire : | ||||
|                 <br>« Mais comment les Dragons peuvent-ils | ||||
|                 être influencés par une créature qui, tout | ||||
|                 bien considéré, n'existe pas vraiment pour eux, | ||||
|                 qui n'est que le fantasme de leur activité nocturne ? »`} | ||||
|   }, | ||||
|  | ||||
|   briseur: { | ||||
|     msgSucces: (data) => `Le ${data.rencontre.name} tente vainement de vous déconcentrer, avant de fuir sans demander son reste.`, | ||||
|     msgEchec: (data) => `Le ${data.rencontre.name} vous déconcentre au point de briser votre demi-rêve.`, | ||||
|     postEchec: (tmrDialog, data) => tmrDialog.close(), | ||||
|     poesieSucces: { | ||||
|       reference: "Rêve de Dragon, Denis Gerfaud", | ||||
|       extrait: `La légende affirme que ce sont les Gnomes qui furent | ||||
|               les premiers haut-rêvants. En observant les pierres précieuses, | ||||
|               les gemmes qui sont les larmes de joie des Dragons, ils parvinrent à | ||||
|               en comprendre la langue. Et l'ayant comprise, ils purent s'en servir | ||||
|               pour influencer le cours du rêve`}, | ||||
|     poesieEchec: { | ||||
|       reference: "Quand le rêve se brise, Cypora Sebagh", | ||||
|       extrait: `Quand le rêve se brise, | ||||
|               <br>Dans la plainte du jour, | ||||
|               <br>Ma mémoire devient grise | ||||
|               <br>Et sombre, tour à tour, | ||||
|               <br>Dans le puits du silence | ||||
|               <br>Et de la solitude ; | ||||
|               <br>Elle reprend son errance | ||||
|               <br>Parmi la multitude.`} | ||||
|   }, | ||||
|  | ||||
|   reflet: { | ||||
|     msgSucces: (data) => `Le ${data.rencontre.name} s'estompe dans l'oubli.`, | ||||
|     msgEchec: (data) => `Vous êtes submergé par un ${data.rencontre.name}, les souvenirs vous retiennent tant qu'il ne sera pas vaincu!`, | ||||
|     poesieSucces: { | ||||
|       reference: "Une charogne, Charles Baudelaire", | ||||
|       extrait: `Les formes s'effaçaient et n'étaient plus qu'un rêve, | ||||
|               <br>Une ébauche lente à venir | ||||
|               <br>Sur la toile oubliée, et que l'artiste achève | ||||
|               <br>Seulement par le souvenir.`}, | ||||
|     poesieEchec: { | ||||
|       reference: "La chevelure, Charles Baudelaire", | ||||
|       extrait: `Longtemps ! toujours ! ma main dans ta crinière lourde | ||||
|               <br>Sèmera le rubis, la perle et le saphir, | ||||
|               <br>Afin qu'à mon désir tu ne sois jamais sourde ! | ||||
|               <br>N'es-tu pas l'oasis où je rêve, et la gourde | ||||
|               <br>Où je hume à longs traits le vin du souvenir`} | ||||
|   }, | ||||
|  | ||||
|   passeurfou: { | ||||
|     msgSucces: (data) => `Le ${data.rencontre.name} tente vainement de découvrir où vous avez caché vos réserves. Vous le chassez, et en déroute il part harceler un autre voyageur du rêve.`, | ||||
|     msgEchec: (data) => TMRRencontres.msgEchecPasseurFou(data), | ||||
|     postEchec: (tmrDialog, data) => TMRRencontres.postEchecPasseurFou(tmrDialog, data), | ||||
|     poesieSucces: { | ||||
|       reference: "Un Fou et un Sage, Jean de La Fontaine", | ||||
|       extrait: `Certain Fou poursuivait à coups de pierre un Sage. | ||||
|               <br>Le Sage se retourne et lui dit : Mon ami, | ||||
|               <br>C'est fort bien fait à toi ; reçois cet écu-ci : | ||||
|               <br>Tu fatigues assez pour gagner davantage.`}, | ||||
|     poesieEchec: { | ||||
|       reference: "Guitare, Victor Hugo", | ||||
|       extrait: `Je la voyais passer de ma demeure, | ||||
|               <br>Et c'était tout. | ||||
|               <br>Mais à présent je m'ennuie à toute heure, | ||||
|               <br>Plein de dégoût, | ||||
|               <br>Rêveur oisif, l'âme dans la campagne, | ||||
|               <br>La dague au clou ... – | ||||
|               <br>Le vent qui vient à travers la montagne | ||||
|               <br>M'a rendu fou !`} | ||||
|   }, | ||||
|  | ||||
|   tbblanc: { | ||||
|     msgSucces: (data) => `Le ${data.rencontre.name} souleve une poussière blanche, vous tenez bon, et il tourbillonne en s'éloignant.`, | ||||
|     msgEchec: (data) => `Le souffle du ${data.rencontre.name} vous déstabilise et vous emmène dans un nuage de poussière.`, | ||||
|     postEchec: (tmrDialog, data) => TMRRencontres.onPostEchecTourbillon(tmrDialog, data, 1), | ||||
|     poesieSucces: { | ||||
|       reference: "Rêve de Dragon, Denis Gerfaud", | ||||
|       extrait: `Le Premier Âge fut appelé l'Âge des Dragons. Ce fut le commencement | ||||
|               des temps, le commencement des rêves. Durant cette période plus mythique | ||||
|               que réellement historique, les Dragons aimaient à se rêver eux-mêmes.`}, | ||||
|     poesieEchec: { | ||||
|       reference: "Les Djinns, Victor Hugo", | ||||
|       extrait: `C'est l'essaim des Djinns qui passe, | ||||
|               <br>Et tourbillonne en sifflant ! | ||||
|               <br>Les ifs, que leur vol fracasse, | ||||
|               <br>Craquent comme un pin brûlant.`}, | ||||
|   }, | ||||
|  | ||||
|   tbnoir: { | ||||
|     msgSucces: (data) => `Le ${data.rencontre.name} orageux vous enveloppe de fureur et d'éclairs, vous tenez bon face à la tempête qui s'éloigne sans vous éloigner de votre chemin.`, | ||||
|     msgEchec: (data) => `Le ${data.rencontre.name} furieux vous secoue tel un fichu de paille malmené par les vents, et vous emporte dans la tourmente.`, | ||||
|     postEchec: (tmrDialog, data) => TMRRencontres.onPostEchecTourbillon(tmrDialog, data, 2), | ||||
|     poesieSucces: { | ||||
|       reference: "Rêve de Dragon, Denis Gerfaud", | ||||
|       extrait: `Car le Second Âge fut bel et bien celui des Magiciens. Durant cette période, les | ||||
|               Gnomes s'enfoncèrent profondément sous les montagnes et la magie passa aux | ||||
|               mains des Humains qui en usèrent et abusèrent, se croyant devenus les maîtres du monde`}, | ||||
|     poesieEchec: { | ||||
|       reference: "Lily, Pierre Perret", | ||||
|       extrait: `Elle aurait pas cru sans le voir | ||||
|               <br>Que la couleur du désespoir | ||||
|               <br>Là-bas aussi ce fût le noir.`}, | ||||
|   }, | ||||
|  | ||||
|   tbrouge: { | ||||
|     msgSucces: (data) => `Le ${data.rencontre.name} s'abat avec violence mais vous êtes plus rapide et parvenez à lui échapper.`, | ||||
|     msgEchec: (data) => `Le ${data.rencontre.name} vous frappe de milliers de morsure et vous malmène à travers les terres médianes.`, | ||||
|     postEchec: (tmrDialog, data) => TMRRencontres.onPostEchecTourbillonRouge(tmrDialog, data), | ||||
|     poesieSucces: { | ||||
|       reference: "Qu'est-ce de votre vie ? une bouteille molle, Jean-Baptiste Chassignet", | ||||
|       extrait: `Qu'est-ce de votre vie ? un tourbillon rouant | ||||
|               <br>De fumière à flot gris, parmi l'air se jouant, | ||||
|               <br>Qui passe plus soudain que foudre meurtrière.`}, | ||||
|     poesieEchec: { | ||||
|       reference: "Les Djinns, poème Victor Hugo", | ||||
|       extrait: `Cris de l'enfer! voix qui hurle et qui pleure ! | ||||
|               <br>L'horrible essaim, poussé par l'aquilon, | ||||
|               <br>Sans doute, ô ciel ! s'abat sur ma demeure. | ||||
|               <br>Le mur fléchit sous le noir bataillon. | ||||
|               <br>La maison crie et chancelle penchée, | ||||
|               <br>Et l'on dirait que, du sol arrachée, | ||||
|               <br>Ainsi qu'il chasse une feuille séchée, | ||||
|               <br>Le vent la roule avec leur tourbillon !`}, | ||||
|   }, | ||||
|  | ||||
|   rdd: { | ||||
|     msgSucces: (data) => TMRRencontres.onSuccessReveDeDragon(data), | ||||
|     msgEchec: (data) => TMRRencontres.onEchecReveDeDragon(data), | ||||
|     postEchec: (tmrDialog, data) => tmrDialog.close(), | ||||
|     poesieSucces: { | ||||
|       reference: "Rêve de Dragon, Denis Gerfaud", | ||||
|       extrait: `Le monde est Rêve de Dragons, mais nous ne savons | ||||
|               <br>ni leur apparence ni qui sont les dragons. | ||||
|               <br>En dépit de l'iconographie qui les clame | ||||
|               <br>immenses créatures ailées crachant des flammes`}, | ||||
|     poesieEchec: { | ||||
|       reference: "El Desdichado, Gérard de Nerval", | ||||
|       extrait: `Je suis le Ténébreux, – le Veuf, – l'Inconsolé, | ||||
|               <br>Le Prince d'Aquitaine à la Tour abolie : | ||||
|               <br>Ma seule Etoile est morte, – et mon luth constellé | ||||
|               <br>Porte le Soleil noir de la Mélancolie.`} | ||||
|   }, | ||||
| } | ||||
|  | ||||
| /* -------------------------------------------- */ | ||||
| const mauvaisesRencontres = [ | ||||
|   { code: "mangeur1d6", name: "Mangeur de Rêve", type: "mangeur", genre: "m", force: "1d6", refoulement: 2, isMauvaise: true }, | ||||
|   { code: "mangeur2d6", name: "Mangeur de Rêve", type: "mangeur", genre: "m", force: "2d6", refoulement: 2, isMauvaise: true }, | ||||
|   { code: "reflet2d6+4", name: "Reflet d'ancien Rêve", type: "reflet", genre: "m", force: "2d6+4", refoulement: 2, isPersistant: true, isMauvaise: true }, | ||||
|   { code: "tbblanc2d6+4", name: "Tourbillon blanc", type: "tbblanc", genre: "m", force: "2d6+4", refoulement: 2, isPersistant: true, isMauvaise: true }, | ||||
|   { code: "tbnoir2d8+4", name: "Tourbillon noir", type: "tbnoir", genre: "m", force: "2d8+4", refoulement: 2, isPersistant: true, isMauvaise: true }, | ||||
|   { code: "passfou2d8", name: "Passeur fou", type: "passeurfou", genre: "m", force: "2d8", refoulement: 2, isMauvaise: true }, | ||||
|   { code: "tbrouge2d8", name: "Tourbillon rouge", type: "tbrouge", genre: "m", force: "2d8", refoulement: 3, isPersistant: true, isMauvaise: true } | ||||
| ] | ||||
|  | ||||
| /* -------------------------------------------- */ | ||||
| const rencontresStandard = [ | ||||
|   { code: "messager2d4", name: "Messager des Rêves", type: "messager", genre: "m", force: "2d4", ignorer: true }, | ||||
|   { code: "passeur2d4", name: "Passeur des Rêves", type: "passeur", genre: "m", force: "2d4", ignorer: true }, | ||||
|   { code: "fleur1d6", name: "Fleur des Rêves", type: "fleur", genre: "f", force: "1d6", ignorer: true }, | ||||
|   { code: "mangeur1d6", name: "Mangeur de Rêve", type: "mangeur", genre: "m", force: "1d6" }, | ||||
|   { code: "changeur2d6", name: "Changeur de Rêve", type: "changeur", genre: "m", force: "2d6" }, | ||||
|   { code: "briseur2d6", name: "Briseur de Rêve", type: "briseur", genre: "m", force: "2d6", quitterTMR: true }, | ||||
|   { code: "reflet1d6", name: "Reflet d'ancien Rêve", type: "reflet", genre: "m", force: "2d6", isPersistant: true }, | ||||
|   { code: "tbblanc2d6", name: "Tourbillon blanc", type: "tbblanc", genre: "m", force: "2d6", isPersistant: true }, | ||||
|   { code: "tbnoir2d8", name: "Tourbillon noir", type: "tbnoir", genre: "m", force: "2d8", isPersistant: true }, | ||||
|   { code: "rdd1ddr+7", name: "Rêve de Dragon", type: "rdd", genre: "m", force: "1ddr + 7", refoulement: 2, quitterTMR: true } | ||||
| ]; | ||||
|  | ||||
| const tableRencontres = { | ||||
|   cite: [{ code: 'messager2d4', range: [1, 25] }, { code: 'passeur2d4', range: [26, 50] }, { code: 'fleur1d6', range: [51, 65] }, { code: 'mangeur1d6', range: [66, 70] }, { code: 'changeur2d6', range: [71, 80] }, { code: 'briseur2d6', range: [81, 85] }, { code: 'reflet2d6', range: [86, 90] }, { code: 'tbblanc2d6', range: [91, 94] }, { code: 'tbnoir2d8', range: [95, 97] }, { code: 'rdd1ddr+7', range: [98, 100] }], | ||||
|   sanctuaire: [{ code: 'messager2d4', range: [1, 25] }, { code: 'passeur2d4', range: [26, 50] }, { code: 'fleur1d6', range: [51, 65] }, { code: 'mangeur1d6', range: [66, 70] }, { code: 'changeur2d6', range: [71, 80] }, { code: 'briseur2d6', range: [81, 85] }, { code: 'reflet2d6', range: [86, 90] }, { code: 'tbblanc2d6', range: [91, 94] }, { code: 'tbnoir2d8', range: [95, 97] }, { code: 'rdd1ddr+7', range: [98, 100] }], | ||||
|   plaines: [{ code: 'messager2d4', range: [1, 20] }, { code: 'passeur2d4', range: [21, 40] }, { code: 'fleur1d6', range: [41, 55] }, { code: 'mangeur1d6', range: [56, 60] }, { code: 'changeur2d6', range: [61, 75] }, { code: 'briseur2d6', range: [76, 82] }, { code: 'reflet2d6', range: [83, 88] }, { code: 'tbblanc2d6', range: [89, 93] }, { code: 'tbnoir2d8', range: [94, 97] }, { code: 'rdd1ddr+7', range: [98, 100] }], | ||||
|   pont: [{ code: 'messager2d4', range: [1, 20] }, { code: 'passeur2d4', range: [21, 40] }, { code: 'fleur1d6', range: [41, 55] }, { code: 'mangeur1d6', range: [56, 60] }, { code: 'changeur2d6', range: [61, 75] }, { code: 'briseur2d6', range: [76, 82] }, { code: 'reflet2d6', range: [83, 88] }, { code: 'tbblanc2d6', range: [89, 93] }, { code: 'tbnoir2d8', range: [94, 97] }, { code: 'rdd1ddr+7', range: [98, 100] }], | ||||
|   collines: [{ code: 'messager2d4', range: [1, 15] }, { code: 'passeur2d4', range: [16, 30] }, { code: 'fleur1d6', range: [31, 42] }, { code: 'mangeur1d6', range: [43, 54] }, { code: 'changeur2d6', range: [55, 69] }, { code: 'briseur2d6', range: [70, 82] }, { code: 'reflet2d6', range: [83, 88] }, { code: 'tbblanc2d6', range: [89, 93] }, { code: 'tbnoir2d8', range: [94, 97] }, { code: 'rdd1ddr+7', range: [98, 100] }], | ||||
|   foret: [{ code: 'messager2d4', range: [1, 15] }, { code: 'passeur2d4', range: [16, 30] }, { code: 'fleur1d6', range: [31, 42] }, { code: 'mangeur1d6', range: [43, 54] }, { code: 'changeur2d6', range: [55, 69] }, { code: 'briseur2d6', range: [70, 82] }, { code: 'reflet2d6', range: [83, 88] }, { code: 'tbblanc2d6', range: [89, 93] }, { code: 'tbnoir2d8', range: [94, 97] }, { code: 'rdd1ddr+7', range: [98, 100] }], | ||||
|   monts: [{ code: 'messager2d4', range: [1, 10] }, { code: 'passeur2d4', range: [11, 20] }, { code: 'fleur1d6', range: [21, 26] }, { code: 'mangeur1d6', range: [27, 44] }, { code: 'changeur2d6', range: [45, 59] }, { code: 'briseur2d6', range: [60, 75] }, { code: 'reflet2d6', range: [76, 85] }, { code: 'tbblanc2d6', range: [86, 92] }, { code: 'tbnoir2d8', range: [93, 97] }, { code: 'rdd1ddr+7', range: [98, 100] }], | ||||
|   desert: [{ code: 'messager2d4', range: [1, 10] }, { code: 'passeur2d4', range: [11, 20] }, { code: 'fleur1d6', range: [21, 26] }, { code: 'mangeur1d6', range: [27, 44] }, { code: 'changeur2d6', range: [45, 59] }, { code: 'briseur2d6', range: [60, 75] }, { code: 'reflet2d6', range: [76, 85] }, { code: 'tbblanc2d6', range: [86, 92] }, { code: 'tbnoir2d8', range: [93, 97] }, { code: 'rdd1ddr+7', range: [98, 100] }], | ||||
|   fleuve: [{ code: 'messager2d4', range: [1, 5] }, { code: 'passeur2d4', range: [6, 10] }, { code: 'fleur1d6', range: [11, 13] }, { code: 'mangeur1d6', range: [14, 37] }, { code: 'changeur2d6', range: [38, 49] }, { code: 'briseur2d6', range: [50, 65] }, { code: 'reflet2d6', range: [66, 79] }, { code: 'tbblanc2d6', range: [80, 89] }, { code: 'tbnoir2d8', range: [90, 97] }, { code: 'rdd1ddr+7', range: [98, 100] }], | ||||
|   lac: [{ code: 'messager2d4', range: [1, 5] }, { code: 'passeur2d4', range: [6, 10] }, { code: 'fleur1d6', range: [11, 13] }, { code: 'mangeur1d6', range: [14, 37] }, { code: 'changeur2d6', range: [38, 49] }, { code: 'briseur2d6', range: [50, 65] }, { code: 'reflet2d6', range: [66, 79] }, { code: 'tbblanc2d6', range: [80, 89] }, { code: 'tbnoir2d8', range: [90, 97] }, { code: 'rdd1ddr+7', range: [98, 100] }], | ||||
|   marais: [{ code: 'messager2d4', range: [1, 2] }, { code: 'passeur2d4', range: [3, 4] }, { code: 'fleur1d6', range: [5, 5] }, { code: 'mangeur1d6', range: [6, 29] }, { code: 'changeur2d6', range: [30, 39] }, { code: 'briseur2d6', range: [40, 60] }, { code: 'reflet2d6', range: [61, 75] }, { code: 'tbblanc2d6', range: [76, 86] }, { code: 'tbnoir2d8', range: [87, 97] }, { code: 'rdd1ddr+7', range: [98, 100] }], | ||||
|   gouffre: [{ code: 'messager2d4', range: [1, 2] }, { code: 'passeur2d4', range: [3, 4] }, { code: 'fleur1d6', range: [5, 5] }, { code: 'mangeur1d6', range: [6, 29] }, { code: 'changeur2d6', range: [30, 39] }, { code: 'briseur2d6', range: [40, 60] }, { code: 'reflet2d6', range: [61, 75] }, { code: 'tbblanc2d6', range: [76, 86] }, { code: 'tbnoir2d8', range: [87, 97] }, { code: 'rdd1ddr+7', range: [98, 100] }], | ||||
|   necropole: [{ code: 'mangeur1d6', range: [1, 20] }, { code: 'changeur2d6', range: [21, 30] }, { code: 'briseur2d6', range: [31, 50] }, { code: 'reflet2d6', range: [51, 65] }, { code: 'tbblanc2d6', range: [66, 80] }, { code: 'tbnoir2d8', range: [81, 97] }, { code: 'rdd1ddr+7', range: [98, 100] }], | ||||
|   desolation: [{ code: 'mangeur1d6', range: [1, 20] }, { code: 'changeur2d6', range: [21, 30] }, { code: 'briseur2d6', range: [31, 50] }, { code: 'reflet2d6', range: [51, 65] }, { code: 'tbblanc2d6', range: [66, 80] }, { code: 'tbnoir2d8', range: [81, 97] }, { code: 'rdd1ddr+7', range: [98, 100] }] | ||||
| } | ||||
|  | ||||
|  | ||||
| /* -------------------------------------------- */ | ||||
| export class TMRRencontres { | ||||
|   static gestionRencontre = {} | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static init() { | ||||
|     for (let type in typeRencontres) { | ||||
|       TMRRencontres.register(type, typeRencontres[type]); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static register(type, rencontre) { | ||||
|     TMRRencontres.gestionRencontre[type] = rencontre; | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   /** | ||||
|    * Retourne une recontre en fonction de la case et du tirage | ||||
|    * @param {*} terrain  | ||||
|    * @param {*} roll  | ||||
|    */ | ||||
|   static async rollRencontre(terrain, roll = undefined) { | ||||
|     if (!terrain) { | ||||
|       ChatMessage.create({ content: "Un type de case doit être indiqué (par exemple sanctuaire, desert ou cité)" }); | ||||
|       return false; | ||||
|     } | ||||
|     if (!roll || roll <= 0 || roll > 100) { | ||||
|       roll = new Roll("1d100").evaluate().total; | ||||
|     } | ||||
|     let rencontre = TMRRencontres.getRencontreAleatoire(terrain, roll); | ||||
|     ChatMessage.create({ | ||||
|       user: game.user._id, | ||||
|       whisper: [game.user._id], | ||||
|       content: `Rencontre en ${terrain} (jet : ${roll}%)<br>Vous rencontrez un ${rencontre.name} de ${force} Points de Rêve` | ||||
|     }); | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static getRencontre(index) { | ||||
|     let rencontre; | ||||
|     if (isNaN(index)) { | ||||
|       rencontre = rencontresStandard.find(r => r.type == index) | ||||
|       if (!rencontre) { | ||||
|         rencontre = mauvaisesRencontres.find(r => r.type == index) | ||||
|       } | ||||
|     } | ||||
|     else { | ||||
|       if (0 <= index && index < rencontresStandard.length) { | ||||
|         rencontre = rencontresStandard[index]; | ||||
|       } | ||||
|       else if (rencontresStandard.length <= index && index < rencontresStandard.length + mauvaisesRencontres.length) { | ||||
|         rencontre = mauvaisesRencontres[index - rencontresStandard.length]; | ||||
|       } | ||||
|     } | ||||
|     if (rencontre) { | ||||
|       return duplicate(rencontre); | ||||
|     } | ||||
|     return undefined; | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static async getRencontreAleatoire(terrain, roll = undefined) { | ||||
|     if (!roll || roll <= 0 || roll > 100) { | ||||
|       roll = new Roll("1d100").evaluate().total; | ||||
|     } | ||||
|     terrain = Grammar.toLowerCaseNoAccent(terrain); | ||||
|     console.log("getRencontreAleatoire", terrain, roll); | ||||
|     const code = tableRencontres[terrain].find(it => it.range[0] <= roll && roll <= it.range[1]).code; | ||||
|     const rencontre = duplicate(rencontresStandard.find(it => it.code == code)); | ||||
|     rencontre.roll = roll; | ||||
|     await TMRRencontres.evaluerForceRencontre(rencontre); | ||||
|     return rencontre; | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static async getMauvaiseRencontre(index = undefined) { | ||||
|     if (index == undefined || index >= mauvaisesRencontres.length) { | ||||
|       index = new Roll("1d" + mauvaisesRencontres.length).roll().total - 1; | ||||
|     } | ||||
|     const rencontre = duplicate(mauvaisesRencontres[index]); | ||||
|     await TMRRencontres.evaluerForceRencontre(rencontre); | ||||
|     return rencontre; | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static async evaluerForceRencontre(rencontre) { | ||||
|     if (TMRRencontres.isReveDeDragon(rencontre)) { | ||||
|       rencontre.force = await DeDraconique.ddr("selfroll").total + 7; | ||||
|     } | ||||
|     else { | ||||
|       rencontre.force = new Roll(rencontre.force).evaluate().total; | ||||
|     } | ||||
|     return rencontre.force; | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static isReveDeDragon(rencontre) { | ||||
|     return rencontre.type == "rdd"; | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static getGestionRencontre(name) { | ||||
|     let gestion = TMRRencontres.gestionRencontre[name]; | ||||
|     if (!gestion) { | ||||
|       ui.notifications.error(`La rencontre ${name} est inconnue, pas de méthode de gestion associée`) | ||||
|       gestion = TMRRencontres.gestionRencontre['messager']; | ||||
|     } | ||||
|     return gestion; | ||||
|   } | ||||
|  | ||||
|   static async gererRencontre(tmrDialog, data) { | ||||
|     let gestion = TMRRencontres.getGestionRencontre(data.rencontre.type); | ||||
|     if (data.rolled.isSuccess) { | ||||
|       data.message = gestion.msgSucces(data); | ||||
|       if (data.nbRounds > 1) { | ||||
|         data.message += ` Au total, vous avez passé ${data.nbRounds} rounds à vous battre!`; | ||||
|       }  | ||||
|       data.poesie = gestion.poesieSucces; | ||||
|       return gestion.postSucces; | ||||
|     } | ||||
|      | ||||
|     data.message = gestion.msgEchec(data); | ||||
|     if (data.nbRounds > 1) { | ||||
|       data.message += ` Vous avez passé ${data.nbRounds} rounds à lutter!`; | ||||
|     }  | ||||
|     data.poesie = gestion.poesieEchec; | ||||
|     return gestion.postEchec; | ||||
|   } | ||||
|  | ||||
|   static msgEchecPasseurFou(data) { | ||||
|     data.sortReserve = data.actor.data.data.reve.reserve.list[0]; | ||||
|     if (data.sortReserve) { | ||||
|       // Passeur fou positionne sur la case d'un ort en réserve // TODO : Choisir le sort le plus loin ou au hasard | ||||
|       data.newTMR = TMRUtility.getTMR(data.sortReserve.coord); | ||||
|     } else { | ||||
|       // Déplacement aléatoire de la force du Passeur Fou | ||||
|       const locList = TMRUtility.getTMRPortee(data.tmr.coord, data.rencontre.force); | ||||
|       const newCoord = locList[new Roll("1d" + locList.length).evaluate().total - 1]; | ||||
|       data.newTMR = TMRUtility.getTMR(newCoord); | ||||
|     } | ||||
|     if (data.sortReserve) { | ||||
|       return `Le ${data.rencontre.name} vous dérobe la clé de vos sorts. Vous vous saisissez de lui, mais dans un nuage violet, il vous emporte en ${data.newTMR.label} déclencher votre sort en réserve de ${data.sortReserve.name}.`; | ||||
|     } | ||||
|     else { | ||||
|       return `Le ${data.rencontre.name} tente de vous dérober la clé de vos sorts. Ne la trouvant pas, il déclenche un nuage violet et vous emporte en ${data.newTMR.label}`; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   static async postEchecPasseurFou(tmrDialog, data) { | ||||
|     if (data.sortReserve) { | ||||
|       await tmrDialog.processSortReserve(data.sortReserve); | ||||
|     } | ||||
|     await tmrDialog.forceDemiRevePosition(data.newTMR.coord); | ||||
|     if (data.sortReserve) { | ||||
|       tmrDialog.close(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static async onPostEchecTourbillon(tmrDialog, data, cases) { | ||||
|     await data.actor.reveActuelIncDec(-cases); | ||||
|     await TMRRencontres._toubillonner(tmrDialog, data.actor, cases); | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static async onPostEchecTourbillonRouge(tmrDialog, data) { | ||||
|     await data.actor.reveActuelIncDec(-2); // -2 pts de Reve a chaque itération | ||||
|     TMRRencontres._toubillonner(tmrDialog, data.actor, 4); | ||||
|     await data.actor.santeIncDec("vie", -1); // Et -1 PV | ||||
|   } | ||||
|  | ||||
|   static async _toubillonner(tmrDialog, actor, cases) { | ||||
|     let coord = actor.data.data.reve.tmrpos.coord; | ||||
|     for (let i = 0; i < cases; i++) { | ||||
|       coord = TMRUtility.deplaceTMRAleatoire(coord); | ||||
|     } | ||||
|     await tmrDialog.forceDemiRevePosition(coord) | ||||
|   } | ||||
|  | ||||
|   static async onSuccessReveDeDragon(data) { | ||||
|     await data.actor.appliquerReveDeDragon(data.rolled, data.rencontre.force); | ||||
|     if (data.rolled.isPart) { | ||||
|       await data.actor.appliquerExperience(data.rolled, 'reve', data.competence); | ||||
|     } | ||||
|     return `A tout seigneur, tout honneur, vous faites face à un ${data.rencontre.name}. Vous le maîtrisez et récupérez ses rêves. Vous gagnez ses ${data.rencontre.force} points de rêve, votre nouveau total est de " + tmrDialog.actor.data.data.reve.reve.value `; | ||||
|   } | ||||
|  | ||||
|   static async onEchecReveDeDragon(data) { | ||||
|     await data.actor.appliquerReveDeDragon(data.rolled, data.rencontre.force); | ||||
|     const queues = data.rolled.isETotal ? 'deux queues' : 'une queue'; | ||||
|     return `A tout seigneur, tout honneur, vous faites face à un ${data.rencontre.name}. La rencontre tourne au cauchemar, dans la lutte épique, vous subissez ${queues} de dragon!`; | ||||
|   } | ||||
|  | ||||
| } | ||||
|  | ||||
| @@ -1,4 +1,5 @@ | ||||
| import { DeDraconique } from "./de-draconique.js"; | ||||
| import { TMRRencontres } from "./tmr-rencontres.js"; | ||||
| import { Grammar } from "./grammar.js"; | ||||
|  | ||||
| /* -------------------------------------------- */ | ||||
| @@ -208,71 +209,55 @@ const TMRMapping = { | ||||
|       M15: { type: "cite", label: "Cité de Klana"} | ||||
|     } | ||||
|  | ||||
| /* -------------------------------------------- */ | ||||
| const rencontresSpeciale = [ | ||||
|   {name:"Mangeur de Rêve", data: { force: "1d6", ignorer: false, derober: true, refoulement: 2, quitterTMR: false } },  | ||||
|   {name:"Mangeur de Rêve", data: { force: "2d6", ignorer: false, derober: true, refoulement: 2, quitterTMR: false } },  | ||||
|   {name:"Reflet d'ancien Rêve", data: { force: "2d6+4", ignorer: false, derober: true, refoulement: 2, quitterTMR: false } },  | ||||
|   {name:"Tourbillon blanc", data: { force: "2d6+4", ignorer: false, derober: true, refoulement: 2, quitterTMR: false } },  | ||||
|   {name:"Tourbillon noir", data: { force: "2d8+4", ignorer: false, derober: true, refoulement: 2, quitterTMR: false } },  | ||||
|   {name:"Passeur fou", data: { force: "2d8", ignorer: false, derober: true, refoulement: 2, quitterTMR: false } },  | ||||
|   {name:"Tourbillon rouge", data: { force: "2d8", ignorer: false, derober: true, refoulement: 3, quitterTMR: false } } | ||||
| ] | ||||
|  | ||||
| /* -------------------------------------------- */ | ||||
| const rencontresTable = [ | ||||
|       {name:"Messagers des Rêves", data: { force: "2d4", ignorer: true, derober: true, refoulement: 1, quitterTMR: false,  | ||||
|                                         cite: "01-25", sanctuaire: "01-25", plaines: "01-20", pont: "01-20", collines: "01-15", foret: "01-15", monts: "01-10", desert: "01-10", fleuve: "01-05",  | ||||
|                                         lac: "01-05", marais: "01-02", gouffre: "01-02", necropole: "00-00", desolation: "00-00" } }, | ||||
|       {name:"Passeur des Rêves",  data:  { force: "2d4", ignorer: true, derober: true, refoulement: 1, quitterTMR: false,  | ||||
|                                           cite: "26-50", sanctuaire: "26-50", plaines: "21-40", pont: "21-40", collines: "16-30", foret: "16-30", monts: "11-20", desert: "11-20", fleuve: "06-10",  | ||||
|                                           lac: "06-10", marais: "03-04", gouffre: "03-04", necropole: "00-00", desolation: "00-00" } }, | ||||
|       {name:"Fleur des Rêves", data:     { force: "1d6", ignorer: true, derober: true, refoulement: 1, quitterTMR: false,  | ||||
|                                         cite: "51-65", sanctuaire: "51-65", plaines: "41-55", pont: "41-55", collines: "31-42", foret: "31-42", monts: "21-26", desert: "21-26", fleuve: "11-13",  | ||||
|                                        lac: "11-13", marais: "05-05", gouffre: "05-05", necropole: "00-00", desolation: "00-00" } }, | ||||
|       {name:"Mangeur de Rêve", data:    { force: "1d6", ignorer: false, derober: true, refoulement: 1, quitterTMR: false,  | ||||
|                                        cite: "66-70", sanctuaire: "66-70", plaines: "56-60", pont: "56-60", collines: "43-54", foret: "43-54", monts: "27-44", desert: "27-44", fleuve: "14-37",  | ||||
|                                        lac: "14-37", marais: "06-29", gouffre: "06-29", necropole: "01-20", desolation: "01-20" } }, | ||||
|       {name:"Changeur de Rêve", data:   { force: "2d6", ignorer: false, derober: true, refoulement: 1, quitterTMR: false,  | ||||
|                                        cite: "71-80", sanctuaire: "71-80", plaines: "61-75", pont: "61-75", collines: "55-69", foret: "55-69", monts: "45-59", desert: "45-59", fleuve: "38-49",  | ||||
|                                        lac: "38-49", marais: "30-39", gouffre: "30-39", necropole: "21-30", desolation: "21-30" } }, | ||||
|       {name:"Briseur de Rêve", data:    { force: "2d6", ignorer: false, derober: true, refoulement: 1, quitterTMR: true,  | ||||
|                                        cite: "81-85", sanctuaire: "81-85", plaines: "76-82", pont: "76-82", collines: "70-82", foret: "70-82", monts: "60-75", desert: "60-75", fleuve: "50-65",  | ||||
|                                        lac: "50-65", marais: "40-60", gouffre: "40-60", necropole: "31-50", desolation: "31-50" } }, | ||||
|       {name:"Reflet d'ancien Rêve", data:  { force: "2d6", ignorer: false, derober: true, refoulement: 1,quitterTMR: false,  | ||||
|                                        cite: "86-90", sanctuaire: "86-90", plaines: "83-88", pont: "83-88", collines: "83-88", foret: "83-88", monts: "76-85", desert: "76-85", fleuve: "66-79",  | ||||
|                                        lac: "66-79", marais: "61-75", gouffre: "61-75", necropole: "51-65", desolation: "51-65" } }, | ||||
|       {name:"Tourbillon blanc", data:  { force: "2d6", ignorer: false, derober: true, refoulement: 1, quitterTMR: false,  | ||||
|                                        cite: "91-94", sanctuaire: "91-94", plaines: "89-93", pont: "89-93", collines: "89-93", foret: "89-93", monts: "86-92", desert: "86-92", fleuve: "80-89",  | ||||
|                                        lac: "80-89", marais: "76-86", gouffre: "76-86", necropole: "66-80", desolation: "66-80" } }, | ||||
|       {name:"Tourbillon noir", data:   { force: "2d8", ignorer: false, derober: true, refoulement: 1, quitterTMR: false,  | ||||
|                                       cite: "95-97", sanctuaire: "95-97", plaines: "94-97", pont: "94-97", collines: "94-97", foret: "94-97", monts: "93-97", desert: "93-97", fleuve: "90-97",  | ||||
|                                       lac: "90-97", marais: "87-97", gouffre: "90-97", necropole: "81-97", desolation: "81-97" } }, | ||||
|       {name:"Rêve de Dragon", data:   { force: "1ddr + 7", ignorer: false, derober: true, refoulement: 2, quitterTMR: true,  | ||||
|                                        cite: "98-00", sanctuaire: "98-00", plaines: "98-00", pont: "98-00", collines: "98-00", foret: "98-00", monts: "98-00", desert: "98-00", fleuve: "98-00",  | ||||
|                                        lac: "98-00", marais: "98-00", gouffre: "98-00", necropole: "98-00", desolation: "98-00" } }  | ||||
|       ] | ||||
| export const TMRType = {  | ||||
|   cite: "cité", | ||||
|   sanctuaire: "sanctuaire", | ||||
|   plaines: "plaines", | ||||
|   pont: "pont", | ||||
|   collines: "collines", | ||||
|   foret: "forêt", | ||||
|   monts: "monts", | ||||
|   desert: "désert", | ||||
|   fleuve: "fleuve", | ||||
|   lac: "lac", | ||||
|   marais: "marais", | ||||
|   gouffre: "gouffre", | ||||
|   necropole: "nécropole", | ||||
|   desolation: "désolation" | ||||
| } | ||||
|  | ||||
| /* -------------------------------------------- */ | ||||
| const caseSpecificModes = [ "attache", "trounoir", "debordement", "reserve_extensible", "maitrisee" ]; | ||||
|  | ||||
| /* -------------------------------------------- */ | ||||
| const tmrMovePattern =  | ||||
| const tmrRandomMovePatten =  | ||||
|                 [ { name: 'top',      x: 0, y: -1 }, | ||||
|                   { name: 'topright', x: 1,  y: -1 }, | ||||
|                   { name: 'left',     x: 1,  y: 'alt' }, | ||||
|                   { name: 'botright', x: 1,  y: 1 }, | ||||
|                   { name: 'bot',      x: 0, y: 1 }, | ||||
|                   { name: 'botleft',  x: -1,  y: 1 }, | ||||
|                   { name: 'left',     x: -1,  y: 'alt' }, | ||||
|                   { name: 'topleft',  x: -1,  y: -1 } | ||||
|                ] | ||||
|  | ||||
| /* -------------------------------------------- */ | ||||
| export const tmrConstants = { | ||||
|   col1_y: 30, | ||||
|   col2_y: 55, | ||||
|   cellw: 55, | ||||
|   cellh: 55, | ||||
|   gridx: 28, | ||||
|   gridy: 28 | ||||
| } | ||||
|  | ||||
| /* -------------------------------------------- */ | ||||
|  | ||||
| /* -------------------------------------------- */ | ||||
| export class TMRUtility  { | ||||
|    | ||||
|   static init() { | ||||
|     for (let coord in TMRMapping) { | ||||
|       TMRMapping[coord].coord = coord; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static convertToTMRCoord( x, y ) | ||||
| @@ -303,261 +288,100 @@ export class TMRUtility  { | ||||
|   } | ||||
|    | ||||
|   /* -------------------------------------------- */ | ||||
|   static getTMRDescription( coordTMR) | ||||
|   static getTMR( coordTMR) | ||||
|   { | ||||
|     return TMRMapping[coordTMR]; | ||||
|   } | ||||
|    | ||||
|   /* -------------------------------------------- */ | ||||
|   /** Some debug functions  */ | ||||
|   static setForceRencontre( id, force ) { | ||||
|     this.forceRencontre = { id: id, force: force} | ||||
|   } | ||||
|   /* -------------------------------------------- */ | ||||
|   static clearForceRencontre( id, force ) { | ||||
|     this.forceRencontre = undefined | ||||
|   static async setForceRencontre( index, force = undefined ) { | ||||
|     this.prochaineRencontre = TMRRencontres.getRencontre( index ); | ||||
|     if (this.prochaineRencontre ) { | ||||
|       if (force) { | ||||
|         this.prochaineRencontre.force = force; | ||||
|       } | ||||
|       else{ | ||||
|         await TMRRencontres.evaluerForceRencontre(this.prochaineRencontre) | ||||
|       } | ||||
|       console.log("La prochaine rencontre sera:", this.prochaineRencontre.name, " force:", this.prochaineRencontre.force); | ||||
|     } | ||||
|     else { | ||||
|       ui.notifications.warn("Pas de prochaine rencontre valide pour "+index); | ||||
|     } | ||||
|   } | ||||
|   /* -------------------------------------------- */ | ||||
|   static isForceRencontre() { | ||||
|     return this.forceRencontre | ||||
|     return this.prochaineRencontre | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static getDirectionPattern() { | ||||
|     let index = new Roll("1d"+tmrMovePattern.length+" -1").roll().total; | ||||
|     return tmrMovePattern[index]; | ||||
|     let roll = new Roll("1d"+tmrRandomMovePatten.length).evaluate().total; | ||||
|     return tmrRandomMovePatten[roll -1]; | ||||
|   } | ||||
|  | ||||
|   static deplaceTMRAleatoire(coord) { | ||||
|     return TMRUtility.deplaceTMRSelonPattern(coord, TMRUtility.getDirectionPattern(), 1); | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static deplaceTMRSelonPattern( pos, pattern, nTime ) {     | ||||
|   static deplaceTMRSelonPattern( coord, direction, nTime ) { | ||||
|     for (let i=0; i <nTime; i++) { | ||||
|       let currentPosXY = TMRUtility.convertToCellCoord(pos); | ||||
|       currentPosXY.x = currentPosXY.x + pattern.x; | ||||
|       if (pattern.y == 'alt' ) {  // Alternate version | ||||
|         pattern.y += (pattern.x % 2 == 0 ) ? -1 : 1; | ||||
|       } else { | ||||
|         currentPosXY.y = currentPosXY.y + pattern.y; | ||||
|       } | ||||
|       let currentPosXY = TMRUtility.convertToCellCoord(coord); | ||||
|       currentPosXY.x = currentPosXY.x + direction.x; | ||||
|       currentPosXY.y = currentPosXY.y + direction.y; | ||||
|       if ( this._checkTMRCoord(currentPosXY.x, currentPosXY.y) ) { // Sortie de carte ! Ré-insertion aléatoire | ||||
|         pos = TMRUtility.convertToTMRCoord(currentPosXY.x, currentPosXY.y); | ||||
|         coord = TMRUtility.convertToTMRCoord(currentPosXY.x, currentPosXY.y); | ||||
|       } else { | ||||
|         pos = this.getTMRAleatoire(); | ||||
|         coord = this.getTMRAleatoire(); | ||||
|       } | ||||
|       console.log("Nouvelle case iteration !!!", i, pos); | ||||
|       console.log("Nouvelle case iteration !!!", i, coord); | ||||
|     } | ||||
|     return pos; | ||||
|     return coord; | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static async rencontreTMRRoll( coordTMR, cellDescr, isSpecial = false )  | ||||
|   static async rencontreTMRRoll( coordTMR, cellDescr, isMauvaise = false )  | ||||
|   { | ||||
|     if ( this.forceRencontre ) { | ||||
|       // Forced | ||||
|       let rencontre = duplicate(rencontresTable[this.forceRencontre.id]); | ||||
|       rencontre.force = this.forceRencontre.force; | ||||
|       rencontre.coord = coordTMR; | ||||
|       rencontre.nbCases = 0; // Utilisé pour les Tourbillons | ||||
|       return rencontre; | ||||
|     } | ||||
|     let rencontre; | ||||
|     if ( isSpecial ) { | ||||
|       let index = new Roll("1d7").roll().total; | ||||
|       rencontre = rencontresSpeciale[index-1]; | ||||
|     } else { | ||||
|       rencontre = await this.rencontreTMRTypeCase(cellDescr.type); | ||||
|     } | ||||
|     //let rencontre = rencontresTable[4]; | ||||
|     if (rencontre) { | ||||
|       rencontre = duplicate(rencontre); | ||||
|       rencontre.force = await this.evaluerForceRencontre(rencontre); | ||||
|     if ( this.prochaineRencontre ) { | ||||
|       rencontre = this.prochaineRencontre; | ||||
|       rencontre.coord = coordTMR; | ||||
|       rencontre.nbCases = 0; // Utilisé pour les Tourbillons | ||||
|       rencontre.isSpecial = isSpecial; // Garder l'information | ||||
|       this.prochaineRencontre = undefined; | ||||
|     } | ||||
|     else if ( isMauvaise ) { | ||||
|       rencontre = await TMRRencontres.getMauvaiseRencontre(); | ||||
|     } else { | ||||
|       rencontre = await TMRRencontres.getRencontreAleatoire(cellDescr.type); | ||||
|     } | ||||
|     rencontre.coord = coordTMR; | ||||
|     return rencontre; | ||||
|   } | ||||
|    | ||||
|   /* -------------------------------------------- */ | ||||
|   static async rencontreTMRTypeCase(typeTMR, roll=undefined) { | ||||
|     if (!roll) { | ||||
|       //roll = await RdDDice.show(new Roll("d100").evaluate()).total; | ||||
|       roll = new Roll("1d100").roll().total; | ||||
|       console.log("rencontreTMRTypeCase", roll); | ||||
|     } | ||||
|     typeTMR = Grammar.toLowerCaseNoAccent(typeTMR); | ||||
|     for( let rencontre of rencontresTable) { | ||||
|       console.log("TMR !!!", typeTMR, roll); | ||||
|       let scoreDef = rencontre.data[typeTMR]; | ||||
|       let min = scoreDef.substr(0,2); | ||||
|       let max = scoreDef.substr(3,2); | ||||
|       if (min=="00") min = 101; | ||||
|       if (max=="00") max = 100; | ||||
|       if (roll >= min && roll <= max) { | ||||
|         return rencontre; | ||||
|   static getListTMR(terrain) { | ||||
|     let list = []; | ||||
|     for (let index in TMRMapping) { | ||||
|       if (TMRMapping[index].type == terrain){ | ||||
|         list.push(TMRMapping[index]); | ||||
|       } | ||||
|     } | ||||
|     return list; | ||||
|   } | ||||
|  | ||||
|   static getListCoordTMR(terrain) { | ||||
|     return this.getListTMR(terrain).map(it=>it.coord); | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   /** | ||||
|    * Retourne une recontre en fonction de la case et du tirage | ||||
|    * @param {*} terrain  | ||||
|    * @param {*} roll  | ||||
|    */ | ||||
|   static async getRencontre( terrain, roll ) { | ||||
|     if ( !terrain) { | ||||
|       ChatMessage.create({ content: "Un nom de case doit être indiqué (ie /tmrr desert ou /tmrr cite)" }); | ||||
|       return false; | ||||
|     } | ||||
|      | ||||
|     roll = roll ?? new Roll("1d100").evaluate().total; | ||||
|     roll = Math.max(1, Math.min(roll, 100)); | ||||
|  | ||||
|     let rencontre = await this.rencontreTMRTypeCase(terrain, roll); | ||||
|     if (rencontre) { | ||||
|       let force = await this.evaluerForceRencontre(rencontre); | ||||
|       ChatMessage.create({  | ||||
|         user: game.user._id, | ||||
|         whisper: [game.user._id], | ||||
|         content: `Rencontre en ${terrain} (jet : ${roll}%)<br>Vous rencontrez un ${rencontre.name} de ${force} Points de Rêve`}); | ||||
|     } | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static getLocationTypeList( coordTMR ) { | ||||
|     let descr = this.getTMRDescription( coordTMR ); | ||||
|     let typeList = []; | ||||
|     for (let index in TMRMapping) { | ||||
|       let caseTMR = TMRMapping[index]; | ||||
|       if (caseTMR.type == descr.type) | ||||
|         typeList.push(index) | ||||
|     } | ||||
|     return typeList; | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static async evaluerForceRencontre(rencontre) { | ||||
|     if (this.isReveDeDragon(rencontre)) { | ||||
|       let ddr = await DeDraconique.ddr("selfroll"); | ||||
|       return ddr.total + 7; | ||||
|     }  | ||||
|     else { | ||||
|       const roll = new Roll(rencontre.data.force).evaluate(); | ||||
|       return roll.total; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static isReveDeDragon(rencontre) { | ||||
|     return rencontre.name.toLowerCase() == "Rêve de Dragon".toLowerCase(); | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static async processRencontreReussite( actor, rencontre, rolled ) { | ||||
|     let message = "<br>"; | ||||
|     let state = "aucune"; | ||||
|  | ||||
|     console.log("processRencontreReussite", actor, rencontre); | ||||
|     if (rencontre.name == "Messagers des Rêves") { | ||||
|       message += "Le Messager des Rêves vous permet de lancer votre sort à " + rencontre.force + " cases !"; | ||||
|       state = 'messager'; | ||||
|      | ||||
|     } else if (rencontre.name == "Passeur des Rêves") { | ||||
|       message += "Le Passeur des Rêves vous permet de vous téléporter à " + rencontre.force + " cases !"; | ||||
|       state = 'passeur'; | ||||
|      | ||||
|     } else if (rencontre.name == "Fleur des Rêves") { | ||||
|       await actor.reveActuelIncDec( rencontre.force ); | ||||
|       message += "La Fleur des rêves s'évanouit en vous fournissant " + rencontre.force + " Points de Rêve"; | ||||
|        | ||||
|     } else if (rencontre.name == "Mangeur de Rêve") { | ||||
|       message += "Ce Mangeur des Rêves disparait !" | ||||
|      | ||||
|     } else if (rencontre.name == "Changeur de Rêve") { | ||||
|       message += "Ce Changeur des Rêves vous propose de vous déplacer sur une autre case de même type." | ||||
|       state = 'changeur'; | ||||
|        | ||||
|     } else if (rencontre.name == "Briseur de Rêve") { | ||||
|       message += "Ce Briseur des Rêves disparait !" | ||||
|        | ||||
|     } else if (rencontre.name == "Reflet d'ancien Rêve") { | ||||
|       message += "Ce Reflet d'ancien Rêve disparait !" | ||||
|  | ||||
|     } else if (rencontre.name == "Tourbillon blanc") { | ||||
|       message += "Ce Tourbillon Blanc disparait !" | ||||
|  | ||||
|     } else if (rencontre.name == "Tourbillon noir") { | ||||
|       message += "Ce Tourbillon Noir disparait !" | ||||
|  | ||||
|     } else if (rencontre.name == "Rêve de Dragon") { | ||||
|       // TODO: xp particulière | ||||
|       message += "Vous maîtrisez le Rêve de Dragon !" | ||||
|       message += await actor.appliquerReveDeDragon(rolled, rencontre.force); | ||||
|     }   | ||||
|     return { message: message, state: state }; | ||||
|   } | ||||
|    | ||||
|   /* -------------------------------------------- */ | ||||
|   static async processRencontreEchec( actor, rencontre, rolled, tmrDialog ) { | ||||
|     let message = "<br>"; | ||||
|     let state = "aucune"; | ||||
|  | ||||
|     if (rencontre.name == "Messagers des Rêves") { | ||||
|       message += "Le Messager des Rêves s'éloigne de vous !"; | ||||
|        | ||||
|     } else if (rencontre.name == "Passeur des Rêves") { | ||||
|       message += "Le Passeur des Rêves s'éloigne de vous !"; | ||||
|        | ||||
|     } else if (rencontre.name == "Fleur des Rêves") { | ||||
|       message += "La Fleur des rêves s'éloigne de vous et se perd dans les Terres Médianes"; | ||||
|        | ||||
|     } else if (rencontre.name == "Mangeur de Rêve") { | ||||
|       await actor.reveActuelIncDec( -rencontre.force ); | ||||
|       message += "Ce Mangeur des Rêves croque votre Rêve ! Vous perdez " + rencontre.force + " points de rêve actuels, votre nouveau total est de " + actor.data.data.reve.reve.value;       | ||||
|     | ||||
|     } else if (rencontre.name == "Changeur de Rêve") { | ||||
|       message += "Ce Changeur des Rêves vous déplace sur un autre case du même type.<br>" | ||||
|       let locList = this.getLocationTypeList( actor.data.data.reve.tmrpos.coord );  | ||||
|       let index = new Roll("1d"+locList.length + " - 1").roll().total; | ||||
|       let newCoord = locList[index]; | ||||
|       tmrDialog.forceDemiRevePosition(newCoord); | ||||
|       let cellDescr = TMRUtility.getTMRDescription(newCoord); | ||||
|       message += "Vous avez été téléporté en " + newCoord +  " - " + cellDescr.label; | ||||
|        | ||||
|     } else if (rencontre.name == "Briseur de Rêve") { | ||||
|       message += "Votre Rêve est Brisé, vous quittez les Terres Médianes"; | ||||
|        | ||||
|     } else if (rencontre.name == "Passeur fou") { | ||||
|       message += "Vous êtes déplacé sur la case de votre sort en réserve le plus proche, ou sinon aléatoirement dans une direction"; | ||||
|       state = "passeurfou"; | ||||
|        | ||||
|     } else if (rencontre.name == "Reflet d'ancien Rêve") { | ||||
|       message += "Votre Rêve est figé, vous restez sur cette case tant que ce Reflet n'est pas vaincu!"; | ||||
|       state = "reflet"; | ||||
|  | ||||
|     } else if (rencontre.name == "Tourbillon blanc") { | ||||
|       message += "Vous êtes emporté par le Tourbillon..."; | ||||
|       state = "tourbillonblanc"; | ||||
|      | ||||
|     } else if (rencontre.name == "Tourbillon noir") { | ||||
|       message +=  "Vous êtes emporté par le Tourbillon..."; | ||||
|       state = "tourbillonnoir"; | ||||
|  | ||||
|     } else if (rencontre.name == "Tourbillon rouge") { | ||||
|       message +=  "Vous êtes emporté par le Tourbillon..."; | ||||
|       state = "tourbillonrouge"; | ||||
|        | ||||
|     } else if (rencontre.name == "Rêve de Dragon") { | ||||
|       message += "Le Rêve de Dragon tourne au cauchemar !" | ||||
|       message += actor.appliquerReveDeDragon(rolled, rencontre.force); | ||||
|     } | ||||
|     return { message: message, state: state }; | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static getTMRAleatoire() | ||||
|   static getTMRAleatoire(terrain=undefined) | ||||
|   { | ||||
|     if (terrain) { | ||||
|       let list = TMRUtility.getListTMR(terrain); | ||||
|       let index = new Roll("1d" + list.length).evaluate().total - 1; | ||||
|       return list[index];   | ||||
|     } | ||||
|     let num = new Roll("1d15").roll().total; | ||||
|     let letter, letterValue; | ||||
|     if ( num == 15) { | ||||
| @@ -575,7 +399,8 @@ export class TMRUtility  { | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static _checkTMRCoord( x, y ) { | ||||
|     if (x >= 0 && x < 13 && y >= 0 && y < 15  ) return true; | ||||
|     if (x >= 0 && x < 13 && y >= 0 && y < 14  ) return true; | ||||
|     if (x >= 0 && x < 13 && x%2 == 0 && y == 14  ) return true; | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
| @@ -592,10 +417,10 @@ export class TMRUtility  { | ||||
|   static getSortReserveList( reserveList, coordTMR ) { | ||||
|     // TODO : Gérer les têtes spéciales réserve! | ||||
|     let sortReserveList | ||||
|     let tmrDescr = this.getTMRDescription(coordTMR); | ||||
|     let tmrDescr = this.getTMR(coordTMR); | ||||
|     //console.log("Sort réserve : ", tmrDescr); | ||||
|     if ( tmrDescr.type == 'fleuve') { // Gestion de la reserve en Fleuve | ||||
|       sortReserveList = reserveList.filter(it => TMRUtility.getTMRDescription(it.coord).type == 'fleuve' ); | ||||
|       sortReserveList = reserveList.filter(it => TMRUtility.getTMR(it.coord).type == 'fleuve' ); | ||||
|     } else { // Reserve sur un case "normale" | ||||
|     sortReserveList = reserveList.filter(it => it.coord == coordTMR); | ||||
|     } | ||||
| @@ -607,6 +432,10 @@ export class TMRUtility  { | ||||
|   /** Returns a list of case inside a given distance | ||||
|    *  | ||||
|    */ | ||||
|   static getTMRPortee(coord, portee) { | ||||
|     return TMRUtility.getTMRArea(coord, portee, tmrConstants); | ||||
|   } | ||||
|  | ||||
|   static getTMRArea( coord, distance, tmrConstants ) { | ||||
|     let pos = this.convertToCellCoord( coord );     | ||||
|     let posPic = this.computeRealPictureCoordinates( pos, tmrConstants ); | ||||
|   | ||||
| @@ -431,6 +431,14 @@ table {border: 1px solid #7a7971;} | ||||
|   padding: 5px; | ||||
| } | ||||
|  | ||||
| .poesie-extrait { | ||||
|   font-size: 0.85rem; | ||||
|   font-style: italic; | ||||
| } | ||||
| .poesie-reference{ | ||||
|   font-size: 0.70rem; | ||||
|   text-align: right; | ||||
| } | ||||
| /* ======================================== */ | ||||
| /* Sheet  */ | ||||
| .window-app.sheet .window-content .sheet-header{ | ||||
|   | ||||
							
								
								
									
										16
									
								
								templates/chat-rencontre-tmr.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								templates/chat-rencontre-tmr.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| <img class="chat-icon" src="{{competence.img}}" alt="{{competence.name}}"/> | ||||
| <h4 data-categorie="tmr" data-actor-id="{{actor._id}}" data-rencontre-round="{{nbRounds}}"> | ||||
|   {{alias}} rencontre {{#if (eq genre 'f')}}une{{else}}un{{/if}} {{rencontre.name}} de force {{rencontre.force}} | ||||
| </h4> | ||||
| {{> "systems/foundryvtt-reve-de-dragon/templates/chat-infojet.html"}} | ||||
| <hr> | ||||
| <span> | ||||
|   {{message}} | ||||
| </span> | ||||
| {{#if poesie}} | ||||
| <hr> | ||||
| <span class="poesie-extrait"> | ||||
|   {{{poesie.extrait}}} | ||||
|   <p class="poesie-reference">{{poesie.reference}}</p> | ||||
| </span> | ||||
| {{/if}} | ||||
		Reference in New Issue
	
	Block a user