Merge branch 'travail-cases-humides' into 'v1.3'

Fixes nombreux sur tmr autour des cases humides

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!144
This commit is contained in:
Leratier Bretonnien 2021-02-05 22:10:50 +00:00
commit 48688fc453
15 changed files with 351 additions and 256 deletions

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

60
icons/creatures/mule.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

@ -9,6 +9,7 @@ import { RdDItem } from "./item.js";
import { RdDItemArme } from "./item-arme.js";
import { RdDItemCompetence } from "./item-competence.js";
import { RdDBonus } from "./rdd-bonus.js";
import { Misc } from "./misc.js";
/* -------------------------------------------- */
export class RdDActorSheet extends ActorSheet {
@ -38,11 +39,11 @@ export class RdDActorSheet extends ActorSheet {
data.data.showCompNiveauBase = this.options.showCompNiveauBase;
data.data.montrerArchetype = this.options.montrerArchetype;
data.itemsByType = RdDItem.buildItemsClassification(data.items);
data.itemsByType = Misc.classify(data.items);
// Competence per category
data.data.competenceXPTotal = 0;
data.competenceByCategory = RdDItem.classify(
data.competenceByCategory = Misc.classify(
data.itemsByType.competence,
item => item.data.categorie,
item => {

View File

@ -6,6 +6,7 @@
import { RdDUtility } from "./rdd-utility.js";
import { HtmlUtility } from "./html-utility.js";
import { RdDItem } from "./item.js";
import { Misc } from "./misc.js";
/* -------------------------------------------- */
export class RdDActorVehiculeSheet extends ActorSheet {
@ -36,7 +37,7 @@ export class RdDActorVehiculeSheet extends ActorSheet {
getData() {
let data = super.getData();
data.itemsByType = RdDItem.buildItemsClassification(data.items);
data.itemsByType = Misc.classify(data.items);
RdDUtility.filterItemsPerTypeForSheet(data);
RdDUtility.buildArbreDeConteneur(this, data);

View File

@ -1,5 +1,5 @@
import { RdDUtility } from "./rdd-utility.js";
import { TMRUtility } from "./tmr-utility.js";
import { TMRType, TMRUtility } from "./tmr-utility.js";
import { RdDRollDialogEthylisme } from "./rdd-roll-ethylisme.js";
import { RdDRoll } from "./rdd-roll.js";
import { RdDTMRDialog } from "./rdd-tmr-dialog.js";
@ -21,6 +21,7 @@ import { RdDAlchimie } from "./rdd-alchimie.js";
import { StatusEffects } from "./status-effects.js";
import { RdDItemCompetenceCreature } from "./item-competencecreature.js";
import { ReglesOptionelles } from "./regles-optionelles.js";
import { RdDItem } from "./item.js";
/* -------------------------------------------- */
@ -1837,22 +1838,25 @@ export class RdDActor extends Actor {
}
/* -------------------------------------------- */
checkIsAdditionnalHumide(cellDescr, coordTMR) {
let pontHumide = this.data.items.find(item => item.type == 'souffle' && item.name.toLowerCase().includes(' des ponts'));
if (pontHumide && cellDescr.type == 'pont') {
isCaseHumideAdditionelle(tmr) {
if (tmr.type == 'pont' && this.data.items.find(it => RdDItem.isSoufflePontImpraticable(it))) {
ChatMessage.create({
content: "Vous êtes sous le coup d'une Impraticabilité des Ponts : ils doivent être maîtrisés comme des cases humides.",
content: tmr.label +": Vous êtes sous le coup d'une Impraticabilité des Ponts : ce pont doit être maîtrisé comme une case humide.",
whisper: ChatMessage.getWhisperRecipients(game.user.name)
});
return true;
}
// Débordement ?
let debordementList = this.data.items.filter(item => item.type == 'casetmr' && item.data.specific == 'debordement');
for (let caseTMR of debordementList) {
if (caseTMR.data.coord == coordTMR)
let isTmrInondee = this.data.items.filter(it => RdDItem.isCaseTMR(it))
.filter(it => it.data.coord == tmr.coord)
.find(it => RdDItem.isCaseInnondee(it));
if (isTmrInondee) {
ChatMessage.create({
content: tmr.label +": cette case est inondée, elle doit être maîtrisée comme une case humide.",
whisper: ChatMessage.getWhisperRecipients(game.user.name)
});
return true;
}
return false;
}
@ -2398,14 +2402,14 @@ export class RdDActor extends Actor {
}
/* -------------------------------------------- */
checkMonteeLaborieuse() { // Return +1 si la queue Montée Laborieuse est présente, 0 sinon
let monteLaborieuse = this.data.items.find(item => (item.type == 'queue' || item.type == 'souffle') && item.name.toLowerCase().includes('montée laborieuse'));
if (monteLaborieuse) {
checkMonteeLaborieuse() { // Return +1 par queue/ombre/souffle Montée Laborieuse présente
let countMonteLaborieuse = this.data.items.filter(item => (item.type == 'queue' || item.type == 'ombre' || item.type == 'souffle') && item.name.toLowerCase().includes('montée laborieuse')).length;
if (countMonteLaborieuse) {
ChatMessage.create({
content: "Vous êtes sous le coup d'une Montée Laborieuse : vos montées en TMR coûtent 1 Point de Rêve de plus.",
content: `Vous êtes sous le coup d'une Montée Laborieuse : vos montées en TMR coûtent ${countMonteLaborieuse} Point de Rêve de plus.`,
whisper: ChatMessage.getWhisperRecipients(game.user.name)
});
return 1;
return countMonteLaborieuse;
}
return 0;
}
@ -2414,7 +2418,7 @@ export class RdDActor extends Actor {
refreshTMRView( tmrData ) {
console.log("REFRESH !!!!");
if ( this.currentTMR ) {
this.currentTMR.forceDemiRevePositionView(tmrData.tmrPos);
this.currentTMR.forceDemiRevePositionView();
}
}

View File

@ -29,7 +29,7 @@ export class RdDItemCompetenceCreature extends Item {
});
return arme;
}
console.error("RdDItem.toArme(", item, ") : impossible de transformer l'Item en arme");
console.error("RdDItemCompetenceCreature.toArme(", item, ") : impossible de transformer l'Item en arme");
return undefined;
}

View File

@ -1,28 +1,18 @@
import { Misc } from "./misc.js";
/* -------------------------------------------- */
export class RdDItem {
/* -------------------------------------------- */
static buildItemsClassification(items) {
return RdDItem.classify(items, it => it.type)
static isSoufflePontImpraticable(item) {
return item.type == 'souffle' && item.name.toLowerCase().includes(' des ponts');
}
static classify(items, classifier = it => it.type, transform = it => it) {
let itemsBy = {};
RdDItem.classifyInto(itemsBy, items, classifier, transform);
return itemsBy;
static isCaseInnondee(item) {
return RdDItem.isCaseTMR(item) && item.data.specific == 'debordement';
}
static classifyInto(itemsBy, items, classifier = it => it.type, transform = it => it) {
for (const item of items) {
const classification = classifier(item);
let list = itemsBy[classification];
if (!list) {
list = [];
itemsBy[classification] = list;
}
list.push(transform(item));
}
}
static isCaseTMR(item) {
return item.type == 'casetmr';
}
}

View File

@ -1,5 +1,4 @@
/**
* This class is intended as a placeholder for utility methods unrelated
* to actual classes of the game system or of FoundryVTT
@ -41,4 +40,23 @@ export class Misc {
default: return '1/' + diviseur;
}
}
static classify(items, classifier = it => it.type, transform = it => it) {
let itemsBy = {};
Misc.classifyInto(itemsBy, items, classifier, transform);
return itemsBy;
}
static classifyInto(itemsBy, items, classifier = it => it.type, transform = it => it) {
for (const item of items) {
const classification = classifier(item);
let list = itemsBy[classification];
if (!list) {
list = [];
itemsBy[classification] = list;
}
list.push(transform(item));
}
}
}

View File

@ -1,6 +1,5 @@
/* -------------------------------------------- */
import { ChatUtility } from "./chat-utility.js";
import { DeDraconique } from "./de-draconique.js";
import { RdDItemCompetence } from "./item-competence.js";
import { Misc } from "./misc.js";
@ -10,7 +9,7 @@ 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";
import { TMRType, TMRUtility } from "./tmr-utility.js";
const rddRollNumeric = /(\d+)\s*([\+\-]?\d+)?\s*(s)?/;
@ -28,15 +27,17 @@ export class RdDCommands {
rddCommands.registerCommand({ path: ["/table", "tete"], func: (content, msg, params) => RdDRollTables.getTete(), descr: "Tire une Tête de Dragon" });
rddCommands.registerCommand({ path: ["/table", "souffle"], func: (content, msg, params) => RdDRollTables.getSouffle(), descr: " Tire un Souffle de Dragon" });
rddCommands.registerCommand({ path: ["/table", "tarot"], func: (content, msg, params) => RdDRollTables.getTarot(), descr: "Tire une carte du Tarot Draconique" });
rddCommands.registerCommand({ path: ["/table", "tmr"], func: (content, msg, params) => TMRUtility.getTMRAleatoire(), descr: "Tire une case aléatoire des Terres médianes" });
rddCommands.registerCommand({ path: ["/tmra"], func: (content, msg, params) => TMRUtility.getTMRAleatoire(), descr: "Tire une case aléatoire des Terres médianes" });
rddCommands.registerCommand({
path: ["/tmra"], func: (content, msg, params) => rddCommands.getTMRAleatoire(msg, params),
descr: `Tire une case aléatoire des Terres médianes
<br><strong>/tmra forêt</strong> détermine une 'forêt' aléatoire
<br><strong>/tmra</strong> détermine une case aléatoire dans toutes les TMR` });
rddCommands.registerCommand({
path: ["/tmrr"], func: (content, msg, params) => rddCommands.getRencontreTMR(params),
descr: `
Exemple: <strong>/tmrr foret</strong> lance un d100 et détermine la rencontre correspondante en 'forêt'
Exemple: <strong>/tmrr forêt 47</strong> détermine la rencontre en 'forêt' pour un jet de dé de 47
`
descr: `Détermine une rencontre dans un type de case
<br><strong>/tmrr foret</strong> lance un d100 et détermine la rencontre correspondante en 'forêt'
<br><strong>/tmrr forêt 47</strong> détermine la rencontre en 'forêt' pour un jet de dé de 47`
});
rddCommands.registerCommand({
@ -59,8 +60,7 @@ export class RdDCommands {
<br><strong>/rdd 10 3</strong> effectue un jet 10 à +3
<br><strong>/rdd 10 +2</strong> effectue un jet 10 à +2
<br><strong>/rdd 15 -2</strong> effectue un jet 15 à -2
<br><strong>/rdd 15 0 s</strong> effectue un jet 15 à 0, avec significative requise
`
<br><strong>/rdd 15 0 s</strong> effectue un jet 15 à 0, avec significative requise`
});
rddCommands.registerCommand({ path: ["/ddr"], func: (content, msg, params) => rddCommands.rollDeDraconique(msg), descr: "Lance un Dé Draconique" });
@ -68,14 +68,12 @@ export class RdDCommands {
path: ["/payer"], func: (content, msg, params) => RdDUtility.afficherDemandePayer(params[0], params[1]),
descr: `Permet de payer un montant. Exemples:
<br><strong>/payer 5s 10d</strong> permet d'envoyer un message pour payer 5 sols et 10 deniers
<br><strong>/payer 10d</strong> permet d'envoyer un message pour payer 10 deniers
`
<br><strong>/payer 10d</strong> permet d'envoyer un message pour payer 10 deniers`
});
rddCommands.registerCommand({
path: ["/astro"], func: (content, msg, params) => RdDUtility.afficherHeuresChanceMalchance(params[0]),
descr: `Affiche les heures de chance et de malchance selon l'heure de naissance donnée en argument. Exemples:
<br><strong>/astro Lyre</strong>
`
<br><strong>/astro Lyre</strong>`
});
game.system.rdd.commands = rddCommands;
}
@ -187,7 +185,7 @@ export class RdDCommands {
/* -------------------------------------------- */
async getRencontreTMR(params) {
if (params.length == 1 || params.length ==2) {
if (params.length == 1 || params.length == 2) {
return TMRRencontres.rollRencontre(params[0], params[1])
}
else {
@ -234,6 +232,18 @@ export class RdDCommands {
RdDCommands._chatAnswer(msg, `Lancer d'un Dé draconique: ${ddr.total}`);
}
getTMRAleatoire(msg, params) {
if (params.length < 2) {
let type = params[0];
const tmr = TMRUtility.getTMRAleatoire(type);
RdDCommands._chatAnswer(msg, `Case aléatoire: ${tmr.coord} - ${tmr.label}`);
}
else {
return false;
}
}
/* -------------------------------------------- */
getCoutXpComp(msg, params) {
if (params && (params.length == 1 || params.length == 2)) {

View File

@ -35,12 +35,12 @@ export class RdDRollTables {
}
/* -------------------------------------------- */
static async getSouffle(toChat = true) {
static async getSouffle(toChat = false) {
return await RdDRollTables.drawItemFromRollTable("Souffles de Dragon", toChat);
}
/* -------------------------------------------- */
static async getQueue(toChat = true) {
static async getQueue(toChat = false) {
let queue = await RdDRollTables.drawItemFromRollTable("Queues de dragon", toChat);
if (queue.name.toLowerCase().includes('lancinant') ) {
queue = await RdDRollTables.drawItemFromRollTable("Désirs lancinants", toChat);
@ -52,17 +52,17 @@ export class RdDRollTables {
}
/* -------------------------------------------- */
static async getTete(toChat = true) {
static async getTete(toChat = false) {
return await RdDRollTables.drawItemFromRollTable("Têtes de Dragon pour haut-rêvants", toChat);
}
/* -------------------------------------------- */
static async getTeteHR(toChat = true) {
static async getTeteHR(toChat = false) {
return await RdDRollTables.drawItemFromRollTable("Têtes de Dragon pour tous personnages", toChat);
}
/* -------------------------------------------- */
static async getOmbre(toChat = true) {
static async getOmbre(toChat = false) {
return await RdDRollTables.drawItemFromRollTable("Ombre de Thanatos", toChat);
}

View File

@ -200,7 +200,6 @@ export class RdDTMRDialog extends Dialog {
this.actor.deleteTMRRencontreAtPosition();
this.updatePreviousRencontres();
let rencontreData = {
actor: this.actor,
alias: this.actor.name,
@ -248,12 +247,13 @@ export class RdDTMRDialog extends Dialog {
rencontreData.nbRounds++;
this.nbFatigue += 1;
this._tentativeMaitrise(rencontreData);
setTimeout(() => this._deleteTmrMessages(rencontreData.actor, rencontreData.nbRounds), 500);
this._deleteTmrMessages(rencontreData.actor, rencontreData.nbRounds);
}, 2000);
}
}
_deleteTmrMessages(actor, nbRounds = -1) {
setTimeout(() => {
if (nbRounds < 0) {
ChatUtility.removeChatMessageContaining(`<h4 data-categorie="tmr" data-actor-id="${actor._id}"`);
}
@ -262,6 +262,7 @@ export class RdDTMRDialog extends Dialog {
ChatUtility.removeChatMessageContaining(`<h4 data-categorie="tmr" data-actor-id="${actor._id}" data-rencontre-round="${i}">`);
}
}
}, 500);
}
/* -------------------------------------------- */
@ -275,38 +276,38 @@ export class RdDTMRDialog extends Dialog {
}
/* -------------------------------------------- */
async manageRencontre(coordTMR, cellDescr) {
async manageRencontre(tmr, postRencontre) {
if (this.viewOnly) {
return;
}
this.currentRencontre = undefined;
let rencontre = await this._jetDeRencontre(coordTMR, cellDescr);
let rencontre = await this._jetDeRencontre(tmr);
if (rencontre) { // Manages it
if (rencontre.rencontre) rencontre = rencontre.rencontre; // Manage stored rencontres
console.log("manageRencontre", rencontre);
this.currentRencontre = duplicate(rencontre);
let dialog = new RdDTMRRencontreDialog("", this, this.currentRencontre);
let dialog = new RdDTMRRencontreDialog("", this, this.currentRencontre, postRencontre);
dialog.render(true);
}
}
/* -------------------------------------------- */
async _jetDeRencontre(coordTMR, cellDescr) {
async _jetDeRencontre(tmr) {
if (TMRUtility.isForceRencontre()) {
return await TMRUtility.rencontreTMRRoll(coordTMR, cellDescr);
return await TMRUtility.rencontreTMRRoll(tmr.coord, tmr);
}
let rencontre = this.rencontresExistantes.find(prev => prev.coord == coordTMR);
let rencontre = this.rencontresExistantes.find(prev => prev.coord == tmr.coord);
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);
return await TMRUtility.rencontreTMRRoll(tmr.coord, tmr, isMauvaise);
}
this._tellToUser(myRoll.total + ": Pas de rencontre en " + cellDescr.label + " (" + coordTMR + ")");
this._tellToUser(myRoll.total + ": Pas de rencontre en " + tmr.label + " (" + tmr.coord + ")");
}
/* -------------------------------------------- */
@ -330,13 +331,13 @@ export class RdDTMRDialog extends Dialog {
}
/* -------------------------------------------- */
async manageCaseSpeciale(cellDescr, coordTMR) {
async manageCaseSpeciale(tmr) {
for (let caseTMR of this.casesSpeciales) {
if (caseTMR.data.coord == coordTMR) { // Match !
if (caseTMR.data.coord == tmr.coord) { // Match !
if (caseTMR.data.specific == 'trounoir') {
let newTMR = TMRUtility.getTMRAleatoire();
let tmrPos = duplicate(this.actor.data.data.reve.tmrpos);
tmrPos.coord = newTMR;
tmrPos.coord = newTMR.coord;
await this.actor.update({ "data.reve.tmrpos": tmrPos });
ChatMessage.create({
content: "Vous êtes rentré sur un Trou Noir : ré-insertion aléatoire.",
@ -349,12 +350,7 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */
isCaseMaitrisee(coordTMR) {
for (let caseTMR of this.casesSpeciales) {
if (caseTMR.data.coord == coordTMR && caseTMR.data.specific == 'maitrisee') {
return true;
}
}
return false;
return this.casesSpeciales.find(it => it.data.coord = coordTMR && it.data.specific == 'maitrisee');
}
/* -------------------------------------------- */
@ -364,19 +360,11 @@ export class RdDTMRDialog extends Dialog {
}
/* -------------------------------------------- */
async manageCaseHumide(cellDescr, coordTMR) {
async manageCaseHumide(tmr) {
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({
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)
});
return;
}
if (this.isCaseHumide(tmr)) {
// TODO: permettre de choisir la voie de draconic?
let draconic = this.actor.getBestDraconic();
@ -404,7 +392,7 @@ export class RdDTMRDialog extends Dialog {
explication += "Vous êtes entré sur une case humide, et vous avez <strong>réussi</strong> votre maîtrise !"
msg2MJ += game.user.name + " est rentré sur une case humides : Réussite !";
}
explication += "<br><strong>Test : Rêve actuel / " + draconic.name + " / " + cellDescr.type + "</strong>"
explication += "<br><strong>Test : Rêve actuel / " + draconic.name + " / " + tmr.type + "</strong>"
+ RdDResolutionTable.explain(rolled);
if (rolled.isETotal) {
@ -414,7 +402,7 @@ export class RdDTMRDialog extends Dialog {
}
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, selectedCarac: { label: 'reve' }, competence: draconic.name })
msg2MJ += "<br>Et a fait une réussite particulière";
}
@ -432,6 +420,21 @@ export class RdDTMRDialog extends Dialog {
humideDiag.render(true);
}
}
isCaseHumide(tmr) {
if (this.isCaseMaitrisee(tmr.coord)) {
ChatMessage.create({
content: tmr.label + ": 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)
});
return false;
}
if (this.actor.isCaseHumideAdditionelle(tmr)) {
return true;
}
return tmr.type == "lac" || tmr.type == "fleuve" || tmr.type == "marais";
}
/* -------------------------------------------- */
isReserveExtensible(coordTMR) {
for (let caseTMR of this.casesSpeciales) {
@ -537,60 +540,38 @@ export class RdDTMRDialog extends Dialog {
}
/* -------------------------------------------- */
async deplacerDemiReve(event) {
async onClickTMR(event) {
if (this.viewOnly) {
return;
}
let origEvent = event.data.originalEvent;
let myself = event.target.tmrObject;
let tmrObject = event.target.tmrObject;
let eventCoord = RdDTMRDialog._computeEventCoord(origEvent);
let cellx = eventCoord.cellx;
let celly = eventCoord.celly;
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 eventPos = RdDTMRDialog._computeEventPos(origEvent);
await tmrObject._onClickTMRPos(eventPos); // Vérifier l'état des compteurs reve/fatigue/vie
}
async _onClickTMRPos(eventPos) {
let currentPos = TMRUtility.convertToCellPos(this.actor.data.data.reve.tmrpos.coord);
console.log("deplacerDemiReve >>>>", currentPos, eventPos);
let targetCoordTMR = TMRUtility.convertToTMRCoord(eventPos);
let currentCoordTMR = TMRUtility.convertToTMRCoord(currentPos);
// 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 (this.rencontreState == 'aucune') { // Pas de recontre en post-processing, donc deplacement normal
if (!RdDTMRDialog._horsDePortee(currentPos, eventPos) || this.isTerreAttache(targetCoordTMR) || this.checkConnaissanceFleuve(currentCoordTMR, targetCoordTMR)) {
deplacementType = 'normal';
}
} else {
deplacementType = myself.processClickPostRencontre(coordTMR);
deplacementType = this.processClickPostRencontre(targetCoordTMR);
}
// Si le deplacement est valide
if (deplacementType == 'normal' || deplacementType == 'saut') {
if (myself.currentRencontre != 'normal')
myself.nettoyerRencontre();
let cellDescr = TMRUtility.getTMR(coordTMR);
await myself.manageCaseSpeciale(cellDescr, coordTMR); // Gestion cases spéciales type Trou noir, etc
console.log("deplacerDemiReve: TMR column is", coordTMR, cellx, celly, cellDescr, this);
let tmrPos = duplicate(myself.actor.data.data.reve.tmrpos);
tmrPos.coord = coordTMR;
await myself.actor.update({ "data.reve.tmrpos": tmrPos });
myself._updateDemiReve(myself);
myself.nbFatigue += 1;
myself.updateValuesDisplay();
game.socket.emit("system.foundryvtt-reve-de-dragon", {
msg: "msg_tmr_move", data: {
actorId: myself.actor.data._id,
tmrPos: tmrPos
}
});
if (deplacementType == 'normal') { // Pas de rencontres après un saut de type passeur/changeur/...
await myself.manageRencontre(coordTMR, cellDescr);
}
await myself.manageCaseHumide(cellDescr, coordTMR);
await myself.declencheSortEnReserve(coordTMR);
await myself.actor.checkSoufflePeage(cellDescr);
await this._deplacerDemiReve(targetCoordTMR, deplacementType);
} else if (deplacementType == 'messager') { // Dans ce cas, ouverture du lancement de sort sur la case visée
/*
@ -598,29 +579,71 @@ 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);
myself.nettoyerRencontre();
await this._messagerDemiReve(targetCoordTMR);
} else {
ui.notifications.error("Vous ne pouvez vous déplacer que sur des cases adjacentes à votre position ou valides dans le cas d'une rencontre");
console.log("STATUS :", myself.rencontreState, myself.currentRencontre);
console.log("STATUS :", this.rencontreState, this.currentRencontre);
}
myself.checkQuitterTMR(); // Vérifier l'état des compteurs reve/fatigue/vie
this.checkQuitterTMR();
}
async _messagerDemiReve(targetCoordTMR) {
await this.actor.rollUnSort(targetCoordTMR);
this.nettoyerRencontre();
}
async _deplacerDemiReve(targetCoord, deplacementType) {
if (this.currentRencontre != 'normal') {
this.nettoyerRencontre();
}
let tmr = TMRUtility.getTMR(targetCoord);
await this.manageCaseSpeciale(tmr); // Gestion cases spéciales type Trou noir, etc
console.log("deplacerDemiReve: TMR is", tmr, this);
let tmrPos = duplicate(this.actor.data.data.reve.tmrpos);
tmrPos.coord = targetCoord;
await this.actor.update({ "data.reve.tmrpos": tmrPos });
this._updateDemiReve();
this.nbFatigue += 1;
this.updateValuesDisplay();
game.socket.emit("system.foundryvtt-reve-de-dragon", {
msg: "msg_tmr_move", data: {
actorId: this.actor.data._id,
tmrPos: tmrPos
}
});
if (deplacementType == 'normal') { // Pas de rencontres après un saut de type passeur/changeur/...
await this.manageRencontre(tmr, () => this.postRencontre(tmr));
}
else{
await this.postRencontre(tmr);
}
}
async postRencontre(tmr) {
await this.manageCaseHumide(tmr);
await this.declencheSortEnReserve(tmr.coord);
await this.actor.checkSoufflePeage(tmr);
}
/* -------------------------------------------- */
async forceDemiRevePositionView(coordTMR) {
this._updateDemiReve(this);
this._updateDemiReve();
}
/* -------------------------------------------- */
async forceDemiRevePosition(coordTMR) {
await this.actor.updateCoordTMR(coordTMR);
this._updateDemiReve(this);
let cellDescr = TMRUtility.getTMR(coordTMR);
this.manageCaseHumide(cellDescr, coordTMR);
await this.declencheSortEnReserve(coordTMR);
this._updateDemiReve();
let tmr = TMRUtility.getTMR(coordTMR);
this.manageCaseHumide(tmr);
await this.declencheSortEnReserve(tmr.coord);
}
/* -------------------------------------------- */
@ -661,7 +684,7 @@ export class RdDTMRDialog extends Dialog {
mytmr.buttonMode = true;
mytmr.tmrObject = this;
if (!this.viewOnly) {
mytmr.on('pointerdown', this.deplacerDemiReve);
mytmr.on('pointerdown', this.onClickTMR);
}
this.pixiApp.stage.addChild(mytmr);
@ -676,42 +699,36 @@ export class RdDTMRDialog extends Dialog {
}
// Gestion du cout de montée en points de rêve
let reveCout = -1;
if (this.actor.checkTeteDeplacementAccelere()) {
reveCout = -1;
} else {
reveCout = (this.tmrdata.isRapide) ? -2 : -1;
}
let reveCout = (this.tmrdata.isRapide && !this.actor.checkTeteDeplacementAccelere()) ? -2 : -1;
reveCout -= this.actor.checkMonteeLaborieuse();
await this.actor.reveActuelIncDec(reveCout);
// Le reste...
this.updateValuesDisplay();
let coordTMR = this.actor.data.data.reve.tmrpos.coord;
let cellDescr = TMRUtility.getTMR(coordTMR);
await this.manageRencontre(coordTMR, cellDescr);
this.manageCaseHumide(cellDescr, coordTMR);
// Mise à jour du nb de cases de Fatigue
let tmr = TMRUtility.getTMR(this.actor.data.data.reve.tmrpos.coord);
await this.manageRencontre(tmr, () => {
this.postRencontre(tmr);
this.nbFatigue = this.actor.getTMRFatigue();
this.actor.displayTMRQueueSouffleInformation();
});
}
/* -------------------------------------------- */
static _computeEventCoord(origEvent) {
static _computeEventPos(origEvent) {
let canvasRect = origEvent.target.getBoundingClientRect();
let x = origEvent.clientX - canvasRect.left;
let y = origEvent.clientY - canvasRect.top;
let cellx = Math.floor(x / tmrConstants.cellw); // [From 0 -> 12]
y -= (cellx % 2 == 0) ? tmrConstants.col1_y : tmrConstants.col2_y;
let celly = Math.floor(y / tmrConstants.cellh); // [From 0 -> 14]
return { cellx, celly };
return { x: cellx, y: celly };
}
/* -------------------------------------------- */
static _horsDePortee(pos, cellx, celly) {
return Math.abs(cellx - pos.x) > 1
|| Math.abs(celly - pos.y) > 1
|| (pos.y == 0 && celly > pos.y && cellx != pos.x && pos.x % 2 == 0)
|| (celly == 0 && celly < pos.y && cellx != pos.x && pos.x % 2 == 1);
static _horsDePortee(origin, target) {
return Math.abs(target.x - origin.x) > 1
|| Math.abs(target.y - origin.y) > 1
|| (origin.y == 0 && target.y > origin.y && target.x != origin.x && origin.x % 2 == 0)
|| (target.y == 0 && target.y < origin.y && target.x != origin.x && origin.x % 2 == 1);
}
/* -------------------------------------------- */
@ -811,14 +828,14 @@ export class RdDTMRDialog extends Dialog {
}
/* -------------------------------------------- */
_updateDemiReve(myself) {
myself._setTokenPosition(myself.demiReve);
_updateDemiReve() {
this._setTokenPosition(this.demiReve);
}
/* -------------------------------------------- */
/** Retourne les coordonnées x, h, w, h du rectangle d'une case donnée */
_getCaseRectangleCoord(coord) {
let coordXY = TMRUtility.convertToCellCoord(coord);
let coordXY = TMRUtility.convertToCellPos(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;
@ -827,7 +844,7 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */
_setTokenPosition(token) {
let coordXY = TMRUtility.convertToCellCoord(token.coordTMR());
let coordXY = TMRUtility.convertToCellPos(token.coordTMR());
let decallagePairImpair = (coordXY.x % 2 == 0) ? tmrConstants.col1_y : tmrConstants.col2_y;
let dx = (token.sprite.decallage == undefined) ? 0 : token.sprite.decallage.x;
let dy = (token.sprite.decallage == undefined) ? 0 : token.sprite.decallage.y;

View File

@ -2,20 +2,20 @@
export class RdDTMRRencontreDialog extends Dialog {
/* -------------------------------------------- */
constructor(html, tmrApp, rencontre) {
constructor(html, tmrApp, rencontre, postRencontre) {
const dialogConf = {
title: "Rencontre en TMR!",
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() } }
derober: { icon: '<i class="fas fa-check"></i>', label: "Se dérober", callback: () => { this.onButtonFuir(() => tmrApp.derober()); } },
refouler: { icon: '<i class="fas fa-check"></i>', label: "Refouler", callback: () => this.onButtonAction(() => tmrApp.refouler()) },
maitiser: { icon: '<i class="fas fa-check"></i>', label: "Maîtriser", callback: () => this.onButtonAction(() => 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() }};
}
dialogConf.buttons.ignorer = { icon: '<i class="fas fa-check"></i>', label: "Ignorer", callback: () => this.onButtonAction(() => tmrApp.ignorerRencontre()) }
};
const dialogOptions = {
classes: ["tmrrencdialog"],
@ -26,13 +26,25 @@ export class RdDTMRRencontreDialog extends Dialog {
this.toClose = false;
this.rencontreData = duplicate(rencontre);
this.postRencontre = postRencontre;
this.tmrApp = tmrApp;
this.tmrApp.minimize();
}
async onButtonAction(action) {
this.toClose = true;
await action();
this.postRencontre();
}
onButtonFuir(action) {
this.toClose = true;
await action();
}
/* -------------------------------------------- */
close() {
if ( this.toClose ) {
if (this.toClose) {
this.tmrApp.maximize();
return super.close();
}

View File

@ -84,7 +84,7 @@ const typeRencontres = {
},
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é.`,
msgSucces: (data) => `Le ${data.rencontre.name} vaincu accepte de vous déplacer sur une autre ${TMRType[data.tmr.type].name} 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.`;

View File

@ -1,6 +1,7 @@
import { DeDraconique } from "./de-draconique.js";
import { TMRRencontres } from "./tmr-rencontres.js";
import { Grammar } from "./grammar.js";
import { Misc } from "./misc.js";
/* -------------------------------------------- */
const TMRMapping = {
@ -210,20 +211,20 @@ const TMRMapping = {
}
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"
cite: {name:"cité"},
sanctuaire: {name:"sanctuaire"},
plaines: {name:"plaines"},
pont: {name:"pont"},
collines: {name:"collines"},
foret: {name:"forêt"},
monts: {name:"monts"},
desert: {name:"désert"},
fleuve: {name:"fleuve"},
lac: {name:"lac"},
marais: {name:"marais"},
gouffre: {name:"gouffre"},
necropole: {name:"nécropole"},
desolation: {name:"désolation"}
}
/* -------------------------------------------- */
@ -257,14 +258,17 @@ export class TMRUtility {
for (let coord in TMRMapping) {
TMRMapping[coord].coord = coord;
}
let tmrByType = Misc.classify(Object.values(TMRMapping));
for (const [type, list] of Object.entries(tmrByType)) {
TMRType[type].list = list;
}
}
/* -------------------------------------------- */
static convertToTMRCoord( x, y )
static convertToTMRCoord( pos )
{
y = y + 1
let letterX = String.fromCharCode(65+x);
return letterX+y
let letterX = String.fromCharCode(65+ (pos.x));
return letterX + (pos.y +1)
}
/* -------------------------------------------- */
@ -280,7 +284,7 @@ export class TMRUtility {
}
/* -------------------------------------------- */
static convertToCellCoord( coordTMR )
static convertToCellPos( coordTMR )
{
let x = coordTMR.charCodeAt(0) - 65;
let y = coordTMR.substr(1) - 1;
@ -328,13 +332,13 @@ export class TMRUtility {
/* -------------------------------------------- */
static deplaceTMRSelonPattern( coord, direction, nTime ) {
for (let i=0; i <nTime; i++) {
let currentPosXY = TMRUtility.convertToCellCoord(coord);
let currentPosXY = TMRUtility.convertToCellPos(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
coord = TMRUtility.convertToTMRCoord(currentPosXY.x, currentPosXY.y);
coord = TMRUtility.convertToTMRCoord(currentPosXY);
} else {
coord = this.getTMRAleatoire();
coord = this.getTMRAleatoire().coord;
}
console.log("Nouvelle case iteration !!!", i, coord);
}
@ -361,41 +365,18 @@ export class TMRUtility {
/* -------------------------------------------- */
static getListTMR(terrain) {
let list = [];
for (let index in TMRMapping) {
if (TMRMapping[index].type == terrain){
list.push(TMRMapping[index]);
}
}
return list;
return TMRType[terrain].list;
}
static getListCoordTMR(terrain) {
return this.getListTMR(terrain).map(it=>it.coord);
}
/* -------------------------------------------- */
static getTMRAleatoire(terrain=undefined)
{
if (terrain) {
let list = TMRUtility.getListTMR(terrain);
static getTMRAleatoire(terrain = undefined) {
let list = terrain ? TMRUtility.getListTMR(terrain) : Object.values(TMRMapping);
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) {
letterValue = new Roll( "1d7").roll().total;
letter = String.fromCharCode( 65 + ((parseInt(letterValue)-1)*2) );
} else {
letterValue = new Roll( "1d13 + 64" ).roll().total;
letter = String.fromCharCode( letterValue );
}
let caseIndex = letter+num;
ChatMessage.create( { content: "Case aléatoire : " + letter+num + " - " + TMRMapping[caseIndex].label ,
whisper: ChatMessage.getWhisperRecipients("GM") } );
return caseIndex;
}
/* -------------------------------------------- */
static _checkTMRCoord( x, y ) {
@ -432,22 +413,22 @@ export class TMRUtility {
/** Returns a list of case inside a given distance
*
*/
static getTMRPortee(coord, portee) {
return TMRUtility.getTMRArea(coord, portee, tmrConstants);
static getTMRPortee(centerCoord, portee) {
return TMRUtility.getTMRArea(centerCoord, portee, tmrConstants);
}
static getTMRArea( coord, distance, tmrConstants ) {
let pos = this.convertToCellCoord( coord );
let posPic = this.computeRealPictureCoordinates( pos, tmrConstants );
static getTMRArea( centerCoord, distance, tmrConstants ) {
let centerPos = this.convertToCellPos( centerCoord );
let posPic = this.computeRealPictureCoordinates( centerPos, tmrConstants );
let caseList = [];
for (let x=pos.x-distance; x<=pos.x+distance; x++ ) { // Loop thru lines
for (let y=pos.y-distance; y<=pos.y+distance; y++ ) { // Loop thru lines
//console.log("Parsing position", x, y);
if ( this._checkTMRCoord(x, y) ) { // Coordinate is valie
let posPicNow = this.computeRealPictureCoordinates( {x: x, y: y}, tmrConstants );
for (let dx=-distance; dx<=distance; dx++ ) { // Loop thru lines
for (let dy=-distance; dy<=distance; dy++ ) { // Loop thru lines
const currentPos = { x: centerPos.x+dx, y: centerPos.y+dy };
if ( this._checkTMRCoord(currentPos.x, currentPos.y) ) { // Coordinate is valie
let posPicNow = this.computeRealPictureCoordinates( currentPos, tmrConstants );
let dist = Math.sqrt(Math.pow(posPicNow.x - posPic.x,2) + Math.pow(posPicNow.y - posPic.y, 2)) / tmrConstants.cellw;
if ( dist < distance+0.5) {
caseList.push( this.convertToTMRCoord(x, y) ); // Inside the area
caseList.push( this.convertToTMRCoord(currentPos) ); // Inside the area
}
}
}

File diff suppressed because one or more lines are too long