Améliorations des tmr

* fermeture des cités
* utilisations d'icônes pour les cases spéciales, rencontres, sorts
en réserve
* séparation pixi/actions TMR / définition des cases spéciales

fixes divers:
* #153 lancer de sort si draconic utilise compétence autre que rêve
* #152: table de résolution doublée sur cht points de rêve
* /table n'affichait plus les résultats suite à chgt sur souffles/queues
This commit is contained in:
Vincent Vandemeulebrouck
2021-02-11 02:48:27 +01:00
parent d25e25e6fd
commit b226e5c09a
36 changed files with 1631 additions and 774 deletions

View File

@ -5,7 +5,6 @@
import { RdDUtility } from "./rdd-utility.js";
import { HtmlUtility } from "./html-utility.js";
import { RdDItem } from "./item.js";
import { RdDItemArme } from "./item-arme.js";
import { RdDItemCompetence } from "./item-competence.js";
import { RdDBonus } from "./rdd-bonus.js";

View File

@ -5,7 +5,6 @@
import { RdDUtility } from "./rdd-utility.js";
import { HtmlUtility } from "./html-utility.js";
import { RdDItem } from "./item.js";
import { Misc } from "./misc.js";
/* -------------------------------------------- */

File diff suppressed because it is too large Load Diff

View File

@ -1,41 +0,0 @@
import { Misc } from "./misc.js";
/* -------------------------------------------- */
export class RdDItem {
static isQueueDragon(item) {
return item.type == 'queue' || item.type == 'ombre';
}
static isSouffleDragon(item) {
return item.type == 'souffle';
}
static isHRPontImpraticable(item) {
return RdDItem.isSouffleDragon(item) && item.name.toLowerCase().includes(' des ponts');
}
static isHRDoubleResistanceFleuve(item) {
return RdDItem.isSouffleDragon(item) && item.name.toLowerCase().includes('résistance du fleuve')
}
static isHRPeage(item) {
return RdDItem.isSouffleDragon(item) && item.name.toLowerCase().includes('péage')
}
static isHRMauvaiseRencontreEnPerspective(item) {
return (RdDItem.isQueueDragon(item) || RdDItem.isSouffleDragon(item)) && item.name.toLowerCase().includes('mauvaise rencontre')
}
static isHRInertieDraconique(item) {
return RdDItem.isQueueDragon(item) && item.name.toLowerCase().includes('inertie draconique')
}
static isCaseTMR(item) {
return item.type == 'casetmr';
}
static isHRCaseInnondee(item) {
return RdDItem.isCaseTMR(item) && item.data.specific == 'debordement';
}
}

View File

