Merge branch 'rencontres' into 'v1.3'

Rencontres

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!142
This commit is contained in:
Leratier Bretonnien 2021-02-03 19:58:11 +00:00
commit 2fd2de29ad
10 changed files with 874 additions and 520 deletions

View File

@ -473,9 +473,15 @@ export class RdDActor extends Actor {
await this._recupererEthylisme(message); await this._recupererEthylisme(message);
await this.recupererFatigue(message); await this.recupererFatigue(message);
await this.recuperationReve(message); await this.recuperationReve(message);
if (this.isDonDoubleReve()) {
await this.recuperationReve(message);
}
} }
ChatMessage.create(message); ChatMessage.create(message);
} }
isDonDoubleReve() {
return this.data.items.find(item => item.type == 'tete' && item.name == 'Don de double-rêve');
}
/* -------------------------------------------- */ /* -------------------------------------------- */
async _recupererEthylisme(message) { async _recupererEthylisme(message) {
@ -549,7 +555,7 @@ export class RdDActor extends Actor {
if (deRecuperation >= 7) { if (deRecuperation >= 7) {
// Rêve de Dragon ! // Rêve de Dragon !
message.content += `<br>Vous faites un <strong>Rêve de Dragon</strong> de ${deRecuperation} Points de rêve`; message.content += `<br>Vous faites un <strong>Rêve de Dragon</strong> de ${deRecuperation} Points de rêve`;
message.content += await this.combattreReveDeDragon(deRecuperation); /*message.content += */await this.combattreReveDeDragon(deRecuperation);
} }
else { else {
message.content += `<br>Vous récupérez ${deRecuperation} Points de rêve`; message.content += `<br>Vous récupérez ${deRecuperation} Points de rêve`;
@ -599,7 +605,11 @@ export class RdDActor extends Actor {
if (roll.isETotal) { if (roll.isETotal) {
message += "<br>A cause de votre échec total, vous subissez une deuxième Queue de Dragon: " + await this.ajouterQueue(); 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
});
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -1019,7 +1029,7 @@ export class RdDActor extends Actor {
await this.createOwnedItem(souffle); await this.createOwnedItem(souffle);
if (options.chat) { if (options.chat) {
ChatMessage.create({ ChatMessage.create({
whisper: ChatMessage.getWhisperRecipients([ "GM", game.user.name] ), whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name),
content: this.name + " subit un Souffle de Dragon : " + souffle.name content: this.name + " subit un Souffle de Dragon : " + souffle.name
}); });
} }
@ -1041,7 +1051,7 @@ export class RdDActor extends Actor {
await this.createOwnedItem(queue); await this.createOwnedItem(queue);
if (options.chat) { if (options.chat) {
ChatMessage.create({ ChatMessage.create({
whisper: ChatMessage.getWhisperRecipients([ "GM", game.user.name] ), whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name),
content: this.name + " subit une Queue de Dragon : " + queue.name content: this.name + " subit une Queue de Dragon : " + queue.name
}); });
} }
@ -1075,7 +1085,7 @@ export class RdDActor extends Actor {
} }
if (content) { if (content) {
ChatMessage.create({ ChatMessage.create({
whisper: ChatMessage.getWhisperRecipients([ "GM", game.user.name] ), whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name),
content: content content: content
}); });
} }
@ -1704,7 +1714,7 @@ export class RdDActor extends Actor {
competence: this.getBestDraconic(), competence: this.getBestDraconic(),
selectedSort: sortList[0], selectedSort: sortList[0],
coord: coord, coord: coord,
coordLabel: TMRUtility.getTMRDescription(coord).label, coordLabel: TMRUtility.getTMR(coord).label,
diffLibre: sortList[0].data.difficulte, // Per default at startup diffLibre: sortList[0].data.difficulte, // Per default at startup
coutreve: Array(20).fill().map((item, index) => 1 + index) coutreve: Array(20).fill().map((item, index) => 1 + index)
} }

View File

@ -9,6 +9,7 @@ import { RdDResolutionTable } from "./rdd-resolution-table.js";
import { RdDRollResolutionTable } from "./rdd-roll-resolution-table.js"; import { RdDRollResolutionTable } from "./rdd-roll-resolution-table.js";
import { RdDRollTables } from "./rdd-rolltables.js"; import { RdDRollTables } from "./rdd-rolltables.js";
import { RdDUtility } from "./rdd-utility.js"; import { RdDUtility } from "./rdd-utility.js";
import { TMRRencontres } from "./tmr-rencontres.js";
import { TMRUtility } from "./tmr-utility.js"; import { TMRUtility } from "./tmr-utility.js";
const rddRollNumeric = /(\d+)\s*([\+\-]?\d+)?\s*(s)?/; const rddRollNumeric = /(\d+)\s*([\+\-]?\d+)?\s*(s)?/;
@ -187,7 +188,7 @@ export class RdDCommands {
/* -------------------------------------------- */ /* -------------------------------------------- */
async getRencontreTMR(params) { async getRencontreTMR(params) {
if (params.length == 1 || params.length ==2) { if (params.length == 1 || params.length ==2) {
return TMRUtility.getRencontre(params[0], params[1]) return TMRRencontres.rollRencontre(params[0], params[1])
} }
else { else {
return false; return false;

View File

@ -26,6 +26,7 @@ import { RdDItemCompetence } from "./item-competence.js";
import { StatusEffects } from "./status-effects.js"; import { StatusEffects } from "./status-effects.js";
import { RddCompendiumOrganiser } from "./rdd-compendium-organiser.js"; import { RddCompendiumOrganiser } from "./rdd-compendium-organiser.js";
import { ReglesOptionelles } from "./regles-optionelles.js"; import { ReglesOptionelles } from "./regles-optionelles.js";
import { TMRRencontres } from "./tmr-rencontres.js";
/* -------------------------------------------- */ /* -------------------------------------------- */
/* Foundry VTT Initialization */ /* Foundry VTT Initialization */
@ -108,7 +109,10 @@ Hooks.once("init", async function () {
// preload handlebars templates // preload handlebars templates
RdDUtility.preloadHandlebarsTemplates(); RdDUtility.preloadHandlebarsTemplates();
// Create useful storage space // Create useful storage space
game.system.rdd = { TMRUtility: TMRUtility } game.system.rdd = {
TMRUtility,
RdDUtility
}
/* -------------------------------------------- */ /* -------------------------------------------- */
game.settings.register("foundryvtt-reve-de-dragon", "accorder-entite-cauchemar", { game.settings.register("foundryvtt-reve-de-dragon", "accorder-entite-cauchemar", {
@ -225,6 +229,8 @@ Hooks.once("init", async function () {
RdDActor.init(); RdDActor.init();
RddCompendiumOrganiser.init(); RddCompendiumOrganiser.init();
ReglesOptionelles.init(); ReglesOptionelles.init();
TMRUtility.init();
TMRRencontres.init();
}); });
/* -------------------------------------------- */ /* -------------------------------------------- */

View File

@ -2,21 +2,14 @@
* Extend the base Dialog entity by defining a custom window to perform spell. * Extend the base Dialog entity by defining a custom window to perform spell.
* @extends {Dialog} * @extends {Dialog}
*/ */
import { RollDataAjustements } from "./rolldata-ajustements.js";
import { RdDUtility } from "./rdd-utility.js"; import { RdDUtility } from "./rdd-utility.js";
import { TMRUtility } from "./tmr-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 { RdDResolutionTable } from "./rdd-resolution-table.js";
import { RdDTMRRencontreDialog } from "./rdd-tmr-rencontre-dialog.js"; import { RdDTMRRencontreDialog } from "./rdd-tmr-rencontre-dialog.js";
import { TMRRencontres } from "./tmr-rencontres.js";
/* -------------------------------------------- */ import { ChatUtility } from "./chat-utility.js";
const tmrConstants = {
col1_y: 30,
col2_y: 55,
cellw: 55,
cellh: 55,
gridx: 28,
gridy: 28
}
/* -------------------------------------------- */ /* -------------------------------------------- */
export class RdDTMRDialog extends Dialog { export class RdDTMRDialog extends Dialog {
@ -120,16 +113,25 @@ export class RdDTMRDialog extends Dialog {
this.close(); this.close();
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async refouler(data) { async refouler() {
this._tellToGM(this.actor.name + " a refoulé : " + this.currentRencontre.name); this._tellToGM(this.actor.name + " a refoulé : " + this.currentRencontre.name);
await this.actor.deleteTMRRencontreAtPosition(); // Remove the stored rencontre if necessary 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(); this.updatePreviousRencontres();
console.log("-> refouler", this.currentRencontre) console.log("-> refouler", this.currentRencontre)
this.updateValuesDisplay(); this.updateValuesDisplay();
this.nettoyerRencontre(); this.nettoyerRencontre();
} }
/* -------------------------------------------- */
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) { colorierZoneRencontre(locList) {
this.currentRencontre.graphics = []; // Keep track of rectangles to delete it this.currentRencontre.graphics = []; // Keep track of rectangles to delete it
@ -148,132 +150,118 @@ export class RdDTMRDialog extends Dialog {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async gererTourbillon( value ) { // garder la trace de l'état en cours
this.nbFatigue += 1; setStateRencontre(state) {
await this.actor.reveActuelIncDec( -value ); this.rencontreState = state;
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);
} }
/* -------------------------------------------- */ async choisirCasePortee(coord, portee) {
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);
}
/* -------------------------------------------- */
/** 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 // Récupère la liste des cases à portées
locList = TMRUtility.getTMRArea(this.actor.data.data.reve.tmrpos.coord, this.currentRencontre.force, tmrConstants ); let locList = TMRUtility.getTMRPortee(coord, portee);
} 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); this.colorierZoneRencontre(locList);
} }
async choisirCaseType(type) {
const locList = TMRUtility.getListCoordTMR(type);
this.colorierZoneRencontre(locList);
}
/* -------------------------------------------- */ /* -------------------------------------------- */
checkQuitterTMR() { 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(); 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._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 ) { if (this.actor.getReveActuel() == 0) {
this._tellToGM("Vous n'avez plus de Points de Vie : vous quittez les Terres médianes !"); this._tellToGM("Vos Points de Rêve sont à 0 : vous quittez les Terres médianes !");
this.close(); this.quitterLesTMRInconscient();
return true;
} }
return false;
}
async quitterLesTMRInconscient() {
if (this.currentRencontre?.isPersistant) {
await this.refouler();
}
this.close();
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async maitriser(data) { async maitriser() {
this.actor.deleteTMRRencontreAtPosition(); // Remove the stored rencontre if necessary this.actor.deleteTMRRencontreAtPosition();
this.updatePreviousRencontres(); 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 rencontreData = {
let message = "<br><strong>Test : Rêve actuel / " + draconic.name + " / " + this.currentRencontre.name + "</strong>" + "<br>" actor: this.actor,
+ RdDResolutionTable.explain(rolled); alias: this.actor.name,
reveDepart: this.actor.getReveActuel(),
let rencontreData competence: this.actor.getBestDraconic(),
if (rolled.isEchec) { rencontre: this.currentRencontre,
rencontreData = await TMRUtility.processRencontreEchec(this.actor, this.currentRencontre, rolled, this); nbRounds: 1,
message += rencontreData.message; tmr: TMRUtility.getTMR(this.actor.data.data.reve.tmrpos.coord)
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);
} }
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(); 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(); _deleteTmrMessages(actor, nbRounds = -1) {
if ( this.rencontreState == 'reflet' || this.rencontreState == 'tourbillonblanc' || this.rencontreState == 'tourbillonnoir' ) if (nbRounds < 0) {
this.maitriser(); 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,22 +294,20 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */ /* -------------------------------------------- */
async _jetDeRencontre(coordTMR, cellDescr) { 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()) { if (TMRUtility.isForceRencontre()) {
return await TMRUtility.rencontreTMRRoll(coordTMR, cellDescr); return await TMRUtility.rencontreTMRRoll(coordTMR, cellDescr);
} }
let rencontre = this.rencontresExistantes.find(prev => prev.coord == coordTMR);
if (rencontre) {
return 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 + ")");
}
/* -------------------------------------------- */ /* -------------------------------------------- */
updateValuesDisplay() { updateValuesDisplay() {
@ -329,7 +315,7 @@ export class RdDTMRDialog extends Dialog {
ptsreve.innerHTML = this.actor.data.data.reve.reve.value; ptsreve.innerHTML = this.actor.data.data.reve.reve.value;
let tmrpos = document.getElementById("tmr-pos"); 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 + ")"; tmrpos.innerHTML = this.actor.data.data.reve.tmrpos.coord + " (" + tmr.label + ")";
let etat = document.getElementById("tmr-etatgeneral-value"); let etat = document.getElementById("tmr-etatgeneral-value");
@ -354,7 +340,8 @@ export class RdDTMRDialog extends Dialog {
await this.actor.update({ "data.reve.tmrpos": tmrPos }); await this.actor.update({ "data.reve.tmrpos": tmrPos });
ChatMessage.create({ ChatMessage.create({
content: "Vous êtes rentré sur un Trou Noir : ré-insertion aléatoire.", content: "Vous êtes rentré sur un Trou Noir : ré-insertion aléatoire.",
whisper: ChatMessage.getWhisperRecipients(game.user.name) } ); whisper: ChatMessage.getWhisperRecipients(game.user.name)
});
} }
} }
} }
@ -378,7 +365,7 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */ /* -------------------------------------------- */
async manageCaseHumide(cellDescr, coordTMR) { async manageCaseHumide(cellDescr, coordTMR) {
if (this.viewOnly) { if (this.viewOnly || this.currentRencontre) {
return; return;
} }
let isHumide = this.actor.checkIsAdditionnalHumide(cellDescr, coordTMR); let isHumide = this.actor.checkIsAdditionnalHumide(cellDescr, coordTMR);
@ -386,7 +373,8 @@ export class RdDTMRDialog extends Dialog {
if (this.isCaseMaitrisee(coordTMR)) { if (this.isCaseMaitrisee(coordTMR)) {
ChatMessage.create({ ChatMessage.create({
content: "Cette case humide est déja maitrisée grâce à votre Tête <strong>Quête des Eaux</strong>", 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; return;
} }
// TODO: permettre de choisir la voie de draconic? // TODO: permettre de choisir la voie de draconic?
@ -469,7 +457,8 @@ export class RdDTMRDialog extends Dialog {
msg += "</ol>"; msg += "</ol>";
ChatMessage.create({ ChatMessage.create({
content: msg, content: msg,
whisper: ChatMessage.getWhisperRecipients(game.user.name) } ); whisper: ChatMessage.getWhisperRecipients(game.user.name)
});
} else { } else {
await this.processSortReserve(sortReserveList[0]); await this.processSortReserve(sortReserveList[0]);
} }
@ -485,7 +474,8 @@ export class RdDTMRDialog extends Dialog {
} else { } else {
ChatMessage.create({ ChatMessage.create({
content: "Une erreur est survenue : impossible de récupérer le sort en réserve demandé.", 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)
});
} }
} }
@ -495,7 +485,7 @@ export class RdDTMRDialog extends Dialog {
this.updateSortReserve(); this.updateSortReserve();
console.log("declencheSortEnReserve", sortReserve) console.log("declencheSortEnReserve", sortReserve)
const declenchementSort = "Vous avez déclenché le sort <strong>" + sortReserve.sort.name 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"; + ") avec " + sortReserve.sort.data.ptreve_reel + " points de Rêve";
this._tellToGM(declenchementSort); this._tellToGM(declenchementSort);
this.close(); this.close();
@ -515,15 +505,14 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */ /* -------------------------------------------- */
processClickPostRencontre(coord) { processClickPostRencontre(coord) {
let deplacementType = "erreur";
if (this.rencontreState == 'passeur' || this.rencontreState == 'messager' || this.rencontreState == 'changeur') { if (this.rencontreState == 'passeur' || this.rencontreState == 'messager' || this.rencontreState == 'changeur') {
console.log("Searching", this.currentRencontre.locList, coord); console.log("Searching", this.currentRencontre.locList, coord);
let isInArea = this.currentRencontre.locList.find(locCoord => locCoord == coord); let isInArea = this.currentRencontre.locList.find(locCoord => locCoord == coord);
if (isInArea) { // OK ! if (isInArea) { // OK !
deplacementType = (this.rencontreState == 'messager') ? 'messager' : 'saut'; return (this.rencontreState == 'messager') ? 'messager' : 'saut';
} }
} }
return deplacementType; return "erreur";
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -540,7 +529,7 @@ export class RdDTMRDialog extends Dialog {
checkConnaissanceFleuve(currentTMR, nextTMR) { checkConnaissanceFleuve(currentTMR, nextTMR) {
if (this.actor.isConnaissanceFleuve()) { if (this.actor.isConnaissanceFleuve()) {
//console.log(currentTMR, nextTMR ); //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; return true;
} }
} }
@ -577,7 +566,7 @@ export class RdDTMRDialog extends Dialog {
if (deplacementType == 'normal' || deplacementType == 'saut') { if (deplacementType == 'normal' || deplacementType == 'saut') {
if (myself.currentRencontre != 'normal') if (myself.currentRencontre != 'normal')
myself.nettoyerRencontre(); 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 await myself.manageCaseSpeciale(cellDescr, coordTMR); // Gestion cases spéciales type Trou noir, etc
@ -629,8 +618,7 @@ export class RdDTMRDialog extends Dialog {
async forceDemiRevePosition(coordTMR) { async forceDemiRevePosition(coordTMR) {
await this.actor.updateCoordTMR(coordTMR); await this.actor.updateCoordTMR(coordTMR);
this._updateDemiReve(this); this._updateDemiReve(this);
let cellDescr = TMRUtility.getTMRDescription(coordTMR); let cellDescr = TMRUtility.getTMR(coordTMR);
await this.manageRencontre(coordTMR, cellDescr);
this.manageCaseHumide(cellDescr, coordTMR); this.manageCaseHumide(cellDescr, coordTMR);
await this.declencheSortEnReserve(coordTMR); await this.declencheSortEnReserve(coordTMR);
} }
@ -699,7 +687,7 @@ export class RdDTMRDialog extends Dialog {
// Le reste... // Le reste...
this.updateValuesDisplay(); this.updateValuesDisplay();
let coordTMR = this.actor.data.data.reve.tmrpos.coord; let coordTMR = this.actor.data.data.reve.tmrpos.coord;
let cellDescr = TMRUtility.getTMRDescription(coordTMR); let cellDescr = TMRUtility.getTMR(coordTMR);
await this.manageRencontre(coordTMR, cellDescr); await this.manageRencontre(coordTMR, cellDescr);
this.manageCaseHumide(cellDescr, coordTMR); this.manageCaseHumide(cellDescr, coordTMR);
// Mise à jour du nb de cases de Fatigue // Mise à jour du nb de cases de Fatigue

View File

@ -2,10 +2,10 @@
export class RdDTMRRencontreDialog extends Dialog { export class RdDTMRRencontreDialog extends Dialog {
/* -------------------------------------------- */ /* -------------------------------------------- */
constructor(html, tmrApp, rencontreData) { constructor(html, tmrApp, rencontre) {
const dialogConf = { const dialogConf = {
title: "Rencontre en TMR!", 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: { buttons: {
derober: { icon: '<i class="fas fa-check"></i>', label: "Se dérober", callback: () => { this.toClose = true; this.tmrApp.derober() } }, 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() } }, refouler: { icon: '<i class="fas fa-check"></i>', label: "Refouler", callback: () => { this.toClose = true; this.tmrApp.refouler() } },
@ -13,6 +13,9 @@ export class RdDTMRRencontreDialog extends Dialog {
}, },
default: "derober" 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 = { const dialogOptions = {
classes: ["tmrrencdialog"], classes: ["tmrrencdialog"],
@ -22,7 +25,7 @@ export class RdDTMRRencontreDialog extends Dialog {
super(dialogConf, dialogOptions); super(dialogConf, dialogOptions);
this.toClose = false; this.toClose = false;
this.rencontreData = duplicate(rencontreData); this.rencontreData = duplicate(rencontre);
this.tmrApp = tmrApp; this.tmrApp = tmrApp;
this.tmrApp.minimize(); this.tmrApp.minimize();
} }

View File

@ -114,6 +114,12 @@ export const referenceAjustements = {
bonusCase: { bonusCase: {
isUsed: (rollData, actor) => rollData.selectedSort && rollData.coord, isUsed: (rollData, actor) => rollData.selectedSort && rollData.coord,
getDescr: (rollData, actor) => rollData.selectedSort && rollData.coord ? `Bonus de case: ${RdDItemSort.getCaseBonus(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
View 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 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>&laquo; 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 ? &raquo;`}
},
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 je rêve, et la gourde
<br> 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>-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!`;
}
}

View File

@ -1,4 +1,5 @@
import { DeDraconique } from "./de-draconique.js"; import { DeDraconique } from "./de-draconique.js";
import { TMRRencontres } from "./tmr-rencontres.js";
import { Grammar } from "./grammar.js"; import { Grammar } from "./grammar.js";
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -208,71 +209,55 @@ const TMRMapping = {
M15: { type: "cite", label: "Cité de Klana"} M15: { type: "cite", label: "Cité de Klana"}
} }
/* -------------------------------------------- */ export const TMRType = {
const rencontresSpeciale = [ cite: "cité",
{name:"Mangeur de Rêve", data: { force: "1d6", ignorer: false, derober: true, refoulement: 2, quitterTMR: false } }, sanctuaire: "sanctuaire",
{name:"Mangeur de Rêve", data: { force: "2d6", ignorer: false, derober: true, refoulement: 2, quitterTMR: false } }, plaines: "plaines",
{name:"Reflet d'ancien Rêve", data: { force: "2d6+4", ignorer: false, derober: true, refoulement: 2, quitterTMR: false } }, pont: "pont",
{name:"Tourbillon blanc", data: { force: "2d6+4", ignorer: false, derober: true, refoulement: 2, quitterTMR: false } }, collines: "collines",
{name:"Tourbillon noir", data: { force: "2d8+4", ignorer: false, derober: true, refoulement: 2, quitterTMR: false } }, foret: "forêt",
{name:"Passeur fou", data: { force: "2d8", ignorer: false, derober: true, refoulement: 2, quitterTMR: false } }, monts: "monts",
{name:"Tourbillon rouge", data: { force: "2d8", ignorer: false, derober: true, refoulement: 3, quitterTMR: false } } desert: "désert",
] fleuve: "fleuve",
lac: "lac",
/* -------------------------------------------- */ marais: "marais",
const rencontresTable = [ gouffre: "gouffre",
{name:"Messagers des Rêves", data: { force: "2d4", ignorer: true, derober: true, refoulement: 1, quitterTMR: false, necropole: "nécropole",
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", desolation: "désolation"
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" } }
]
/* -------------------------------------------- */ /* -------------------------------------------- */
const caseSpecificModes = [ "attache", "trounoir", "debordement", "reserve_extensible", "maitrisee" ]; const caseSpecificModes = [ "attache", "trounoir", "debordement", "reserve_extensible", "maitrisee" ];
/* -------------------------------------------- */ /* -------------------------------------------- */
const tmrMovePattern = const tmrRandomMovePatten =
[ { name: 'top', x: 0, y: -1 }, [ { name: 'top', x: 0, y: -1 },
{ name: 'topright', x: 1, y: -1 }, { name: 'topright', x: 1, y: -1 },
{ name: 'left', x: 1, y: 'alt' },
{ name: 'botright', x: 1, y: 1 }, { name: 'botright', x: 1, y: 1 },
{ name: 'bot', x: 0, y: 1 }, { name: 'bot', x: 0, y: 1 },
{ name: 'botleft', x: -1, y: 1 }, { name: 'botleft', x: -1, y: 1 },
{ name: 'left', x: -1, y: 'alt' },
{ name: 'topleft', x: -1, y: -1 } { 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 { export class TMRUtility {
static init() {
for (let coord in TMRMapping) {
TMRMapping[coord].coord = coord;
}
}
/* -------------------------------------------- */ /* -------------------------------------------- */
static convertToTMRCoord( x, y ) static convertToTMRCoord( x, y )
@ -303,261 +288,100 @@ export class TMRUtility {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static getTMRDescription( coordTMR) static getTMR( coordTMR)
{ {
return TMRMapping[coordTMR]; return TMRMapping[coordTMR];
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
/** Some debug functions */ /** Some debug functions */
static setForceRencontre( id, force ) { static async setForceRencontre( index, force = undefined ) {
this.forceRencontre = { id: id, force: force} 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 clearForceRencontre( id, force ) {
this.forceRencontre = undefined
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static isForceRencontre() { static isForceRencontre() {
return this.forceRencontre return this.prochaineRencontre
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static getDirectionPattern() { static getDirectionPattern() {
let index = new Roll("1d"+tmrMovePattern.length+" -1").roll().total; let roll = new Roll("1d"+tmrRandomMovePatten.length).evaluate().total;
return tmrMovePattern[index]; 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++) { for (let i=0; i <nTime; i++) {
let currentPosXY = TMRUtility.convertToCellCoord(pos); let currentPosXY = TMRUtility.convertToCellCoord(coord);
currentPosXY.x = currentPosXY.x + pattern.x; currentPosXY.x = currentPosXY.x + direction.x;
if (pattern.y == 'alt' ) { // Alternate version currentPosXY.y = currentPosXY.y + direction.y;
pattern.y += (pattern.x % 2 == 0 ) ? -1 : 1;
} else {
currentPosXY.y = currentPosXY.y + pattern.y;
}
if ( this._checkTMRCoord(currentPosXY.x, currentPosXY.y) ) { // Sortie de carte ! Ré-insertion aléatoire 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 { } 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; let rencontre;
if ( isSpecial ) { if ( this.prochaineRencontre ) {
let index = new Roll("1d7").roll().total; rencontre = this.prochaineRencontre;
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);
rencontre.coord = coordTMR; rencontre.coord = coordTMR;
rencontre.nbCases = 0; // Utilisé pour les Tourbillons this.prochaineRencontre = undefined;
rencontre.isSpecial = isSpecial; // Garder l'information
} }
else if ( isMauvaise ) {
rencontre = await TMRRencontres.getMauvaiseRencontre();
} else {
rencontre = await TMRRencontres.getRencontreAleatoire(cellDescr.type);
}
rencontre.coord = coordTMR;
return rencontre; return rencontre;
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static async rencontreTMRTypeCase(typeTMR, roll=undefined) { static getListTMR(terrain) {
if (!roll) { let list = [];
//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;
}
}
}
/* -------------------------------------------- */
/**
* 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) { for (let index in TMRMapping) {
let caseTMR = TMRMapping[index]; if (TMRMapping[index].type == terrain){
if (caseTMR.type == descr.type) list.push(TMRMapping[index]);
typeList.push(index)
} }
return typeList; }
return list;
}
static getListCoordTMR(terrain) {
return this.getListTMR(terrain).map(it=>it.coord);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static async evaluerForceRencontre(rencontre) { static getTMRAleatoire(terrain=undefined)
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()
{ {
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 num = new Roll("1d15").roll().total;
let letter, letterValue; let letter, letterValue;
if ( num == 15) { if ( num == 15) {
@ -575,7 +399,8 @@ export class TMRUtility {
/* -------------------------------------------- */ /* -------------------------------------------- */
static _checkTMRCoord( x, y ) { 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; return false;
} }
@ -592,10 +417,10 @@ export class TMRUtility {
static getSortReserveList( reserveList, coordTMR ) { static getSortReserveList( reserveList, coordTMR ) {
// TODO : Gérer les têtes spéciales réserve! // TODO : Gérer les têtes spéciales réserve!
let sortReserveList let sortReserveList
let tmrDescr = this.getTMRDescription(coordTMR); let tmrDescr = this.getTMR(coordTMR);
//console.log("Sort réserve : ", tmrDescr); //console.log("Sort réserve : ", tmrDescr);
if ( tmrDescr.type == 'fleuve') { // Gestion de la reserve en Fleuve 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" } else { // Reserve sur un case "normale"
sortReserveList = reserveList.filter(it => it.coord == coordTMR); sortReserveList = reserveList.filter(it => it.coord == coordTMR);
} }
@ -607,6 +432,10 @@ export class TMRUtility {
/** Returns a list of case inside a given distance /** Returns a list of case inside a given distance
* *
*/ */
static getTMRPortee(coord, portee) {
return TMRUtility.getTMRArea(coord, portee, tmrConstants);
}
static getTMRArea( coord, distance, tmrConstants ) { static getTMRArea( coord, distance, tmrConstants ) {
let pos = this.convertToCellCoord( coord ); let pos = this.convertToCellCoord( coord );
let posPic = this.computeRealPictureCoordinates( pos, tmrConstants ); let posPic = this.computeRealPictureCoordinates( pos, tmrConstants );

View File

@ -431,6 +431,14 @@ table {border: 1px solid #7a7971;}
padding: 5px; padding: 5px;
} }
.poesie-extrait {
font-size: 0.85rem;
font-style: italic;
}
.poesie-reference{
font-size: 0.70rem;
text-align: right;
}
/* ======================================== */ /* ======================================== */
/* Sheet */ /* Sheet */
.window-app.sheet .window-content .sheet-header{ .window-app.sheet .window-content .sheet-header{

View 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}}