@ -49,7 +49,8 @@ const poesieHautReve = [
},
{
reference: 'Denis Gerfaud',
extrait: `Ainsi se cuccèdent les Jours et les Ages. Les jours des Dragons sont les Ages des Hommes`
extrait: `Ainsi se cuccèdent les Jours et les Ages.
<br>Les jours des Dragons sont les Ages des Hommes.`
},
{
reference: 'Denis Gerfaud',

View File

@ -22,12 +22,13 @@ export class RdDCommands {
const rddCommands = new RdDCommands();
rddCommands.registerCommand({ path: ["/aide"], func: (content, msg, params) => rddCommands.help(msg), descr: "Affiche l'aide pour toutes les commandes" });
rddCommands.registerCommand({ path: ["/help"], func: (content, msg, params) => rddCommands.help(msg), descr: "Affiche l'aide pour toutes les commandes" });
rddCommands.registerCommand({ path: ["/table", "queues"], func: (content, msg, params) => RdDRollTables.getQueue(), descr: "Tire une Queue de Dragon" });
rddCommands.registerCommand({ path: ["/table", "ombre"], func: (content, msg, params) => RdDRollTables.getOmbre(), descr: "Tire une Ombre de Dragon" });
rddCommands.registerCommand({ path: ["/table", "tetehr"], func: (content, msg, params) => RdDRollTables.getTeteHR(), descr: "Tire une Tête de Dragon pour Hauts Revants" });
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", "queues"], func: (content, msg, params) => RdDRollTables.getQueue(true), descr: "Tire une Queue de Dragon" });
rddCommands.registerCommand({ path: ["/table", "ombre"], func: (content, msg, params) => RdDRollTables.getOmbre(true), descr: "Tire une Ombre de Dragon" });
rddCommands.registerCommand({ path: ["/table", "tetehr"], func: (content, msg, params) => RdDRollTables.getTeteHR(true), descr: "Tire une Tête de Dragon pour Hauts Revants" });
rddCommands.registerCommand({ path: ["/table", "tete"], func: (content, msg, params) => RdDRollTables.getTete(true), descr: "Tire une Tête de Dragon" });
rddCommands.registerCommand({ path: ["/table", "souffle"], func: (content, msg, params) => RdDRollTables.getSouffle(true), descr: " Tire un Souffle de Dragon" });
rddCommands.registerCommand({ path: ["/table", "comp"], func: (content, msg, params) => RdDRollTables.getCompetence(true), descr: "Tire une compétence au hasard" });
rddCommands.registerCommand({ path: ["/table", "tarot"], func: (content, msg, params) => RdDRollTables.getTarot(true), descr: "Tire une carte du Tarot Draconique" });
rddCommands.registerCommand({ path: ["/nom"], func: (content, msg, params) => RdDNameGen.getName(msg, params), descr: "Génère un nom aléatoire" });
rddCommands.registerCommand({
@ -237,7 +238,7 @@ export class RdDCommands {
getTMRAleatoire(msg, params) {
if (params.length < 2) {
let type = params[0];
const tmr = TMRUtility.getTMRAleatoire(type);
const tmr = TMRUtility.getTMRAleatoire(it => it.type == type);
RdDCommands._chatAnswer(msg, `Case aléatoire: ${tmr.coord} - ${tmr.label}`);
}
else {

View File

@ -1,17 +1,15 @@
import { Misc } from "./misc.js";
/* -------------------------------------------- */
/**
* Mapping des types d'Item/Actor vers le nom d'affichage.
* Par défaut, on prend le type avec la première lettre
* majuscule, pas besoin d'ajouter tous les types.
*/
const typeDisplayName = {
"objet": "Objet",
"arme": "Arme",
"armure": "Armure",
"conteneur": "Conteneur",
"competence": "Compétence",
"sort": "Sort",
"herbe": "Plante",
"ingredient": "Ingrédient",
"livre": "Livre",
"potion": "Potion",
"munition": "Munition",
"queue": "Queue de dragon",
"ombre": "Ombre de Thanatos",
"souffle": "Souffle de Dragon",
@ -20,20 +18,16 @@ const typeDisplayName = {
"rencontresTMR": "Rencontre des TMR",
"competencecreature": "Compétence de créature",
"nombreastral": "Nombre astral",
"casetmr": "Case des TMR",
"casetmr": "Effet sur TMR",
"recettealchimique": "Recette alchimique",
"recettecuisine": "Recette de cuisine",
"tarot": "Carte de tarot draconique",
"tache": "Tâche",
"meditation": "Méditation",
"monnaie": "Monnaie",
"musique": "Morceau de musique",
"chant": "Chanson",
"danse": "Danse",
"jeu": "Jeu",
"personnage": "Personnage",
"creature": "Créature",
"entite": "Entité de cauchemar",
"entite": "Entité",
"vehicule": "Véhicule"
}

View File

@ -28,6 +28,7 @@ import { RddCompendiumOrganiser } from "./rdd-compendium-organiser.js";
import { ReglesOptionelles } from "./regles-optionelles.js";
import { TMRRencontres } from "./tmr-rencontres.js";
import { RdDHotbar } from "./rdd-hotbar-drop.js"
import { EffetsDraconiques } from "./tmr/effets-draconiques.js";
/* -------------------------------------------- */
/* Foundry VTT Initialization */
@ -234,6 +235,7 @@ Hooks.once("init", async function () {
RdDActor.init();
RddCompendiumOrganiser.init();
ReglesOptionelles.init();
EffetsDraconiques.init()
TMRUtility.init();
TMRRencontres.init();
RdDHotbar.initDropbar();

View File

@ -179,12 +179,6 @@ export class RdDRoll extends Dialog {
console.log("RdDRollSelectDialog - Cout reve", ptreve);
this.updateRollResult();
});
html.find('#ptreve-variable').change((event) => {
let ptreve = Misc.toInt(event.currentTarget.value);
this.rollData.selectedSort.data.ptreve_reel = ptreve; // Update the selectedCarac
console.log("RdDRollSelectDialog - Cout reve", ptreve);
this.updateRollResult();
});
html.find('#coupsNonMortels').change((event) => {
this.rollData.dmg.mortalite = event.currentTarget.checked ? "non-mortel" : "mortel";
this.updateRollResult();

View File

@ -34,6 +34,11 @@ export class RdDRollTables {
return drawnItemRef.text;
}
/* -------------------------------------------- */
static async getCompetence(toChat = false) {
return await RdDRollTables.drawItemFromRollTable("Détermination aléatoire de compétence", toChat);
}
/* -------------------------------------------- */
static async getSouffle(toChat = false) {
return await RdDRollTables.drawItemFromRollTable("Souffles de Dragon", toChat);

View File

@ -1,24 +1,31 @@
/**
* Extend the base Dialog entity by defining a custom window to perform spell.
* @extends {Dialog}
*/
import { RollDataAjustements } from "./rolldata-ajustements.js";
import { RdDUtility } from "./rdd-utility.js";
import { TMRUtility } from "./tmr-utility.js";
import { tmrConstants } from "./tmr-utility.js";
import { TMRUtility, tmrConstants } from "./tmr-utility.js";
import { RdDResolutionTable } from "./rdd-resolution-table.js";
import { RdDTMRRencontreDialog } from "./rdd-tmr-rencontre-dialog.js";
import { TMRRencontres } from "./tmr-rencontres.js";
import { ChatUtility } from "./chat-utility.js";
import { RdDRoll } from "./rdd-roll.js";
import { Poetique } from "./poetique.js";
import { EffetsDraconiques } from "./tmr/effets-draconiques.js";
import { PixiTMR } from "./tmr/pixi-tmr.js";
import { Draconique } from "./tmr/draconique.js";
/* -------------------------------------------- */
export class RdDTMRDialog extends Dialog {
static async create(html, actor, tmrData) {
if (tmrData.mode != 'visu') {
// Notification au MJ
ChatMessage.create({ content: actor.name + " est monté dans les TMR en mode : " + tmrData.mode, whisper: ChatMessage.getWhisperRecipients("GM") });
}
return new RdDTMRDialog(html, actor, tmrData);
}
/* -------------------------------------------- */
constructor(html, actor, tmrData, mode) {
constructor(html, actor, tmrData) {
const dialogConf = {
title: "Terres Médianes de Rêve",
content: html,
@ -38,19 +45,145 @@ export class RdDTMRDialog extends Dialog {
this.tmrdata = duplicate(tmrData);
this.actor = actor;
this.actor.tmrApp = this; // reference this app in the actor structure
this.viewOnly = mode == "visu"
this.viewOnly = tmrData.mode == "visu"
this.fatigueParCase = this.viewOnly ? 0 : this.actor.getTMRFatigue();
this.cumulFatigue = 0;
this.rencontresExistantes = duplicate(this.actor.data.data.reve.rencontre.list);
this.sortReserves = duplicate(this.actor.data.data.reve.reserve.list);
this.casesSpeciales = this.actor.data.items.filter(item => item.type == 'casetmr');
this.loadRencontres();
this.loadSortsReserve();
this.casesSpeciales = this.actor.data.items.filter(item => Draconique.isCaseTMR(item));
this.allTokens = [];
this.rencontreState = 'aucune';
this.pixiApp = new PIXI.Application({ width: 720, height: 860 });
this.pixiTMR = new PixiTMR(this, this.pixiApp);
this.callbacksOnAnimate = [];
if (!this.viewOnly) {
this.actor.setStatusDemiReve(true);
this._tellToGM(this.actor.name + " monte dans les terres médianes (" + mode + ")");
this._tellToGM(this.actor.name + " monte dans les terres médianes (" + tmrData.mode + ")");
}
// load the texture we need
this.pixiTMR.load((loader, resources) => this.createPixiSprites());
}
loadSortsReserve() {
this.sortsReserves = duplicate(this.actor.data.data.reve.reserve.list);
}
loadRencontres() {
this.rencontresExistantes = duplicate(this.actor.getTMRRencontres()).list;
}
/* -------------------------------------------- */
createPixiSprites() {
EffetsDraconiques.carteTmr._createSprite(this.pixiTMR);
this.updateTokens();
this.demiReve = this._tokenDemiReve();
this._updateDemiReve();
}
/* -------------------------------------------- */
_createTokens() {
let tokens = this._getTokensCasesTmr()
.concat(this._getTokensRencontres())
.concat(this._getTokensSortsReserve());
for (let t of tokens) {
this._trackToken(t);
}
}
updateTokens() {
this._removeTokens(t => true);
this.loadRencontres();
this.loadSortsReserve();
this._createTokens();
}
/* -------------------------------------------- */
_getTokensCasesTmr() {
return this.casesSpeciales.map(c => this._tokenCaseSpeciale(c)).filter(token => token);
}
_getTokensRencontres() {
return this.rencontresExistantes.map(it => this._tokenRencontre(it));
}
_getTokensSortsReserve() {
return this.sortsReserves.map(it => this._tokenSortEnReserve(it));
}
/* -------------------------------------------- */
_tokenRencontre(rencontre) {
return EffetsDraconiques.rencontre.token(this.pixiTMR, rencontre, () => rencontre.coord);
}
_tokenCaseSpeciale(caseSpeciale) {
const draconique = Draconique.get(caseSpeciale.data.specific);
return draconique?.token(this.pixiTMR, caseSpeciale, () => caseSpeciale.data.coord);
}
_tokenSortEnReserve(sortEnReserve) {
return EffetsDraconiques.sortReserve.token(this.pixiTMR, sortEnReserve.sort, () => sortEnReserve.coord);
}
_tokenDemiReve() {
return EffetsDraconiques.demiReve.token(this.pixiTMR, this.actor, () => this.actor.data.data.reve.tmrpos.coord);
}
_updateDemiReve() {
this._setTokenPosition(this.demiReve);
}
/* -------------------------------------------- */
async activateListeners(html) {
super.activateListeners(html);
document.getElementById("tmrrow1").insertCell(1).append(this.pixiApp.view);
if (this.viewOnly) {
html.find('#lancer-sort').remove();
}
else {
// Roll Sort
html.find('#lancer-sort').click((event) => {
this.actor.rollUnSort(this.actor.data.data.reve.tmrpos.coord);
});
}
if (this.viewOnly) {
return;
}
// Gestion du cout de montée en points de rêve
let reveCout = ((this.tmrdata.isRapide && !EffetsDraconiques.isDeplacementAccelere(this.actor)) ? -2 : -1)
- this.actor.countMonteeLaborieuse();
this.cumulFatigue += this.fatigueParCase;
await this.actor.reveActuelIncDec(reveCout);
// Le reste...
this.updateValuesDisplay();
let tmr = TMRUtility.getTMR(this.actor.data.data.reve.tmrpos.coord);
await this.manageRencontre(tmr, () => {
this.postRencontre(tmr);
this.actor.displayTMRQueueSouffleInformation();
});
}
/* -------------------------------------------- */
updateValuesDisplay() {
let ptsreve = document.getElementById("tmr-pointsreve-value");
ptsreve.innerHTML = this.actor.data.data.reve.reve.value;
let tmrpos = document.getElementById("tmr-pos");
let tmr = TMRUtility.getTMR(this.actor.data.data.reve.tmrpos.coord);
tmrpos.innerHTML = this.actor.data.data.reve.tmrpos.coord + " (" + tmr.label + ")";
let etat = document.getElementById("tmr-etatgeneral-value");
etat.innerHTML = this.actor.getEtatGeneral();
let refoulement = document.getElementById("tmr-refoulement-value");
refoulement.innerHTML = this.actor.data.data.reve.refoulement.value;
let fatigueItem = document.getElementById("tmr-fatigue-table");
//console.log("Refresh : ", this.actor.data.data.sante.fatigue.value);
fatigueItem.innerHTML = "<table class='table-fatigue'>" + RdDUtility.makeHTMLfatigueMatrix(this.actor.data.data.sante.fatigue.value, this.actor.data.data.sante.endurance.max).html() + "</table>";
}
/* -------------------------------------------- */
@ -63,51 +196,6 @@ export class RdDTMRDialog extends Dialog {
}
}
/* -------------------------------------------- */
displaySortReserve() {
console.debug("displaySortReserve", this.sortReserves);
for (let sort of this.sortReserves) {
this._trackToken(this._tokenSortEnReserve(sort));
}
}
/* -------------------------------------------- */
displaySpecificCase() {
for (let caseSpeciale of this.casesSpeciales) {
console.log("SPEC CASE ", caseSpeciale);
if (caseSpeciale.data.specific == 'trounoir') {
this._trackToken(this._tokenTrouNoir(caseSpeciale.data.coord));
} else if (caseSpeciale.data.specific == 'attache') {
this._trackToken(this._tokenTerreAttache(caseSpeciale.data.coord));
} else if (caseSpeciale.data.specific == 'debordement') {
this._trackToken(this._tokenDebordement(caseSpeciale.data.coord));
} else if (caseSpeciale.data.specific == 'maitrisee') {
this._trackToken(this._tokenMaitrisee(caseSpeciale.data.coord));
}
}
}
/* -------------------------------------------- */
displayPreviousRencontres() {
console.debug("displayPreviousRencontres", this.rencontresExistantes);
for (let rencontre of this.rencontresExistantes) {
this._trackToken(this._tokenRencontre(rencontre));
}
}
/* -------------------------------------------- */
updatePreviousRencontres() {
this._removeTokens(t => t.rencontre != undefined);
this.rencontresExistantes = duplicate(this.actor.data.data.reve.rencontre.list);
this.displayPreviousRencontres();
}
/* -------------------------------------------- */
updateSortReserve() {
this._removeTokens(t => t.sort != undefined);
this.sortReserves = duplicate(this.actor.data.data.reve.reserve.list);
this.displaySortReserve();
}
/* -------------------------------------------- */
async derober() {
@ -121,7 +209,7 @@ export class RdDTMRDialog extends Dialog {
this._tellToGM(this.actor.name + " a refoulé : " + this.currentRencontre.name);
await this.actor.deleteTMRRencontreAtPosition(); // Remove the stored rencontre if necessary
await this.actor.ajouterRefoulement(this.currentRencontre.refoulement ?? 1);
this.updatePreviousRencontres();
this.updateTokens();
console.log("-> refouler", this.currentRencontre)
this.updateValuesDisplay();
this.nettoyerRencontre();
@ -131,7 +219,7 @@ export class RdDTMRDialog extends Dialog {
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.updateTokens();
this.updateValuesDisplay();
this.nettoyerRencontre();
}
@ -166,7 +254,7 @@ export class RdDTMRDialog extends Dialog {
}
async choisirCaseType(type) {
const locList = TMRUtility.getListCoordTMR(type);
const locList = TMRUtility.filterTMR(it => it.type == type).map(it => it.coord);
this.colorierZoneRencontre(locList);
}
@ -200,9 +288,9 @@ export class RdDTMRDialog extends Dialog {
}
/* -------------------------------------------- */
async maitriser() {
async maitriserRencontre() {
this.actor.deleteTMRRencontreAtPosition();
this.updatePreviousRencontres();
this.updateTokens();
let rencontreData = {
actor: this.actor,
@ -218,6 +306,7 @@ export class RdDTMRDialog extends Dialog {
await this._tentativeMaitrise(rencontreData);
}
/* -------------------------------------------- */
async _tentativeMaitrise(rencontreData) {
console.log("-> matriser", rencontreData);
@ -227,7 +316,6 @@ export class RdDTMRDialog extends Dialog {
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({
@ -257,6 +345,7 @@ export class RdDTMRDialog extends Dialog {
}
}
/* -------------------------------------------- */
_deleteTmrMessages(actor, nbRounds = -1) {
setTimeout(() => {
if (nbRounds < 0) {
@ -307,60 +396,41 @@ export class RdDTMRDialog extends Dialog {
if (rencontre) {
return rencontre;
}
// TODO: dialog pour remplacer la rencontre par un présent
//if (this.casesSpeciales.find(c => EffetsDraconiques.isPresentCite(c, tmr.coord))) {
let myRoll = new Roll("1d7").evaluate().total;
if (TMRUtility.isForceRencontre() || myRoll== 7) {
if (TMRUtility.isForceRencontre() || myRoll == 7) {
return await this.rencontreTMRRoll(tmr, this.actor.isRencontreSpeciale());
}
this._tellToUser(myRoll + ": Pas de rencontre en " + tmr.label + " (" + tmr.coord + ")");
}
/* -------------------------------------------- */
async rencontreTMRRoll(tmr, isMauvaise = false) {
let rencontre = TMRUtility.utiliseForceRencontre() ??
isMauvaise ? await TMRRencontres.getMauvaiseRencontre()
: await TMRRencontres.getRencontreAleatoire(tmr.type);
(isMauvaise
? await TMRRencontres.getMauvaiseRencontre()
: await TMRRencontres.getRencontreAleatoire(tmr.type));
rencontre.coord = tmr.coord;
rencontre.date = game.system.rdd.calendrier.getDateFromIndex();
rencontre.heure = game.system.rdd.calendrier.getCurrentHeure();
return rencontre;
}
/* -------------------------------------------- */
updateValuesDisplay() {
let ptsreve = document.getElementById("tmr-pointsreve-value");
ptsreve.innerHTML = this.actor.data.data.reve.reve.value;
let tmrpos = document.getElementById("tmr-pos");
let tmr = TMRUtility.getTMR(this.actor.data.data.reve.tmrpos.coord);
tmrpos.innerHTML = this.actor.data.data.reve.tmrpos.coord + " (" + tmr.label + ")";
let etat = document.getElementById("tmr-etatgeneral-value");
etat.innerHTML = this.actor.getEtatGeneral();
let refoulement = document.getElementById("tmr-refoulement-value");
refoulement.innerHTML = this.actor.data.data.reve.refoulement.value;
let fatigueItem = document.getElementById("tmr-fatigue-table");
//console.log("Refresh : ", this.actor.data.data.sante.fatigue.value);
fatigueItem.innerHTML = "<table class='table-fatigue'>" + RdDUtility.makeHTMLfatigueMatrix(this.actor.data.data.sante.fatigue.value, this.actor.data.data.sante.endurance.max).html() + "</table>";
}
/* -------------------------------------------- */
async manageCaseSpeciale(tmr) {
for (let caseTMR of this.casesSpeciales) {
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.coord;
await this.actor.update({ "data.reve.tmrpos": tmrPos });
ChatMessage.create({
content: "Vous êtes rentré sur un Trou Noir : ré-insertion aléatoire.",
whisper: ChatMessage.getWhisperRecipients(game.user.name)
});
}
}
if (this.casesSpeciales.find(c => EffetsDraconiques.isCaseTrouNoir(c, tmr.coord))) {
let newTMR = TMRUtility.getTMRAleatoire();
let tmrPos = duplicate(this.actor.data.data.reve.tmrpos);
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.",
whisper: ChatMessage.getWhisperRecipients(game.user.name)
});
}
}
@ -376,7 +446,8 @@ export class RdDTMRDialog extends Dialog {
tmr: tmr,
canClose: false,
diffLibre: -7,
forceCarac: { 'reve-actuel': { label: "Rêve Actuel", value: this.actor.getReveActuel() } }
forceCarac: { 'reve-actuel': { label: "Rêve Actuel", value: this.actor.getReveActuel() } },
maitrise: { verbe: 'maîtriser', action: 'Maîtriser le fleuve' }
}
rollData.competence.data.defaut_carac = 'reve-actuel';
@ -384,80 +455,125 @@ export class RdDTMRDialog extends Dialog {
}
}
/* -------------------------------------------- */
async _rollMaitriseCaseHumide(rollData) {
await this._maitriserTMR(rollData, r => this._resultatMaitriseCaseHumide(r));
}
async _resultatMaitriseCaseHumide(rollData) {
if (rollData.rolled.isETotal) {
rollData.souffle = await this.actor.ajouterSouffle({ chat: false });
}
this.toclose = rollData.rolled.isEchec;
if (rollData.rolled.isSuccess && !rollData.previous && EffetsDraconiques.isDoubleResistanceFleuve(this.actor)) {
rollData.previous = { rolled: rollData.rolled, ajustements: rollData.ajustements };
await this._rollMaitriseCaseHumide(rollData);
return;
}
rollData.poesie = Poetique.getExtrait();
ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-resultat-maitrise-tmr.html`, rollData)
});
if (rollData.rolled.isEchec) {
this.close();
}
}
/* -------------------------------------------- */
isCaseHumide(tmr) {
if (!(TMRUtility.isCaseHumide(tmr) || this.isCaseHumideAdditionelle(tmr))) {
return undefined;
}
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;
return undefined;
}
if (this.actor.isCaseHumideAdditionelle(tmr)) {
return true;
}
return tmr.type == "lac" || tmr.type == "fleuve" || tmr.type == "marais";
return -7;
}
/* -------------------------------------------- */
isCaseMaitrisee(coordTMR) {
return this.casesSpeciales.find(it => it.data.coord = coordTMR && it.data.specific == 'maitrisee');
isCaseHumideAdditionelle(tmr) {
if (tmr.type == 'pont' && EffetsDraconiques.isPontImpraticable(this.actor)) {
ChatMessage.create({
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;
}
if (this.isCaseInondee(tmr.coord)) {
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;
}
async _rollMaitriseCaseHumide(rollData) {
this.minimize(); // Hide
const dialog = await RdDRoll.create(this.actor, rollData,
{
html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-tmr-humide.html',
options:{ height: 350 },
close: html => { this.maximize(); } // Re-display TMR
},
{
name: 'maitrise',
label: 'Maîtriser le fleuve',
callbacks: [
this.actor.createCallbackExperience(),
{ action: r => this._maitriseCaseHumide(r) }
]
/* -------------------------------------------- */
async conquerirCiteFermee(tmr) {
if (this.viewOnly || this.currentRencontre) {
return;
}
const citeFermee = this.isCiteFermee(tmr.coord);
if (citeFermee) {
let rollData = {
actor: this.actor,
competence: duplicate(this.actor.getBestDraconic()),
tmr: tmr,
canClose: false,
diffLibre: -9,
forceCarac: { 'reve-actuel': { label: "Rêve Actuel", value: this.actor.getReveActuel() } },
maitrise: { verbe: 'conquérir', action: 'Conquérir la cité' }
}
);
dialog.render(true);
}
rollData.competence.data.defaut_carac = 'reve-actuel';
async _maitriseCaseHumide(rollData) {
await this._maitriserTMR(rollData, r => this._resultatConqueteCiteFermee(r));
}
}
async _resultatConqueteCiteFermee(rollData) {
if (rollData.rolled.isETotal) {
rollData.souffle = await this.actor.ajouterSouffle({ chat: false });
}
this.toclose = rollData.rolled.isEchec;
if (rollData.rolled.isSuccess) {
if (!rollData.previous && this.actor.isDoubleResistanceFleuve()) {
ChatMessage.create({
content: `Double résistance du fleuve: `,
whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name)
});
rollData.previous = [rollData.rolled];
await this._rollMaitriseCaseHumide(rollData);
return;
}
}
rollData.poesie = Poetique.getExtrait();
ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-fleuve-tmr.html`, rollData)
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-resultat-maitrise-tmr.html`, rollData)
});
if (rollData.rolled.isEchec) {
this.close();
}
else {
const citeFermee = this.actor.data.items.find(it => EffetsDraconiques.isCiteFermee(it, rollData.tmr.coord));
this.actor.deleteOwnedItem(citeFermee._id);
this._removeTokens(t => t.coordTMR() == citeFermee.data.coord && t.caseSpeciale?._id == citeFermee._id);
}
}
/* -------------------------------------------- */
isReserveExtensible(coordTMR) {
for (let caseTMR of this.casesSpeciales) {
if (caseTMR.data.specific == 'reserve_extensible' && caseTMR.data.coord == coordTMR)
return true;
}
return false;
async _maitriserTMR(rollData, callbackMaitrise) {
this.minimize(); // Hide
const dialog = await RdDRoll.create(this.actor, rollData,
{
html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-maitrise-tmr.html',
options: { height: 350 },
close: html => { this.maximize(); } // Re-display TMR
},
{
name: rollData.maitrise.verbe, label: rollData.maitrise.action,
callbacks: [
this.actor.createCallbackExperience(),
{ action: callbackMaitrise }
]
}
);
dialog.render(true);
}
/* -------------------------------------------- */
@ -466,9 +582,9 @@ export class RdDTMRDialog extends Dialog {
return;
}
let sortReserveList = TMRUtility.getSortReserveList(this.sortReserves, coordTMR);
let sortReserveList = TMRUtility.getSortReserveList(this.sortsReserves, coordTMR);
if (sortReserveList.length > 0) {
if (this.actor.isReserveEnSecurite() || this.isReserveExtensible(coordTMR)) {
if (EffetsDraconiques.isReserveEnSecurite(this.actor) || this.isReserveExtensible(coordTMR)) {
let msg = "Vous êtes sur une case avec un Sort en Réserve. Grâce à votre Tête <strong>Reserve en Sécurité</strong> ou <strong>Réserve Exensible</strong>, vous pouvez contrôler le déclenchement. Cliquez si vous souhaitez le déclencher : <ul>";
for (let sortReserve of sortReserveList) {
msg += "<li><a class='chat-card-button' id='sort-reserve' data-actor-id='" + this.actor._id + "' data-tmr-coord='" + coordTMR + "' data-sort-id='" + sortReserve.sort._id + "'>" + sortReserve.sort.name + "</a></li>";
@ -483,9 +599,10 @@ export class RdDTMRDialog extends Dialog {
}
}
}
/* -------------------------------------------- */
lancerSortEnReserve(coordTMR, sortId) {
let sortReserveList = TMRUtility.getSortReserveList(this.sortReserves, coordTMR);
let sortReserveList = TMRUtility.getSortReserveList(this.sortsReserves, coordTMR);
let sortReserve = sortReserveList.find(sortReserve => sortReserve.sort._id == sortId);
//console.log("SORT RESA", sortReserveList, coordTMR, sortId, sortReserve);
if (sortReserve) {
@ -535,24 +652,34 @@ export class RdDTMRDialog extends Dialog {
}
/* -------------------------------------------- */
isTerreAttache(coordTMR) {
for (let caseTMR of this.casesSpeciales) {
if (caseTMR.data.specific == 'attache' && caseTMR.data.coord == coordTMR) { // Match !
return true;
}
}
return false;
isCaseInondee(coord) {
return this.casesSpeciales.find(c => EffetsDraconiques.isCaseInondee(c, coord));
}
isCiteFermee(coord) {
return this.casesSpeciales.find(c => EffetsDraconiques.isCiteFermee(c, coord));
}
/* -------------------------------------------- */
checkConnaissanceFleuve(currentTMR, nextTMR) {
if (this.actor.isConnaissanceFleuve()) {
//console.log(currentTMR, nextTMR );
if (TMRUtility.getTMR(currentTMR).type == 'fleuve' && TMRUtility.getTMR(nextTMR).type == 'fleuve') {
return true;
}
}
return false;
isTerreAttache(coord) {
return this.casesSpeciales.find(c => EffetsDraconiques.isTerreAttache(c, coord));
}
/* -------------------------------------------- */
isCaseMaitrisee(coord) {
return this.casesSpeciales.find(c => EffetsDraconiques.isCaseMaitrisee(c, coord));
}
/* -------------------------------------------- */
isReserveExtensible(coord) {
return this.casesSpeciales.find(c => EffetsDraconiques.isReserveExtensible(c, coord));
}
/* -------------------------------------------- */
isConnaissanceFleuve(currentTMR, nextTMR) {
return TMRUtility.getTMR(currentTMR).type == 'fleuve' &&
TMRUtility.getTMR(nextTMR).type == 'fleuve' &&
EffetsDraconiques.isConnaissanceFleuve(this.actor);
}
/* -------------------------------------------- */
@ -562,7 +689,7 @@ export class RdDTMRDialog extends Dialog {
}
let origEvent = event.data.originalEvent;
let tmrObject = event.target.tmrObject;
let tmrObject = this;
let eventPos = RdDTMRDialog._computeEventPos(origEvent);
await tmrObject._onClickTMRPos(eventPos); // Vérifier l'état des compteurs reve/fatigue/vie
@ -579,7 +706,7 @@ export class RdDTMRDialog extends Dialog {
// Validation de la case de destination (gestion du cas des rencontres qui peuvent téléporter)
let deplacementType = 'erreur';
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)) {
if (this.isTerreAttache(targetCoordTMR) || this.isConnaissanceFleuve(currentCoordTMR, targetCoordTMR) || !RdDTMRDialog._horsDePortee(currentPos, eventPos)) {
deplacementType = 'normal';
}
} else {
@ -644,6 +771,7 @@ export class RdDTMRDialog extends Dialog {
async postRencontre(tmr) {
await this.manageCaseHumide(tmr);
await this.conquerirCiteFermee(tmr);
await this.declencheSortEnReserve(tmr.coord);
await this.actor.checkSoufflePeage(tmr);
}
@ -658,76 +786,11 @@ export class RdDTMRDialog extends Dialog {
await this.actor.updateCoordTMR(coordTMR);
this._updateDemiReve();
let tmr = TMRUtility.getTMR(coordTMR);
this.manageCaseHumide(tmr);
await this.manageCaseHumide(tmr);
await this.conquerirCiteFermee(tmr);
await this.declencheSortEnReserve(tmr.coord);
}
/* -------------------------------------------- */
async activateListeners(html) {
super.activateListeners(html);
var row = document.getElementById("tmrrow1");
var cell1 = row.insertCell(1);
cell1.append(this.pixiApp.view);
if (this.viewOnly) {
html.find('#lancer-sort').remove();
}
else {
// Roll Sort
html.find('#lancer-sort').click((event) => {
this.actor.rollUnSort(this.actor.data.data.reve.tmrpos.coord);
});
}
// load the texture we need
await this.pixiApp.loader
.add('tmr', 'systems/foundryvtt-reve-de-dragon/styles/img/ui/tmp_main_r1.webp')
.add('demi-reve', "icons/svg/sun.svg")
.load((loader, resources) => {
// This creates a texture from a TMR image
const mytmr = new PIXI.Sprite(resources.tmr.texture);
// Setup the position of the TMR
mytmr.x = 0;
mytmr.y = 0;
mytmr.width = 720;
mytmr.height = 860;
// Rotate around the center
mytmr.anchor.x = 0;
mytmr.anchor.y = 0;
mytmr.interactive = true;
mytmr.buttonMode = true;
mytmr.tmrObject = this;
if (!this.viewOnly) {
mytmr.on('pointerdown', this.onClickTMR);
}
this.pixiApp.stage.addChild(mytmr);
this._addDemiReve();
this.displayPreviousRencontres();
this.displaySortReserve();
this.displaySpecificCase();
});
if (this.viewOnly) {
return;
}
// Gestion du cout de montée en points de rêve
let reveCout = (this.tmrdata.isRapide && !this.actor.checkTeteDeplacementAccelere()) ? -2 : -1;
this.cumulFatigue += this.fatigueParCase;
reveCout -= this.actor.checkMonteeLaborieuse();
await this.actor.reveActuelIncDec(reveCout);
// Le reste...
this.updateValuesDisplay();
let tmr = TMRUtility.getTMR(this.actor.data.data.reve.tmrpos.coord);
await this.manageRencontre(tmr, () => {
this.postRencontre(tmr);
this.actor.displayTMRQueueSouffleInformation();
});
}
/* -------------------------------------------- */
static _computeEventPos(origEvent) {
let canvasRect = origEvent.target.getBoundingClientRect();
@ -747,125 +810,16 @@ export class RdDTMRDialog extends Dialog {
|| (target.y == 0 && target.y < origin.y && target.x != origin.x && origin.x % 2 == 1);
}
/* -------------------------------------------- */
_tokenRencontre(rencontre) {
let sprite = new PIXI.Graphics();
sprite.beginFill(0x767610, 0.6);
sprite.drawCircle(0, 0, 6);
sprite.endFill();
sprite.decallage = {
x: (tmrConstants.cellw / 2) - 16,
y: 16 - (tmrConstants.cellh / 2)
};
return { sprite: sprite, rencontre: rencontre, coordTMR: () => rencontre.coord };
}
/* -------------------------------------------- */
_tokenTrouNoir(coord) {
let sprite = new PIXI.Graphics();
sprite.beginFill(0x050505, 0.8);
sprite.drawCircle(0, 0, (tmrConstants.cellw / 2) - 2);
sprite.endFill();
sprite.decallage = {
x: 0,
y: 2
}
return { sprite: sprite, coordTMR: () => coord }
}
/* -------------------------------------------- */
_tokenDebordement(coord) {
let sprite = new PIXI.Graphics();
sprite.beginFill(0x0101FE, 0.3);
sprite.drawCircle(0, 0, (tmrConstants.cellw / 2) - 2);
sprite.endFill();
sprite.decallage = {
x: 0,
y: 2
}
return { sprite: sprite, coordTMR: () => coord }
}
/* -------------------------------------------- */
_tokenMaitrisee(coord) {
let sprite = new PIXI.Graphics();
sprite.beginFill(0x1010F0, 0.8);
sprite.drawCircle(0, 0, 6);
sprite.endFill();
sprite.decallage = {
x: 16 - (tmrConstants.cellw / 2),
y: 16 - (tmrConstants.cellh / 2)
}
return { sprite: sprite, coordTMR: () => coord }
}
/* -------------------------------------------- */
_tokenTerreAttache(coord) {
let sprite = new PIXI.Graphics();
sprite.beginFill(0x10F010, 0.8);
sprite.drawCircle(0, 0, 6);
sprite.endFill();
sprite.decallage = {
x: 16 - (tmrConstants.cellw / 2),
y: 16 - (tmrConstants.cellh / 2)
}
return { sprite: sprite, coordTMR: () => coord }
}
/* -------------------------------------------- */
_tokenSortEnReserve(sort) {
let sprite = new PIXI.Graphics();
sprite.beginFill(0x101010, 0.8);
sprite.drawCircle(0, 0, 6);
sprite.endFill();
sprite.decallage = {
x: 16 - (tmrConstants.cellw / 2),
y: 16 - (tmrConstants.cellh / 2)
}
return { sprite: sprite, sort: sort, coordTMR: () => sort.coord }
}
/* -------------------------------------------- */
_tokenDemiReve() {
let texture = PIXI.utils.TextureCache['demi-reve'];
let sprite = new PIXI.Sprite(texture);
sprite.width = tmrConstants.cellw * 0.7;
sprite.height = tmrConstants.cellh * 0.7;
sprite.anchor.set(0.5);
sprite.tint = 0x00FFEE;
return { sprite: sprite, actor: this.actor, coordTMR: () => this.actor.data.data.reve.tmrpos.coord }
}
/* -------------------------------------------- */
_addDemiReve() {
this.demiReve = this._tokenDemiReve();
this._setTokenPosition(this.demiReve);
this.pixiApp.stage.addChild(this.demiReve.sprite);
}
/* -------------------------------------------- */
_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.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;
return { x: x, y: y, w: tmrConstants.cellw, h: tmrConstants.cellh }
return this.pixiTMR.getCaseRectangle(TMRUtility.convertToCellPos(coord));
}
/* -------------------------------------------- */
_setTokenPosition(token) {
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;
token.sprite.x = tmrConstants.gridx + (coordXY.x * tmrConstants.cellw) + dx;
token.sprite.y = tmrConstants.gridy + (coordXY.y * tmrConstants.cellh) + dy + decallagePairImpair;
this.pixiTMR.setPosition(token.sprite, TMRUtility.convertToCellPos(token.coordTMR()));
}
/* -------------------------------------------- */
@ -878,10 +832,9 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */
_trackToken(token) {
this.allTokens.push(token)
this.allTokens.push(token);
this._setTokenPosition(token);
this.pixiApp.stage.addChild(token.sprite);
}
}

View File

@ -9,7 +9,7 @@ export class RdDTMRRencontreDialog extends Dialog {
buttons: {
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()) }
maitiser: { icon: '<i class="fas fa-check"></i>', label: "Maîtriser", callback: () => this.onButtonAction(() => tmrApp.maitriserRencontre()) }
},
default: "derober"
};

View File

@ -112,8 +112,8 @@ export const referenceAjustements = {
getDescr: (rollData, actor) => RdDBonus.find(actor.getSurprise()).descr
},
bonusCase: {
isUsed: (rollData, actor) => rollData.selectedSort && rollData.coord,
getDescr: (rollData, actor) => rollData.selectedSort && rollData.coord ? `Bonus de case: ${RdDItemSort.getCaseBonus(rollData.selectedSort, rollData.coord)}%` : ''
isUsed: (rollData, actor) => rollData.selectedSort && rollData.tmr.coord,
getDescr: (rollData, actor) => rollData.selectedSort && rollData.tmr.coord ? `Bonus de case: ${RdDItemSort.getCaseBonus(rollData.selectedSort, rollData.tmr.coord)}%` : ''
},
rencontreTMR: {
isVisible: (rollData, actor) => rollData.tmr && rollData.rencontre?.name,

View File

@ -86,7 +86,7 @@ const typeRencontres = {
changeur: {
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);
data.newTMR = TMRUtility.getTMRAleatoire(it => it.type = 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) => {
@ -230,7 +230,7 @@ const typeRencontres = {
msgSucces: (data) => `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`,
msgEchec: (data) => `A tout seigneur, tout honneur, vous faites face à un ${data.rencontre.name}. La rencontre tourne au cauchemar, dans la lutte épique, vous subissez ${data.rolled.isETotal ? 'deux queues' : 'une queue'} de dragon!`,
postSucces: (tmrDialog, data) => TMRRencontres.onPostSuccessReveDeDragon(tmrDialog, data),
postEchec: (tmrDialog, data) => TMRRencontres.onPostSuccessReveDeDragon(tmrDialog, data),
postEchec: (tmrDialog, data) => TMRRencontres.onPostEchecReveDeDragon(tmrDialog, data),
poesieSucces: {
reference: "Rêve de Dragon, Denis Gerfaud",
extrait: `Le monde est Rêve de Dragons, mais nous ne savons
@ -475,11 +475,11 @@ export class TMRRencontres {
if (data.rolled.isPart) {
await data.actor.appliquerExperience(data.rolled, 'reve', data.competence);
}
await data.actor.resultCombattreReveDeDragon(data.rolled);
await data.actor.resultCombatReveDeDragon(data);
}
static async onPostEchecReveDeDragon(tmrDialog, data) {
await data.actor.resultCombattreReveDeDragon(data.rolled);
await data.actor.resultCombatReveDeDragon(data);
tmrDialog.close();
}
}

View File

@ -227,68 +227,6 @@ export const TMRType = {
desolation: { name: "désolation", genre: "f" }
}
export const poesieHautReve = [
{
reference: 'Le Ratier Bretonien',
extrait: `Le courant du Fleuve
<br>Te domine et te Porte
<br>Avant que tu te moeuves
<br>Combat le, ou il t'emporte`
},
{
reference: 'Incompatibilité, Charles Beaudelaire',
extrait: `Et lorsque par hasard une nuée errante
<br>Assombrit dans son vol le lac silencieux,
<br>On croirait voir la robe ou l'ombre transparente
<br>D'un esprit qui voyage et passe dans les cieux.`
},
{
reference: 'Au fleuve de Loire, Joachim du Bellay',
extrait: `Ô de qui la vive course
<br>Prend sa bienheureuse source,
<br>Dune argentine fontaine,
<br>Qui dune fuite lointaine,
<br>Te rends au sein fluctueux
<br>De lOcéan monstrueux`
},
{
reference: 'Denis Gerfaud',
extrait: `Et l'on peut savoir qui est le maître d'Oniros, c'est le Fleuve de l'Oubli.
Et l'on sait qui est le créateur du Fleuve de l'Oubli, c'est Hypnos et Narcos.
Mais l'on ne sait pas qui est le maître du Fleuve de l'Oubli,
sinon peut-être lui-même, ou peut-être Thanatos` },
{
reference: 'Denis Gerfaud',
extrait: `Narcos est la source du Fleuve de l'Oubli et Hypnos l'embouchure
Remonter le Fleuve est la Voie de la Nuit, la Voie du Souvenir.
Descendre le Fleuve est la Voie du Jour, la Voie de l'Oubli`
},
{
reference: 'Denis Gerfaud',
extrait: `Narcos engendre le fils dont il est la mère à l'heure du Vaisseau,
car Oniros s'embarque pour redescendre le Fleuve
vers son père Hypnos sur la Voie de l'Oubli`
},
{
reference: 'Denis Gerfaud',
extrait: `Hypnos engendre le fils dont il est la mère à l'heure du Serpent, car
tel les serpents, Oniros commence à remonter le Fleuve
sur le Voie du Souvenir vers son père Narcos`
},
{
reference: 'Denis Gerfaud',
extrait: `Ainsi se cuccèdent les Jours et les Ages. Les jours des Dragons sont les Ages des Hommes`
},
{
reference: 'Denis Gerfaud',
extrait: `Ainsi parlent les sages:
&laquo;Les Dragons sont créateurs de leurs rêves, mais ils ne sont pas créateurs d'Oniros
Les Dragons ne sont pas les maîtres de leurs rêvezs, car ils ne sont pas maîtres d'Oniros.
Nul ne sait qui est le créateur des Dragons, ni qui est leur maître.
Mais l'on peut supposer qui est le maître du Rêve des Dragons, c'est Oniros&raquo;`
},
]
/* -------------------------------------------- */
const caseSpecificModes = ["attache", "trounoir", "debordement", "reserve_extensible", "maitrisee"];
@ -309,9 +247,36 @@ export const tmrConstants = {
cellw: 55,
cellh: 55,
gridx: 28,
gridy: 28
gridy: 28,
// tailles
third: 18,
half: 27.5,
twoThird: 36,
full: 55,
// decallages
center: { x: 0, y: 0 },
top: { x: 0, y: -11.5 },
topLeft: { x: -11.5, y: -11.5 },
left: { x: -11.5, y: 0 },
bottomLeft: { x: -11.5, y: 11.5 },
bottom: { x: 0, y: 11.5 },
bottomRight: { x: 11.5, y: 11.5 },
right: { x: 11.5, y: 0 },
topRight: { x: 11.5, y: -11.5 },
}
// couleurs
export const tmrColors = {
sort: 0xFF8800,
tetes: 0xA000FF,
souffle: 0x804040,
trounoir: 0x401060,
demireve: 0x00FFEE,
rencontre: 0xFF0000,
casehumide: 0x1050F0,
}
/* -------------------------------------------- */
/* -------------------------------------------- */
@ -355,6 +320,9 @@ export class TMRUtility {
static getTMR(coordTMR) {
return TMRMapping[coordTMR];
}
static isCaseHumide(tmr) {
return tmr.type == 'fleuve' || tmr.type == 'lac' || tmr.type == 'marais';
}
/* -------------------------------------------- */
/** Some debug functions */
@ -416,12 +384,16 @@ export class TMRUtility {
return TMRType[terrain].list;
}
static getListCoordTMR(terrain) {
return this.getListTMR(terrain).map(it => it.coord);
static filterTMR(filter) {
return Object.values(TMRMapping).filter(filter);
}
static getTMRAleatoire(terrain = undefined) {
let list = terrain ? TMRUtility.getListTMR(terrain) : Object.values(TMRMapping);
static filterTMRCoord(filter) {
return TMRUtility.filterTMR(filter).map(it => it.coord);
}
static getTMRAleatoire(filter = undefined) {
let list = TMRUtility.filterTMR(filter);
let index = new Roll("1d" + list.length).evaluate().total - 1;
return list[index];
}

20
module/tmr/carte-tmr.js Normal file
View File

@ -0,0 +1,20 @@
import { Draconique } from "./draconique.js";
export class CarteTmr extends Draconique {
constructor() {
super();
}
type() { return '' }
match(item) { return false; }
manualMessage() { return false }
async onActorCreateOwned(actor, item) { }
code() { return 'tmr' }
img() { return 'systems/foundryvtt-reve-de-dragon/styles/img/ui/tmp_main_r1.webp' }
_createSprite(pixiTMR) {
return pixiTMR.carteTmr(this.code());
}
}

31
module/tmr/debordement.js Normal file
View File

@ -0,0 +1,31 @@
import { tmrColors, tmrConstants, TMRUtility } from "../tmr-utility.js";
import { Draconique } from "./draconique.js";
export class Debordement extends Draconique {
constructor() {
super();
}
type() { return 'souffle' }
match(item) { return Draconique.isSouffleDragon(item) && item.name.toLowerCase().includes('trou noir'); }
manualMessage() { return false }
async onActorCreateOwned(actor, item) { await this._creerCaseTmr(actor); }
code() { return 'debordement' }
tooltip(linkData) { return `Débordement en ${TMRUtility.getTMR(linkData.data.coord).label}` }
img() { return 'systems/foundryvtt-reve-de-dragon/icons/svg/wave.svg' }
_createSprite(pixiTMR) {
return pixiTMR.sprite(this.code(),
{
color: tmrColors.casehumide, alpha: 0.5, taille: tmrConstants.twoThird, decallage: tmrConstants.bottom
});
}
async _creerCaseTmr(actor) {
const existants = actor.data.items.filter(it => this.isCase(it)).map(it => it.data.coord);
const tmr = TMRUtility.getTMRAleatoire(it => !(TMRUtility.isCaseHumide(it) || existants.includes(it.coord)));
await this.createCaseTmr(actor, 'Debordement: ' + tmr.label, tmr);
}
}

27
module/tmr/demi-reve.js Normal file
View File

@ -0,0 +1,27 @@
import { tmrColors, tmrConstants } from "../tmr-utility.js";
import { Draconique } from "./draconique.js";
export class DemiReve extends Draconique {
constructor() {
super();
}
type() { return '' }
match(item) { return false; }
manualMessage() { return false }
async onActorCreateOwned(actor, item) { }
code() { return 'demi-reve' }
tooltip(linkData) { return `Demi-rêve` }
img() { return 'icons/svg/sun.svg' }
_createSprite(pixiTMR) {
const sprite = pixiTMR.sprite(this.code(), {
color: tmrColors.demireve,
taille: (tmrConstants.full * 0.7)
});
pixiTMR.animate(pixiApp => pixiApp.ticker.add((delta) => sprite.rotation -= 0.01 * delta));
return sprite;
}
}

118
module/tmr/draconique.js Normal file
View File

@ -0,0 +1,118 @@
import { PixiTMR } from "./pixi-tmr.js";
const registeredEffects = [
]
/**
* Définition des informations d'une "draconique" (queue, ombre, tête, souffle) qui influence les TMR
*/
export class Draconique
{
static isCaseTMR(element) { return element.type == 'casetmr'; }
static isQueueDragon(element) { return element.type == 'queue' || element.type == 'ombre'; }
static isSouffleDragon(element) { return element.type == 'souffle'; }
static isTeteDragon(element) { return element.type == 'tete'; }
static isQueueSouffle(it) { return Draconique.isQueueDragon(it) || Draconique.isSouffleDragon(it); }
static register(draconique) {
registeredEffects[draconique.code()] = draconique;
if (draconique.img()) {
PixiTMR.register(draconique.code(), draconique.img())
}
return draconique;
}
static all() {
return Object.values(registeredEffects);
}
static get(code) {
return registeredEffects[code];
}
/**
* @param item un Item quelconque
* @returns true si l'item correspond
*/
match(item) {
return Draconique.isQueueDragon(item) || Draconique.isSouffleDragon(item) || Draconique.isTeteDragon(item);
}
/**
* @returns un message à afficher si la draconique doit être gérée manuellement.
*/
manualMessage() {
return false;
}
/**
* Méthode responsable de gérer une draconique (par exemple, ajouter des casetmr pour la fermeture des cités).
* @param actor auquel la draconique est ajoutée
*/
async onActorCreateOwned(actor) {
return false;
}
async onActorDeleteOwned(actor) {
return false;
}
/**
* @return le code interne utilisé pour les casetmr correpondant
*/
code() { return undefined }
/**
* @param {*} linkData données associées au token pixi (une casetmr, un sort en réserve, une rencontre en attente)
* @returns un tooltip à afficher au dessus du token
*/
tooltip(linkData) { return undefined }
/**
* @param {*} img l'url du fichier image à utiliser pour le token. Si indéfini (et si createSprite n'est pas surchargé),
* un disque est utilisé.
*/
img() { return undefined }
/**
* factory d'élément graphique PIXI correpsondant à l'objet draconique
* @param {*} pixiTMR instance de PixiTMR qui gère les tooltips, les méthodes de création de sprite standard, les clicks.
*/
token(pixiTMR, linkData, coordTMR, type = undefined) {
const token = {
sprite: this._createSprite(pixiTMR),
coordTMR: coordTMR
};
token[type ?? this.code()] = linkData;
pixiTMR.addTooltip(token.sprite, this.tooltip(linkData));
return token;
return sprite;
}
/**
* factory d'élément graphique PIXI correpsondant à l'objet draconique
* @param {*} pixiTMR instance de PixiTMR qui gère les tooltips, les méthodes de création de sprite standard, les clicks.
*/
_createSprite(pixiTMR) {
if (this.img()) {
return pixiTMR.sprite(this.code());
}
else{
return pixiTMR.circle()
}
}
/**
*
* @param {*} it un item à tester
* @param {*} coord les coordonnées d'une case. Si undefined toute case du type correspondra,
*/
isCase(it, coord = undefined) {
return Draconique.isCaseTMR(it) && it.data.specific == this.code() && (coord ? it.data.coord == coord : true);
}
async createCaseTmr(actor, label, tmr) {
await actor.createOwnedItem({
name: label, type: 'casetmr', img: this.img(), _id: randomID(16),
data: { coord: tmr.coord, specific: this.code() }
});
}
}

View File

@ -0,0 +1,152 @@
import { Debordement } from "./debordement.js";
import { FermetureCites } from "./fermeture-cites.js";
import { QueteEaux } from "./quete-eaux.js";
import { TerreAttache } from "./terre-attache.js";
import { ReserveExtensible } from "./reserve-extensible.js";
import { DemiReve } from "./demi-reve.js";
import { TrouNoir } from "./trou-noir.js";
import { Rencontre } from "./rencontre.js";
import { SortReserve } from "./sort-reserve.js";
import { CarteTmr } from "./carte-tmr.js";
import { PontImpraticable } from "./pont-impraticable.js";
import { Draconique } from "./draconique.js";
import { PresentCites } from "./present-cites.js";
export class EffetsDraconiques {
static carteTmr = new CarteTmr();
static demiReve = new DemiReve();
static rencontre = new Rencontre();
static sortReserve = new SortReserve();
static debordement = new Debordement();
static presentCites = new PresentCites();
static fermetureCites = new FermetureCites();
static queteEaux = new QueteEaux();
static reserveExtensible = new ReserveExtensible();
static terreAttache = new TerreAttache();
static trouNoir = new TrouNoir();
static pontImpraticable = new PontImpraticable();
static init() {
Draconique.register(EffetsDraconiques.carteTmr);
Draconique.register(EffetsDraconiques.demiReve);
Draconique.register(EffetsDraconiques.rencontre);
Draconique.register(EffetsDraconiques.sortReserve);
Draconique.register(EffetsDraconiques.debordement);
Draconique.register(EffetsDraconiques.fermetureCites);
Draconique.register(EffetsDraconiques.queteEaux);
Draconique.register(EffetsDraconiques.reserveExtensible);
Draconique.register(EffetsDraconiques.terreAttache);
Draconique.register(EffetsDraconiques.trouNoir);
Draconique.register(EffetsDraconiques.pontImpraticable);
Draconique.register(EffetsDraconiques.presentCites);
}
/* -------------------------------------------- */
static isCaseInondee(caseTMR, coord) {
return EffetsDraconiques.debordement.isCase(caseTMR, coord) || EffetsDraconiques.pontImpraticable.isCase(caseTMR, coord);
}
static isCaseTrouNoir(caseTMR, coord) {
return EffetsDraconiques.trouNoir.isCase(caseTMR, coord);
}
static isReserveExtensible(caseTMR, coord) {
return EffetsDraconiques.reserveExtensible.isCase(caseTMR, coord);
}
static isTerreAttache(caseTMR, coord) {
return EffetsDraconiques.terreAttache.isCase(caseTMR, coord);
}
static isCiteFermee(caseTMR, coord) {
return EffetsDraconiques.fermetureCites.isCase(caseTMR, coord);
}
static isPresentCite(caseTMR, coord) {
return EffetsDraconiques.presentCites.isCase(caseTMR, coord);
}
/* -------------------------------------------- */
static isMauvaiseRencontre(element) {
return EffetsDraconiques.isMatching(element, it => Draconique.isQueueSouffle(it) && it.name.toLowerCase().includes('mauvaise rencontre'));
}
static isMonteeLaborieuse(element) {
return EffetsDraconiques.isMatching(element, it => Draconique.isQueueSouffle(it) && it.name.toLowerCase().includes('montée laborieuse'));
}
static isFermetureCite(element) {
/* -------------------------------------------- */
return EffetsDraconiques.isMatching(element, it => EffetsDraconiques.fermetureCites.match(it));
}
static isPontImpraticable(element) {
return EffetsDraconiques.isMatching(element, it => EffetsDraconiques.pontImpraticable.match(it));
}
static isDoubleResistanceFleuve(element) {
return EffetsDraconiques.isMatching(element, it => Draconique.isSouffleDragon(it) && it.name.toLowerCase().includes('résistance du fleuve'));
}
static isPeage(element) {
return EffetsDraconiques.isMatching(element, it => Draconique.isSouffleDragon(it) && it.name.toLowerCase().includes('péage'));
}
static isPeriple(element) {
// TODO
return EffetsDraconiques.isMatching(element, it => Draconique.isSouffleDragon(it) && ir.name.toLowerCase() == 'périple');
}
static isDesorientation(element) {
// TODO
return EffetsDraconiques.isMatching(element, it => Draconique.isSouffleDragon(it) && it.name.toLowerCase() == 'désorientation');
}
/* -------------------------------------------- */
static isConquete(element) {
// TODO
return EffetsDraconiques.isMatching(element, it => Draconique.isQueueDragon(it) && it.name.toLowerCase() == 'conquête');
}
static isPelerinage(element) {
return EffetsDraconiques.isMatching(element, it => Draconique.isQueueDragon(it) && it.name.toLowerCase() == 'pélerinage');
}
static countInertieDraconique(element) {
return EffetsDraconiques.count(element, it => Draconique.isQueueDragon(it) && it.name.toLowerCase().includes('inertie draconique'));
}
static isUrgenceDraconique(element) {
return EffetsDraconiques.isMatching(element, it => Draconique.isQueueDragon(it) && it.name.toLowerCase() == 'urgence draconique');
}
/* -------------------------------------------- */
static isDonDoubleReve(element) {
return EffetsDraconiques.isMatching(element, it => Draconique.isTeteDragon(it) && it.name == 'Don de double-rêve');
}
static isConnaissanceFleuve(element) {
return EffetsDraconiques.isMatching(element, it => Draconique.isTeteDragon(it) && it.name.toLowerCase().includes('connaissance du fleuve'));
}
static isReserveEnSecurite(element) {
return EffetsDraconiques.isMatching(element, it => Draconique.isTeteDragon(it) && it.name.toLowerCase().includes(' en sécurité'));
}
static isDeplacementAccelere(element) {
return EffetsDraconiques.isMatching(element, it => Draconique.isTeteDragon(it) && it.name.toLowerCase().includes(' déplacement accéléré'));
}
static isMatching(element, matcher) {
return EffetsDraconiques.toItems(element).find(matcher);
}
static count(element, matcher) {
return EffetsDraconiques.toItems(element).filter(matcher).length;
}
static toItems(element) {
return (element?.entity === 'Actor') ? element.data.items : (element?.entity === 'Item') ? [element] : [];
}
}

View File

@ -0,0 +1,33 @@
import { tmrColors, tmrConstants, TMRUtility } from "../tmr-utility.js";
import { Draconique } from "./draconique.js";
export class FermetureCites extends Draconique {
constructor() {
super();
}
type() { return 'souffle' }
match(item) { return Draconique.isSouffleDragon(item) && item.name.toLowerCase() == 'fermeture des cités'; }
manualMessage() { return false }
async onActorCreateOwned(actor, item) { await this._fermerLesCites(actor); }
code() { return 'fermeture' }
tooltip(linkData) { return `La ${TMRUtility.getTMR(linkData.data.coord).label} est fermée` }
img() { return 'icons/svg/door-closed.svg' }
_createSprite(pixiTMR) {
return pixiTMR.sprite(this.code(),
{
color: tmrColors.souffle, alpha: 0.9, taille: tmrConstants.full, decallage: { x: 2, y: 0 }
});
}
async _fermerLesCites(actor) {
let existants = actor.data.items.filter(it => this.isCase(it)).map(it => it.data.coord);
let ouvertes = TMRUtility.filterTMR(it => it.type == 'cite' && !existants.includes(it.coord));
for (let tmr of ouvertes) {
await this.createCaseTmr(actor, 'Fermeture: ' + tmr.label, tmr);
}
}
}

148
module/tmr/pixi-tmr.js Normal file
View File

@ -0,0 +1,148 @@
import { tmrConstants } from "../tmr-utility.js";
const tooltipStyle = new PIXI.TextStyle({
fontFamily: 'CaslonAntique',
fontSize: 18,
fill: '#FFFFFF',
stroke: '#000000',
strokeThickness: 3
});
export class PixiTMR {
static textures = []
constructor(tmrObject, pixiApp) {
this.tmrObject = tmrObject;
this.pixiApp = pixiApp ?? tmrObject.pixiApp;
this.callbacksOnAnimate = [];
}
load(onLoad = (loader, resources) => {}) {
let loader = this.pixiApp.loader;
for (const [name, img] of Object.entries(PixiTMR.textures)) {
loader = loader.add(name, img);
}
loader.load((loader, resources) => {
onLoad(loader, resources);
for (let onAnimate of this.callbacksOnAnimate) {
onAnimate();
}
});
}
static register(name, img) {
PixiTMR.textures[name] = img;
}
animate(animation = pixiApp=>{})
{
this.callbacksOnAnimate.push(() => animation(this.pixiApp));
}
carteTmr(code) {
const carteTmr = new PIXI.Sprite(PIXI.utils.TextureCache[code]);
// Setup the position of the TMR
carteTmr.x = 0;
carteTmr.y = 0;
carteTmr.width = 720;
carteTmr.height = 860;
// Rotate around the center
carteTmr.anchor.set(0);
carteTmr.interactive = true;
carteTmr.buttonMode = true;
carteTmr.tmrObject = this;
// atténue les couleurs des TMRs
const tmrColorFilter = new PIXI.filters.ColorMatrixFilter();
tmrColorFilter.contrast(1);
tmrColorFilter.brightness(0.2);
tmrColorFilter.saturate(-0.5);
carteTmr.filters = [tmrColorFilter];
if (!this.tmrObject.viewOnly) {
carteTmr.on('pointerdown', event => this.onClickBackground(event));
}
this.pixiApp.stage.addChild(carteTmr);
return carteTmr;
}
sprite(code, options = {}) {
const texture = PIXI.utils.TextureCache[code];
if (!texture) {
console.error("Texture manquante", code)
return;
}
let sprite = new PIXI.Sprite(texture);
sprite.width = options.taille ?? tmrConstants.half;
sprite.height = options.taille ?? tmrConstants.half;
sprite.anchor.set(0.5);
sprite.tint = options.color ?? 0x000000;
sprite.alpha = options.alpha ?? 0.75;
sprite.decallage = options.decallage ?? tmrConstants.center;
this.pixiApp.stage.addChild(sprite);
return sprite;
}
circle(name, options = {}) {
let sprite = new PIXI.Graphics();
sprite.beginFill(options.color, options.opacity);
sprite.drawCircle(0, 0, (options.taille ?? 12) / 2);
sprite.endFill();
sprite.decallage = options.decallage ?? tmrConstants.topLeft;
this.pixiApp.stage.addChild(sprite);
return sprite;
}
addTooltip(sprite, text) {
if (text) {
sprite.tooltip = new PIXI.Text(text, tooltipStyle);
sprite.isOver = false;
sprite.interactive = true;
sprite.on('pointerdown', event => this.onClickBackground(event))
.on('pointerover', () => this.onShowTooltip(sprite))
.on('pointerout', () => this.onHideTooltip(sprite));
}
}
onClickBackground(event) {
this.tmrObject.onClickTMR(event)
}
onShowTooltip(sprite) {
if (sprite.tooltip) {
if (!sprite.isOver) {
sprite.tooltip.x = sprite.x;
sprite.tooltip.y = sprite.y;
this.pixiApp.stage.addChild(sprite.tooltip);
}
sprite.isOver = true;
}
}
onHideTooltip(sprite) {
if (sprite.tooltip) {
if (sprite.isOver) {
this.pixiApp.stage.removeChild(sprite.tooltip);
}
sprite.isOver = false;
}
}
setPosition( sprite, pos) {
let decallagePairImpair = (pos.x % 2 == 0) ? tmrConstants.col1_y : tmrConstants.col2_y;
let dx = (sprite.decallage == undefined) ? 0 : sprite.decallage.x;
let dy = (sprite.decallage == undefined) ? 0 : sprite.decallage.y;
sprite.x = tmrConstants.gridx + (pos.x * tmrConstants.cellw) + dx;
sprite.y = tmrConstants.gridy + (pos.y * tmrConstants.cellh) + dy + decallagePairImpair;
}
getCaseRectangle(pos) {
let decallagePairImpair = (pos.x % 2 == 0) ? tmrConstants.col1_y : tmrConstants.col2_y;
let x = tmrConstants.gridx + (pos.x * tmrConstants.cellw) - (tmrConstants.cellw / 2);
let y = tmrConstants.gridy + (pos.y * tmrConstants.cellh) - (tmrConstants.cellh / 2) + decallagePairImpair;
return { x: x, y: y, w: tmrConstants.cellw, h: tmrConstants.cellh };
}
}

View File

@ -0,0 +1,40 @@
import { tmrColors, tmrConstants, TMRUtility } from "../tmr-utility.js";
import { Draconique } from "./draconique.js";
export class PontImpraticable extends Draconique {
constructor() {
super();
}
type() { return 'souffle' }
match(item) { return Draconique.isSouffleDragon(item) && item.name.toLowerCase().includes('impraticabilité des ponts'); }
async onActorCreateOwned(actor, item) { await this._creerCaseTmr(actor); }
async onActorDeleteOwned(actor, item) { await this._supprimerCaseTmr(actor); }
code() { return 'pont-impraticable' }
tooltip(linkData) { return `${TMRUtility.getTMR(linkData.data.coord).label} impraticable` }
img() { return 'systems/foundryvtt-reve-de-dragon/icons/svg/wave.svg' }
_createSprite(pixiTMR) {
return pixiTMR.sprite(this.code(),
{
color: tmrColors.casehumide, alpha: 0.5, taille: tmrConstants.twoThird, decallage: tmrConstants.bottom
});
}
async _creerCaseTmr(actor) {
const ponts = TMRUtility.getListTMR('pont');
for (let tmr of ponts) {
await this.createCaseTmr(actor, 'Pont impraticable: ' + tmr.label, tmr);
}
}
async _supprimerCaseTmr(actor) {
const existants = actor.data.items.filter(it => this.isCase(it));
for (let caseTMR of existants) {
await actor.deleteOwnedItem(caseTMR._id);
}
}
}

View File

@ -0,0 +1,42 @@
import { ChatUtility } from "../chat-utility.js";
import { tmrColors, tmrConstants, TMRUtility } from "../tmr-utility.js";
import { Draconique } from "./draconique.js";
export class PresentCites extends Draconique {
constructor() {
super();
}
type() { return 'tete' }
match(item) { return Draconique.isTeteDragon(item) && item.name.toLowerCase() == 'présent des cités'; }
manualMessage() { return false }
async onActorCreateOwned(actor, item) { await this._ajouterPresents(actor); }
code() { return 'present-cites' }
tooltip(linkData) { return `La ${TMRUtility.getTMR(linkData.data.coord).label} a un présent` }
img() { return 'systems/foundryvtt-reve-de-dragon/icons/svg/gift.svg' }
_createSprite(pixiTMR) {
return pixiTMR.sprite(this.code(),
{
color: tmrColors.tetes, alpha: 0.7, taille: tmrConstants.third, decallage:tmrConstants.topRight
});
}
async _ajouterPresents(actor) {
let existants = actor.data.items.filter(it => this.isCase(it)).map(it => it.data.coord);
if (existants.length >0 ) {
ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user),
content: "Vous avez encore des présents dans des cités, vous devrez tirer une autre tête pour remplacer celle ci!"
})
}
else{
let cites = TMRUtility.filterTMR(it => it.type == 'cite');
for (let tmr of cites) {
await this.createCaseTmr(actor, 'Présent: ' + tmr.label, tmr);
}
}
}
}

25
module/tmr/quete-eaux.js Normal file
View File

@ -0,0 +1,25 @@
import { tmrColors, tmrConstants, TMRUtility } from "../tmr-utility.js";
import { Draconique } from "./draconique.js";
export class QueteEaux extends Draconique {
constructor() {
super();
}
type() { return 'tete' }
match(item) { return Draconique.isTeteDragon(item) && item.name.toLowerCase().includes("quête des eaux"); }
manualMessage() { return "Vous devrez re-configurer votre Quête des Eaux une fois un lac ou marais vaincu" }
async onActorCreateOwned(actor, item) { await this._creerCaseTmr(actor); }
code() { return 'maitrisee' }
tooltip(linkData) { return `Quête des eaux, le ${TMRUtility.getTMR(linkData.data.coord).label} est maîtrisé` }
img() { return 'icons/svg/bridge.svg' }
_createSprite(pixiTMR) {
return pixiTMR.sprite(this.code(), { color: tmrColors.tetes, decallage: tmrConstants.topRight });
}
async _creerCaseTmr(actor) {
await this.createCaseTmr(actor, "Quête des eaux à déterminer", {coord:'A0'});
}
}

22
module/tmr/rencontre.js Normal file
View File

@ -0,0 +1,22 @@
import { tmrColors, tmrConstants } from "../tmr-utility.js";
import { Draconique } from "./draconique.js";
export class Rencontre extends Draconique {
constructor() {
super();
}
type() { return '' }
match(item) { return false; }
manualMessage() { return false }
async onActorCreateOwned(actor, item) { }
code() { return 'rencontre' }
tooltip(linkData) { return `${linkData.name} de force ${linkData.force}` }
img() { return 'systems/foundryvtt-reve-de-dragon/icons/heures/hd06.svg' }
_createSprite(pixiTMR) {
return pixiTMR.sprite(this.code(), { color: tmrColors.rencontre, taille: tmrConstants.full, decallage: { x: 2, y: 2 } });
}
}

View File

@ -0,0 +1,28 @@
import { tmrColors, tmrConstants, TMRUtility } from "../tmr-utility.js";
import { Draconique } from "./draconique.js";
export class ReserveExtensible extends Draconique {
constructor() {
super();
}
type() { return 'tete' }
match(item) { return Draconique.isTeteDragon(item) && item.name.toLowerCase().includes("réserve extensible"); }
manualMessage() { return "Vous pouvez re-configurer votre Réserve extensible" }
async onActorCreateOwned(actor, item) { await this._creerCaseTmr(actor); }
code() { return 'reserve_extensible' }
tooltip(linkData) { return `Réserve extensible en ${TMRUtility.getTMR(linkData.data.coord).label} !` }
img() { return 'icons/svg/chest.svg' }
_createSprite(pixiTMR) {
return pixiTMR.sprite(this.code(), { color: tmrColors.tetes, decallage: tmrConstants.left});
}
async _creerCaseTmr(actor) {
const existants = actor.data.items.filter(it => this.isCase(it)).map(it => it.data.coord);
const tmr = TMRUtility.getTMRAleatoire(it => !(it.type == 'fleuve' || existants.includes(it.coord)));
await this.createCaseTmr(actor, "Nouvelle Réserve extensible", tmr);
}
}

View File

@ -0,0 +1,22 @@
import { tmrColors, tmrConstants } from "../tmr-utility.js";
import { Draconique } from "./draconique.js";
export class SortReserve extends Draconique {
constructor() {
super();
}
type() { return '' }
match(item) { return false; }
manualMessage() { return false }
async onActorCreateOwned(actor, item) { }
code() { return 'sort' }
tooltip(linkData) { return `${linkData.name}, r${linkData.data.ptreve_reel}` }
img() { return 'icons/svg/book.svg' }
_createSprite(pixiTMR) {
return pixiTMR.sprite(this.code(), { color: tmrColors.sort, decallage: tmrConstants.right });
}
}

View File

@ -0,0 +1,25 @@
import { tmrColors, tmrConstants, TMRUtility } from "../tmr-utility.js";
import { Draconique } from "./draconique.js";
export class TerreAttache extends Draconique {
constructor() {
super();
}
type() { return 'tete' }
match(item) { return Draconique.isTeteDragon(item) && item.name.toLowerCase().includes("terre d'attache"); }
manualMessage() { return "Vous pouvez re-configurer votre Terre d'Attache" }
async onActorCreateOwned(actor, item) { await this._creerCaseTmr(actor); }
code() { return 'attache' }
tooltip(linkData) { return `Terre d'attache en ${TMRUtility.getTMR(linkData.data.coord).label} !` }
img() { return 'icons/svg/anchor.svg' }
_createSprite(pixiTMR) {
return pixiTMR.sprite(this.code(), { color: tmrColors.tetes, decallage: tmrConstants.topLeft });
}
async _creerCaseTmr(actor) {
await this.createCaseTmr(actor, "Terre d'attache à déterminer", {coord:'A0'});
}
}

30
module/tmr/trou-noir.js Normal file
View File

@ -0,0 +1,30 @@
import { tmrColors, tmrConstants, TMRUtility } from "../tmr-utility.js";
import { Draconique } from "./draconique.js";
export class TrouNoir extends Draconique {
constructor() {
super();
}
type() { return 'souffle' }
match(item) { return Draconique.isSouffleDragon(item) && item.name.toLowerCase().includes('trou noir'); }
manualMessage() { return false }
async onActorCreateOwned(actor, item) { await this._creerCaseTmr(actor); }
code() { return 'trounoir' }
tooltip(linkData) { return `Trou noir en ${TMRUtility.getTMR(linkData.data.coord).label} !` }
img() { return 'icons/svg/explosion.svg' }
_createSprite(pixiTMR) {
return pixiTMR.sprite(this.code(),
{
color: tmrColors.trounoir, alpha: 1, taille: tmrConstants.full, decallage: { x: 2, y: 2 },
});
}
async _creerCaseTmr(actor) {
const existants = actor.data.items.filter(it => this.isCase(it)).map(it => it.data.coord);
const tmr = TMRUtility.getTMRAleatoire(it => !(TMRUtility.isCaseHumide(it) || existants.includes(it.coord)));
await this.createCaseTmr(actor, 'Trou noir: ' + tmr.label, tmr);
}
}