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:
parent
d25e25e6fd
commit
b226e5c09a
118
icons/svg/gift.svg
Normal file
118
icons/svg/gift.svg
Normal file
@ -0,0 +1,118 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="Capa_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
width="512"
|
||||
height="512"
|
||||
viewBox="0 0 512 512"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="gift.svg"
|
||||
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"><metadata
|
||||
id="metadata51"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs49" /><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="3840"
|
||||
inkscape:window-height="2066"
|
||||
id="namedview47"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="64"
|
||||
inkscape:cy="64"
|
||||
inkscape:window-x="-11"
|
||||
inkscape:window-y="-11"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g14"
|
||||
inkscape:document-rotation="0" />
|
||||
<g
|
||||
id="g14"
|
||||
transform="matrix(5.7,0,0,5.7,1.6591,1.6521435)">
|
||||
|
||||
<g
|
||||
id="g861"
|
||||
style="fill:#0044aa"
|
||||
transform="matrix(3.8,0,0,3.8,3.0606105,3.0571933)">
|
||||
<path
|
||||
d="m 15.516,6.331 c 1.966,-0.309 3.471,-1.127 3.471,-3.023 0,-0.685 -0.989,-1.596 -2.675,-1.215 -1.674,0.378 -3.837,2.241 -4.435,3.206 -0.153,-0.367 -0.515,-0.625 -0.938,-0.625 -0.421,0 -0.784,0.257 -0.938,0.623 C 9.403,4.334 7.241,2.469 5.565,2.092 3.88,1.712 2.89,2.622 2.89,3.307 2.89,5.204 4.395,6.021 6.361,6.33 H 0 v 4.167 h 1.323 v 1.778 l 1.778,-1.777 h 0.736 l -2.515,2.516 v 2.302 l 4.817,-4.817 h 0.736 l -5.553,5.554 v 2.303 L 9.178,10.5 H 9.374 V 6.356 C 9.647,6.286 9.86,6.189 9.99,6.062 c 0.15,0.378 0.516,0.648 0.948,0.648 0.432,0 0.8,-0.27 0.946,-0.65 0.13,0.127 0.343,0.225 0.616,0.294 v 4.143 h 0.456 L 3.58,19.874 h 2.302 l 9.375,-9.376 h 0.736 L 6.618,19.874 H 8.92 l 9.375,-9.376 h 0.737 l -9.376,9.376 h 2.303 l 8.592,-8.594 v -0.782 h 1.323 V 6.331 Z M 7.136,6.072 C 5.506,5.889 4.205,5.299 4.205,3.779 c 0,-0.507 0.734,-1.182 1.981,-0.9 1.451,0.327 3.405,2.167 3.405,2.674 0,0.508 -0.978,0.685 -2.455,0.519 z m 5.149,-0.518 c 0,-0.507 1.954,-2.346 3.405,-2.674 1.247,-0.282 1.98,0.393 1.98,0.9 0,1.52 -1.299,2.109 -2.931,2.293 -1.476,0.165 -2.454,-0.012 -2.454,-0.519 z"
|
||||
id="path851"
|
||||
style="fill:#0044aa" />
|
||||
<polygon
|
||||
points="2.843,19.874 12.218,10.498 12.5,10.498 10.938,8.415 9.375,10.498 9.916,10.498 1.323,19.091 1.323,19.874 "
|
||||
id="polygon853"
|
||||
style="fill:#0044aa" />
|
||||
<polygon
|
||||
points="20.553,19.874 20.553,18.095 18.774,19.874 "
|
||||
id="polygon855"
|
||||
style="fill:#0044aa" />
|
||||
<polygon
|
||||
points="18.038,19.874 20.553,17.359 20.553,15.056 15.734,19.874 "
|
||||
id="polygon857"
|
||||
style="fill:#0044aa" />
|
||||
<polygon
|
||||
points="14.998,19.874 20.553,14.32 20.553,12.017 12.695,19.874 "
|
||||
id="polygon859"
|
||||
style="fill:#0044aa" />
|
||||
</g></g>
|
||||
<g
|
||||
id="g16">
|
||||
</g>
|
||||
<g
|
||||
id="g18">
|
||||
</g>
|
||||
<g
|
||||
id="g20">
|
||||
</g>
|
||||
<g
|
||||
id="g22">
|
||||
</g>
|
||||
<g
|
||||
id="g24">
|
||||
</g>
|
||||
<g
|
||||
id="g26">
|
||||
</g>
|
||||
<g
|
||||
id="g28">
|
||||
</g>
|
||||
<g
|
||||
id="g30">
|
||||
</g>
|
||||
<g
|
||||
id="g32">
|
||||
</g>
|
||||
<g
|
||||
id="g34">
|
||||
</g>
|
||||
<g
|
||||
id="g36">
|
||||
</g>
|
||||
<g
|
||||
id="g38">
|
||||
</g>
|
||||
<g
|
||||
id="g40">
|
||||
</g>
|
||||
<g
|
||||
id="g42">
|
||||
</g>
|
||||
<g
|
||||
id="g44">
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.5 KiB |
94
icons/svg/wave.svg
Normal file
94
icons/svg/wave.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 23 KiB |
@ -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";
|
||||
|
@ -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";
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
436
module/actor.js
436
module/actor.js
@ -1,5 +1,5 @@
|
||||
import { RdDUtility } from "./rdd-utility.js";
|
||||
import { TMRType, TMRUtility } from "./tmr-utility.js";
|
||||
import { 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,9 +21,10 @@ 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";
|
||||
import { TMRRencontres } from "./tmr-rencontres.js";
|
||||
import { Poetique } from "./poetique.js";
|
||||
import { EffetsDraconiques } from "./tmr/effets-draconiques.js";
|
||||
import { Draconique } from "./tmr/draconique.js";
|
||||
|
||||
|
||||
/* -------------------------------------------- */
|
||||
@ -36,6 +37,8 @@ export class RdDActor extends Actor {
|
||||
static init() {
|
||||
Hooks.on("deleteActiveEffect", (actor, effect, options) => actor.onDeleteActiveEffect(effect, options));
|
||||
Hooks.on("createActiveEffect", (actor, effect, options) => actor.onCreateActiveEffect(effect, options));
|
||||
Hooks.on("createOwnedItem", (actor, item, options, id) => actor.onCreateOwnedItem(item, options, id));
|
||||
Hooks.on("deleteOwnedItem", (actor, item, options, id) => actor.onDeleteOwnedItem(item, options, id));
|
||||
Hooks.on("updateActor", (actor, update, options, actorId) => actor.onUpdateActor(update, options, actorId));
|
||||
}
|
||||
|
||||
@ -74,7 +77,7 @@ export class RdDActor extends Actor {
|
||||
} else if (data.type == "entite") {
|
||||
compendiumName = "foundryvtt-reve-de-dragon.competences-entites";
|
||||
}
|
||||
if ( compendiumName ) {
|
||||
if (compendiumName) {
|
||||
data.items = await RdDUtility.loadCompendium(compendiumName);
|
||||
}
|
||||
// Ajout monnaie
|
||||
@ -116,7 +119,7 @@ export class RdDActor extends Actor {
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
_prepareVehiculeData( actorData ) {
|
||||
_prepareVehiculeData(actorData) {
|
||||
this.computeEncombrementTotalEtMalusArmure();
|
||||
}
|
||||
|
||||
@ -217,7 +220,7 @@ export class RdDActor extends Actor {
|
||||
}
|
||||
/* -------------------------------------------- */
|
||||
getAgilite() {
|
||||
switch(this.data.type) {
|
||||
switch (this.data.type) {
|
||||
case 'personnage': return Misc.toInt(this.data.data.carac.agilite?.value);
|
||||
case 'creature': return Misc.toInt(this.data.data.carac.force?.value);
|
||||
case 'entite': return Misc.toInt(this.data.data.carac.reve?.value);
|
||||
@ -297,7 +300,6 @@ export class RdDActor extends Actor {
|
||||
}
|
||||
return duplicate(list[0]);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async deleteSortReserve(sortReserve) {
|
||||
let reserve = duplicate(this.data.data.reve.reserve);
|
||||
@ -318,11 +320,11 @@ export class RdDActor extends Actor {
|
||||
getSurprise(isCombat = undefined) {
|
||||
let niveauSurprise = Array.from(this.effects?.values() ?? [])
|
||||
.map(effect => StatusEffects.valeurSurprise(effect.data, isCombat))
|
||||
.reduce((a,b)=> a+b, 0);
|
||||
if (niveauSurprise>1) {
|
||||
.reduce((a, b) => a + b, 0);
|
||||
if (niveauSurprise > 1) {
|
||||
return 'totale';
|
||||
}
|
||||
if (niveauSurprise==1 || this.getSonne()) {
|
||||
if (niveauSurprise == 1 || this.getSonne()) {
|
||||
return 'demi';
|
||||
}
|
||||
return '';
|
||||
@ -472,22 +474,19 @@ export class RdDActor extends Actor {
|
||||
async dormir(heures = 1) {
|
||||
let message = {
|
||||
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
|
||||
content: this.name +": Vous dormez " + heures + (heures > 1 ? " heures. " : "heure. ")
|
||||
content: this.name + ": Vous dormez " + heures + (heures > 1 ? " heures. " : "heure. ")
|
||||
};
|
||||
await this.recupereEndurance(message);
|
||||
for (let i = 0; i < heures; i++) {
|
||||
await this._recupererEthylisme(message);
|
||||
await this.recupererFatigue(message);
|
||||
await this.recuperationReve(message);
|
||||
if (this.isDonDoubleReve()) {
|
||||
if (EffetsDraconiques.isDonDoubleReve(this)) {
|
||||
await this.recuperationReve(message);
|
||||
}
|
||||
}
|
||||
ChatMessage.create(message);
|
||||
}
|
||||
isDonDoubleReve() {
|
||||
return this.data.items.find(item => item.type == 'tete' && item.name == 'Don de double-rêve');
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async _recupererEthylisme(message) {
|
||||
@ -588,7 +587,7 @@ export class RdDActor extends Actor {
|
||||
canClose: false,
|
||||
rencontre: duplicate(TMRRencontres.getRencontre('rdd')),
|
||||
tmr: true,
|
||||
use: {libre: false, conditions: false},
|
||||
use: { libre: false, conditions: false },
|
||||
forceCarac: { 'reve-actuel': { label: "Rêve Actuel", value: this.getReveActuel() } }
|
||||
}
|
||||
rollData.rencontre.force = force;
|
||||
@ -663,16 +662,16 @@ export class RdDActor extends Actor {
|
||||
/* -------------------------------------------- */
|
||||
async sortMisEnReserve(rollData, sort) {
|
||||
let reserve = duplicate(this.data.data.reve.reserve);
|
||||
reserve.list.push({ coord: rollData.coord, sort: sort, draconic: duplicate(rollData.competence) });
|
||||
reserve.list.push({ coord: rollData.tmr.coord, sort: sort, draconic: duplicate(rollData.competence) });
|
||||
await this.update({ "data.reve.reserve": reserve });
|
||||
this.currentTMR.updateSortReserve();
|
||||
this.currentTMR.updateTokens();
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async updateCarac(caracName, caracValue) {
|
||||
let caracpath = "data.carac." + caracName + ".value"
|
||||
if (caracName == "force") {
|
||||
if ( Number(caracValue) > this.getTaille() + 4) {
|
||||
if (Number(caracValue) > this.getTaille() + 4) {
|
||||
ui.notifications.warn("Votre FORCE doit être au maximum de TAILLE+4");
|
||||
return;
|
||||
}
|
||||
@ -750,7 +749,7 @@ export class RdDActor extends Actor {
|
||||
} else {
|
||||
console.log("Competence not found", compName);
|
||||
}
|
||||
RdDUtility.checkThanatosXP( compName );
|
||||
RdDUtility.checkThanatosXP(compName);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
@ -810,14 +809,14 @@ export class RdDActor extends Actor {
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
getRecursiveEnc( objet ) {
|
||||
getRecursiveEnc(objet) {
|
||||
let sumEnc = 0;
|
||||
if ( objet.type == 'conteneur') {
|
||||
if (objet.type == 'conteneur') {
|
||||
for (let id of objet.data.data.contenu) {
|
||||
let subobjet = this.items.find(objet => (id == objet._id));
|
||||
if ( subobjet ) {
|
||||
if ( subobjet && subobjet.type == 'conteneur') {
|
||||
sumEnc += this.getRecursiveEnc( subobjet );
|
||||
if (subobjet) {
|
||||
if (subobjet && subobjet.type == 'conteneur') {
|
||||
sumEnc += this.getRecursiveEnc(subobjet);
|
||||
} else {
|
||||
sumEnc += Number(subobjet.data.data.encombrement) * Number(subobjet.data.data.quantite);
|
||||
}
|
||||
@ -840,11 +839,11 @@ export class RdDActor extends Actor {
|
||||
//console.log("Conteneur trouvé : ", conteneur);
|
||||
if (conteneur && conteneur.type == "conteneur") {
|
||||
// Calculer le total actuel des contenus
|
||||
let encContenu = this.getRecursiveEnc( conteneur ) - Number(conteneur.data.data.encombrement);
|
||||
let encContenu = this.getRecursiveEnc(conteneur) - Number(conteneur.data.data.encombrement);
|
||||
let nouvelObjet = this.items.find(objet => (itemId == objet._id)); // On chope l'objet
|
||||
let newEnc = (nouvelObjet) ? this.getRecursiveEnc( nouvelObjet ) : 0; // Calculer le total actuel du nouvel objet
|
||||
let newEnc = (nouvelObjet) ? this.getRecursiveEnc(nouvelObjet) : 0; // Calculer le total actuel du nouvel objet
|
||||
//console.log( currentEnc, newEnc, conteneur.data.data.capacite, conteneur.name);
|
||||
if (nouvelObjet && ( (encContenu + newEnc) > Number(conteneur.data.data.capacite)) ) {
|
||||
if (nouvelObjet && ((encContenu + newEnc) > Number(conteneur.data.data.capacite))) {
|
||||
ui.notifications.warn("Capacité d'encombrement insuffisante dans le conteneur !");
|
||||
return false;
|
||||
}
|
||||
@ -861,8 +860,8 @@ export class RdDActor extends Actor {
|
||||
if (subObj && subObj.type == 'conteneur') {
|
||||
this.buildSubConteneurObjetList(subId, deleteList);
|
||||
}
|
||||
if ( subObj) // Robust...
|
||||
deleteList.push( {id: subId, conteneurId: conteneurId } );
|
||||
if (subObj) // Robust...
|
||||
deleteList.push({ id: subId, conteneurId: conteneurId });
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -870,7 +869,7 @@ export class RdDActor extends Actor {
|
||||
/* -------------------------------------------- */
|
||||
async deleteAllConteneur(itemId) {
|
||||
let list = [];
|
||||
list.push( {id: itemId, conteneurId: undefined }); // Init list
|
||||
list.push({ id: itemId, conteneurId: undefined }); // Init list
|
||||
this.buildSubConteneurObjetList(itemId, list);
|
||||
//console.log("List to delete", list);
|
||||
for (let item of list) {
|
||||
@ -927,40 +926,40 @@ export class RdDActor extends Actor {
|
||||
/* -------------------------------------------- */
|
||||
async moveItemsBetweenActors(itemId, sourceActorId) {
|
||||
let itemsList = []
|
||||
let sourceActor = game.actors.get( sourceActorId );
|
||||
itemsList.push( {id: itemId, conteneurId: undefined }); // Init list
|
||||
sourceActor.buildSubConteneurObjetList( itemId, itemsList ); // Get itemId list
|
||||
let sourceActor = game.actors.get(sourceActorId);
|
||||
itemsList.push({ id: itemId, conteneurId: undefined }); // Init list
|
||||
sourceActor.buildSubConteneurObjetList(itemId, itemsList); // Get itemId list
|
||||
|
||||
let itemMap = {};
|
||||
for (let item of itemsList) {
|
||||
let srcItem = sourceActor.data.items.find( subItem => subItem._id == item.id );
|
||||
let newItem = await this.createOwnedItem( duplicate(srcItem) );
|
||||
let srcItem = sourceActor.data.items.find(subItem => subItem._id == item.id);
|
||||
let newItem = await this.createOwnedItem(duplicate(srcItem));
|
||||
console.log('New object', newItem, srcItem);
|
||||
itemMap[srcItem._id] = newItem._id; // Pour garder le lien ancien / nouveau
|
||||
}
|
||||
for (let item of itemsList) { // Second boucle pour traiter la remise en conteneurs
|
||||
// gestion conteneur/contenu
|
||||
if ( item.conteneurId) { // l'Objet était dans un conteneur
|
||||
if (item.conteneurId) { // l'Objet était dans un conteneur
|
||||
let newConteneurId = itemMap[item.conteneurId]; // Get conteneur
|
||||
let newConteneur = this.data.items.find( subItem => subItem._id == newConteneurId );
|
||||
let newConteneur = this.data.items.find(subItem => subItem._id == newConteneurId);
|
||||
|
||||
let newItemId = itemMap[item.id]; // Get newItem
|
||||
|
||||
console.log('New conteneur filling!', newConteneur, newItemId, item );
|
||||
console.log('New conteneur filling!', newConteneur, newItemId, item);
|
||||
let contenu = duplicate(newConteneur.data.contenu);
|
||||
contenu.push( newItemId );
|
||||
await this.updateOwnedItem( {_id: newConteneurId, 'data.contenu': contenu });
|
||||
contenu.push(newItemId);
|
||||
await this.updateOwnedItem({ _id: newConteneurId, 'data.contenu': contenu });
|
||||
}
|
||||
}
|
||||
for( let item of itemsList) {
|
||||
await sourceActor.deleteOwnedItem( item.id );
|
||||
for (let item of itemsList) {
|
||||
await sourceActor.deleteOwnedItem(item.id);
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
detectSurEncombrement() {
|
||||
let maxEnc = 0;
|
||||
if ( this.data.type == 'vehicule')
|
||||
if (this.data.type == 'vehicule')
|
||||
maxEnc = this.data.data.capacite_encombrement;
|
||||
else
|
||||
maxEnc = this.data.data.attributs.encombrement.value;
|
||||
@ -1096,6 +1095,7 @@ export class RdDActor extends Actor {
|
||||
content: this.name + " subit un Souffle de Dragon : " + souffle.name
|
||||
});
|
||||
}
|
||||
// TODO: fermeture cité
|
||||
return souffle;
|
||||
}
|
||||
|
||||
@ -1123,41 +1123,41 @@ export class RdDActor extends Actor {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
displayTMRQueueSouffleInformation() {
|
||||
let messages = [];
|
||||
for (let item of this.data.items) {
|
||||
let content
|
||||
if (item.type == 'queue') {
|
||||
if (item.name.toLowerCase() == 'conquête') {
|
||||
content = "RAPPEL ! Vous souffrez d'une <strong>Conquête</strong> : " + item.data.description;
|
||||
if (EffetsDraconiques.isConquete(item)) {
|
||||
messages.push("Vous souffrez d'une <strong>Conquête</strong> : " + item.data.description);
|
||||
}
|
||||
else if (item.name.toLowerCase() == 'pélerinage') {
|
||||
content = "RAPPEL ! Vous souffrez d'un <strong>Pélerinage</strong> : " + item.data.description;
|
||||
if (EffetsDraconiques.isPelerinage(item)) {
|
||||
messages.push("Vous souffrez d'un <strong>Pélerinage</strong> : " + item.data.description);
|
||||
}
|
||||
else if (item.name.toLowerCase() == 'urgence draconique') {
|
||||
content = "RAPPEL ! Vous souffrez d'une <strong>Urgence Draconique</strong> : " + item.data.description;
|
||||
if (EffetsDraconiques.isUrgenceDraconique(item)) {
|
||||
messages.push("Vous souffrez d'une <strong>Urgence Draconique</strong> : " + item.data.description);
|
||||
}
|
||||
} else if (item.type == 'souffle') {
|
||||
if (item.name.toLowerCase() == 'périple') {
|
||||
content = "RAPPEL ! Vous souffrez du Souffle <strong>Périple</strong>. Vous devez gérer manuellement le détail du Périple.<br>" + item.data.description;
|
||||
} else if (item.name.toLowerCase() == 'fermeture des cités') {
|
||||
content = "RAPPEL ! Vous souffrez du Souffle <strong>Fermeture des Cités</strong>. Vous devez gérer manuellement le détail des Citées ré-ouvertes.<br>" + item.data.description;
|
||||
} else if (item.name.toLowerCase() == 'désorientation') {
|
||||
content = "RAPPEL ! Vous souffrez du Souffle <strong>Désorientation</strong>. Vous devez gérer avec votre MJ les effets de ce souffle.<br>" + item.data.description;
|
||||
} else if (item.name.toLowerCase() == 'double résistance du fleuve') {
|
||||
content = "RAPPEL ! Vous souffrez du Souffle <strong>Double Résistance du Fleuve</strong>. Vous devez gérer avec votre MJ les effets de ce souffle.<br>" + item.data.description;
|
||||
if (EffetsDraconiques.isPeriple(item)) {
|
||||
messages.push("Vous souffrez du Souffle <strong>Périple</strong>. Vous devez gérer manuellement le détail du Périple.<br>" + item.data.description);
|
||||
}
|
||||
if (EffetsDraconiques.isDesorientation(item)) {
|
||||
messages.push("Vous souffrez du Souffle <strong>Désorientation</strong>. Vous devez gérer avec votre MJ les effets de ce souffle.<br>" + item.data.description);
|
||||
}
|
||||
}
|
||||
if (content) {
|
||||
|
||||
if (messages.length > 0) {
|
||||
ChatMessage.create({
|
||||
whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name),
|
||||
content: content
|
||||
content: "RAPPEL !<br>" + messages.join('<hr>')
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
getTMRRencontres() {
|
||||
return this.data.data.reve.rencontre;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async deleteTMRRencontreAtPosition() {
|
||||
let rencontres = duplicate(this.data.data.reve.rencontre);
|
||||
let rencontres = duplicate(this.getTMRRencontres());
|
||||
let len = rencontres.list.length;
|
||||
let i = 0;
|
||||
//console.log("List", rencontres, len);
|
||||
@ -1175,7 +1175,7 @@ export class RdDActor extends Actor {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async addTMRRencontre(currentRencontre) {
|
||||
let rencontres = duplicate(this.data.data.reve.rencontre);
|
||||
let rencontres = duplicate(this.getTMRRencontres());
|
||||
let len = rencontres.list.length;
|
||||
let i = 0;
|
||||
let already = false;
|
||||
@ -1184,7 +1184,7 @@ export class RdDActor extends Actor {
|
||||
already = true;
|
||||
}
|
||||
if (!already) {
|
||||
rencontres.list.push({ coord: this.data.data.reve.tmrpos.coord, rencontre: currentRencontre });
|
||||
rencontres.list.push(currentRencontre);
|
||||
await this.update({ "data.reve.rencontre": rencontres });
|
||||
}
|
||||
}
|
||||
@ -1301,7 +1301,7 @@ export class RdDActor extends Actor {
|
||||
await RdDDice.show(myRoll);
|
||||
|
||||
let msgText = "Jet d'Endurance : " + myRoll.total + " / " + this.data.data.sante.endurance.value + "<br>";
|
||||
if (myRoll.total == 1 || (myRoll.total != 20 && myRoll.total <= this.data.data.sante.endurance.value) ) {
|
||||
if (myRoll.total == 1 || (myRoll.total != 20 && myRoll.total <= this.data.data.sante.endurance.value)) {
|
||||
msgText += `${this.name} a réussi son Jet d'Endurance !`;
|
||||
if (myRoll.total == 1) {
|
||||
msgText += `et gagne 1 Point d'Experience en Constitution`;
|
||||
@ -1352,20 +1352,20 @@ export class RdDActor extends Actor {
|
||||
const sante = duplicate(this.data.data.sante);
|
||||
let compteur = sante[name];
|
||||
if (!compteur) {
|
||||
return ;
|
||||
return;
|
||||
}
|
||||
let result = {
|
||||
sonne: false,
|
||||
};
|
||||
|
||||
let minValue = name == "vie" ? -this.getSConst()-1 : 0;
|
||||
let minValue = name == "vie" ? -this.getSConst() - 1 : 0;
|
||||
|
||||
result.newValue = Math.max(minValue, Math.min(compteur.value + inc, compteur.max));
|
||||
//console.log("New value ", inc, minValue, result.newValue);
|
||||
let fatigue = 0;
|
||||
if (name == "endurance" && !this.isEntiteCauchemar()) {
|
||||
if (result.newValue == 0 && inc < 0 && !isCritique) { // perte endurance et endurance devient 0 (sauf critique) -> -1 vie
|
||||
sante.vie.value --;
|
||||
sante.vie.value--;
|
||||
}
|
||||
result.newValue = Math.max(0, result.newValue);
|
||||
if (inc > 0) { // le max d'endurance s'applique seulement à la récupération
|
||||
@ -1399,7 +1399,7 @@ export class RdDActor extends Actor {
|
||||
}
|
||||
|
||||
isDead() {
|
||||
return !this.isEntiteCauchemar() && this.data.data.sante.vie.value<-this.getSConst()
|
||||
return !this.isEntiteCauchemar() && this.data.data.sante.vie.value < -this.getSConst()
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
@ -1658,7 +1658,7 @@ export class RdDActor extends Actor {
|
||||
async checkCaracXP(caracName) {
|
||||
let carac = this.data.data.carac[caracName];
|
||||
if (carac && carac.xp > 0) {
|
||||
let xpNeeded = RdDUtility.getCaracNextXp(carac.value+1);
|
||||
let xpNeeded = RdDUtility.getCaracNextXp(carac.value + 1);
|
||||
if (carac.xp >= xpNeeded) {
|
||||
carac = duplicate(carac);
|
||||
carac.value = Number(carac.value) + 1;
|
||||
@ -1679,12 +1679,12 @@ export class RdDActor extends Actor {
|
||||
/* -------------------------------------------- */
|
||||
async checkCompetenceXP(compName, newXP = undefined) {
|
||||
let competence = RdDItemCompetence.findCompetence(this.data.items, compName);
|
||||
if ( competence && newXP && newXP == competence.data.xp ) { // Si édition, mais sans changement XP
|
||||
if (competence && newXP && newXP == competence.data.xp) { // Si édition, mais sans changement XP
|
||||
return;
|
||||
}
|
||||
newXP = (newXP) ? newXP : competence.data.xp;
|
||||
if (competence && newXP > 0) {
|
||||
let xpNeeded = RdDItemCompetence.getCompetenceNextXp(competence.data.niveau+1);
|
||||
let xpNeeded = RdDItemCompetence.getCompetenceNextXp(competence.data.niveau + 1);
|
||||
if (newXP >= xpNeeded) {
|
||||
let newCompetence = duplicate(competence);
|
||||
newCompetence.data.niveau += 1;
|
||||
@ -1712,7 +1712,7 @@ export class RdDActor extends Actor {
|
||||
if (display && xpResult.result) {
|
||||
let xpmsg = "<br>Points d'expérience gagnés ! Carac: " + xpResult.xpCarac + ", Comp: " + xpResult.xpCompetence;
|
||||
let message = {
|
||||
whisher: ChatMessage.getWhisperRecipients( ["GM", this.name ] ),
|
||||
whisher: ChatMessage.getWhisperRecipients(["GM", this.name]),
|
||||
content: "<strong>" + rollData.selectedCarac.label + "</strong>" + xpmsg,
|
||||
}
|
||||
ChatMessage.create(message);
|
||||
@ -1727,18 +1727,18 @@ export class RdDActor extends Actor {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
filterSortList(sortList, coord) {
|
||||
let tmr = TMRUtility.getTMR( coord);
|
||||
let tmr = TMRUtility.getTMR(coord);
|
||||
let letfilteredList = []
|
||||
for ( let sort of sortList) {
|
||||
for (let sort of sortList) {
|
||||
//console.log(sort.name, sort.data.caseTMR.toLowerCase(), sort.data.caseTMRspeciale.toLowerCase(), coord.toLowerCase() );
|
||||
if (sort.data.caseTMR.toLowerCase().includes('variable') ) {
|
||||
letfilteredList.push( sort);
|
||||
} else if (sort.data.caseTMRspeciale.toLowerCase().includes('variable') ) {
|
||||
letfilteredList.push( sort);
|
||||
} else if (sort.data.caseTMR.toLowerCase() == tmr.type ) {
|
||||
letfilteredList.push( sort);
|
||||
} else if ( sort.data.caseTMR.toLowerCase().includes('special') && sort.data.caseTMRspeciale.toLowerCase().includes( coord.toLowerCase() ) ) {
|
||||
letfilteredList.push( sort);
|
||||
if (sort.data.caseTMR.toLowerCase().includes('variable')) {
|
||||
letfilteredList.push(sort);
|
||||
} else if (sort.data.caseTMRspeciale.toLowerCase().includes('variable')) {
|
||||
letfilteredList.push(sort);
|
||||
} else if (sort.data.caseTMR.toLowerCase() == tmr.type) {
|
||||
letfilteredList.push(sort);
|
||||
} else if (sort.data.caseTMR.toLowerCase().includes('special') && sort.data.caseTMRspeciale.toLowerCase().includes(coord.toLowerCase())) {
|
||||
letfilteredList.push(sort);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1746,22 +1746,24 @@ export class RdDActor extends Actor {
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
filterDraconicList(sortList ) {
|
||||
filterDraconicList(sortList) {
|
||||
let draconicDone = {};
|
||||
let newDraconicList = [];
|
||||
let draconicList = this.getDraconicList();
|
||||
let bestDraconic = this.getBestDraconic();
|
||||
for ( let sort of sortList) {
|
||||
for (let sort of sortList) {
|
||||
let voie = sort.data.draconic.toLowerCase();
|
||||
let competenceVoie = draconicList.find(item => item.data.categorie == 'draconic' && item.name.toLowerCase().includes( voie ) );
|
||||
if ( sort.name.toLowerCase().includes('aura') ) {
|
||||
competenceVoie = bestDraconic;
|
||||
let draconic = draconicList.find(item => item.data.categorie == 'draconic' && item.name.toLowerCase().includes(voie));
|
||||
if (sort.name.toLowerCase().includes('aura')) {
|
||||
draconic = bestDraconic;
|
||||
}
|
||||
if (!draconicDone[competenceVoie.name]) {
|
||||
newDraconicList.push( competenceVoie );
|
||||
draconicDone[competenceVoie.name] = newDraconicList.length-1; // Patch local pour relier facilement voie/compétence
|
||||
draconic = duplicate(draconic);
|
||||
if (draconicDone[draconic.name] == undefined) {
|
||||
draconic.data.defaut_carac = 'reve';
|
||||
newDraconicList.push(draconic);
|
||||
draconicDone[draconic.name] = newDraconicList.length - 1; // Patch local pour relier facilement voie/compétence
|
||||
}
|
||||
sort.data.listIndex = draconicDone[competenceVoie.name] || 0;
|
||||
sort.data.listIndex = draconicDone[draconic.name] || 0;
|
||||
}
|
||||
return newDraconicList;
|
||||
}
|
||||
@ -1782,19 +1784,20 @@ export class RdDActor extends Actor {
|
||||
|
||||
let draconicList = this.filterDraconicList(sortList);
|
||||
let rollData = {
|
||||
selectedCarac: this.data.data.carac.reve,
|
||||
forceCarac: { 'reve': duplicate(this.data.data.carac.reve) },
|
||||
draconicList: draconicList,
|
||||
sortList: sortList,
|
||||
competence: this.getBestDraconic(),
|
||||
competence: draconicList[0],
|
||||
selectedSort: sortList[0],
|
||||
tmr: TMRUtility.getTMR(coord),
|
||||
diffLibre: sortList[0].data.difficulte, // Per default at startup
|
||||
coutreve: Array(20).fill().map((item, index) => 1 + index)
|
||||
}
|
||||
|
||||
if ( this.currentTMR ) this.currentTMR.minimize(); // Hide
|
||||
if (this.currentTMR) this.currentTMR.minimize(); // Hide
|
||||
const dialog = await RdDRoll.create(this, rollData,
|
||||
{ html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-sort.html',
|
||||
{
|
||||
html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-sort.html',
|
||||
close: html => { this.currentTMR.maximize() } // Re-display TMR
|
||||
},
|
||||
{
|
||||
@ -1820,7 +1823,7 @@ export class RdDActor extends Actor {
|
||||
/* -------------------------------------------- */
|
||||
isRencontreSpeciale() { // Gestion queue/souffle 'Mauvaise Rencontre en Perpective'
|
||||
let addMsg = "";
|
||||
let rencSpecial = this.data.items.find(item => RdDItem.isHRMauvaiseRencontreEnPerspective(item));
|
||||
let rencSpecial = this.data.items.find(item => EffetsDraconiques.isMauvaiseRencontre(item));
|
||||
if (rencSpecial) {
|
||||
rencSpecial = duplicate(rencSpecial); // To keep it
|
||||
if (rencSpecial.type != 'souffle') {
|
||||
@ -1839,84 +1842,28 @@ export class RdDActor extends Actor {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
getTMRFatigue() { // Pour l'instant uniquement Inertie Draconique
|
||||
let countInertieDraconique = this.data.items.filter(item => RdDItem.isHRInertieDraconique(item)).length;
|
||||
if (countInertieDraconique>0) {
|
||||
let countInertieDraconique = EffetsDraconiques.countInertieDraconique(this);
|
||||
if (countInertieDraconique > 0) {
|
||||
ChatMessage.create({
|
||||
content: `Vous êtes sous le coup d'une Inertie Draconique : vous perdez ${countInertieDraconique+1} cases de Fatigue par déplacement au lieu d'une.`,
|
||||
content: `Vous êtes sous le coup d'une Inertie Draconique : vous perdez ${countInertieDraconique + 1} cases de Fatigue par déplacement au lieu d'une.`,
|
||||
whisper: ChatMessage.getWhisperRecipients(game.user.name)
|
||||
});
|
||||
}
|
||||
return countInertieDraconique + 1;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
isConnaissanceFleuve() {
|
||||
return this.data.items.find(item => item.type == 'tete' && item.name.toLowerCase().includes('connaissance du fleuve'));
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
isReserveEnSecurite() {
|
||||
let reserveSecurite = this.data.items.find(item => item.type == 'tete' && item.name.toLowerCase().includes(' en sécurité'));
|
||||
return reserveSecurite;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
isDoubleResistanceFleuve() {
|
||||
let resistFleuve = this.data.items.find(item => RdDItem.isHRDoubleResistanceFleuve(item));
|
||||
if (resistFleuve) {
|
||||
ChatMessage.create({
|
||||
content: "Vous êtes sous le coup d'une Double Résistance du Fleuve : vous devez maîtriser 2 fois chaque case humide, un second jet est donc effectué.",
|
||||
whisper: ChatMessage.getWhisperRecipients(game.user.name)
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async checkSoufflePeage(cellDescr) {
|
||||
let peage = this.data.items.find(item => RdDItem.isHRPeage(item));
|
||||
let peage = this.data.items.find(item => EffetsDraconiques.isPeage(item));
|
||||
if (peage && (cellDescr.type == 'pont' || cellDescr.type == 'cite')) {
|
||||
await this.reveActuelIncDec(-1);
|
||||
ChatMessage.create({
|
||||
content: "Vous êtes sous le coup d'un Péage : l'entrée sur cette case vous coûte 1 Point de Rêve (déduit automatiquement).",
|
||||
content: "Vous êtes sous le coup d'un Péage : l'entrée sur cette case vous a coûté 1 Point de Rêve (déduit automatiquement).",
|
||||
whisper: ChatMessage.getWhisperRecipients(game.user.name)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
checkTeteDeplacementAccelere() {
|
||||
let deplAccelere = this.data.items.find(item => item.type == 'tete' && item.name.toLowerCase().includes(' déplacement accéléré'));
|
||||
if (deplAccelere) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
isCaseHumideAdditionelle(tmr) {
|
||||
if (tmr.type == 'pont' && this.data.items.find(it => RdDItem.isHRPontImpraticable(it))) {
|
||||
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;
|
||||
}
|
||||
// Débordement ?
|
||||
let isTmrInondee = this.data.items.filter(it => RdDItem.isCaseTMR(it))
|
||||
.filter(it => it.data.coord == tmr.coord)
|
||||
.find(it => RdDItem.isHRCaseInnondee(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;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async _rollUnSortResult(rollData, isSortReserve = false) {
|
||||
let rolled = rollData.rolled;
|
||||
@ -1945,7 +1892,7 @@ export class RdDActor extends Actor {
|
||||
}
|
||||
if (myReve.value > rollData.depenseReve) {
|
||||
// Incrémenter/gére le bonus de case
|
||||
RdDItemSort.incrementBonusCase(this, selectedSort, rollData.coord);
|
||||
RdDItemSort.incrementBonusCase(this, selectedSort, rollData.tmr.coord);
|
||||
|
||||
if (rollData.isSortReserve) {
|
||||
await this.sortMisEnReserve(rollData, selectedSort);
|
||||
@ -2064,7 +2011,7 @@ export class RdDActor extends Actor {
|
||||
competence: competence,
|
||||
tache: tache,
|
||||
diffConditions: tache.data.difficulte,
|
||||
use: { libre: false, conditions: false},
|
||||
use: { libre: false, conditions: false },
|
||||
carac: {}
|
||||
};
|
||||
rollData.carac[tache.data.carac] = duplicate(this.data.data.carac[tache.data.carac]); // Single carac
|
||||
@ -2100,19 +2047,19 @@ export class RdDActor extends Actor {
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async _rollArt(artData, selectedCarac, oeuvre, callBackResult = r =>this._resultArt(r)) {
|
||||
async _rollArt(artData, selected, oeuvre, callBackResult = r => this._resultArt(r)) {
|
||||
mergeObject(artData, {
|
||||
oeuvre: oeuvre,
|
||||
art: oeuvre.type,
|
||||
competence: duplicate(this.getCompetence(oeuvre.data.competence ?? artData.art)),
|
||||
diffLibre: - (oeuvre.data.niveau ??0),
|
||||
diffLibre: - (oeuvre.data.niveau ?? 0),
|
||||
diffConditions: 0,
|
||||
use: { libre: false, conditions: true },
|
||||
selectedCarac: duplicate(this.data.data.carac[selectedCarac]),
|
||||
selectedCarac: duplicate(this.data.data.carac[selected]),
|
||||
forceCarac: {}
|
||||
});
|
||||
artData.competence.data.defaut_carac = selectedCarac;
|
||||
artData.forceCarac[selectedCarac] = duplicate(this.data.data.carac[selectedCarac]);
|
||||
artData.competence.data.defaut_carac = selected;
|
||||
artData.forceCarac[selected] = duplicate(this.data.data.carac[selected]);
|
||||
|
||||
console.log("rollArt !!!", artData);
|
||||
|
||||
@ -2169,14 +2116,14 @@ export class RdDActor extends Actor {
|
||||
async rollRecetteCuisine(id) {
|
||||
const artData = { art: 'cuisine', verbe: 'Cuisiner' };
|
||||
const oeuvre = duplicate(this.getRecetteCuisine(id));
|
||||
await this._rollArt(artData, 'odoratgout', oeuvre, r => this._resultRecetteCuisine(r) );
|
||||
await this._rollArt(artData, 'odoratgout', oeuvre, r => this._resultRecetteCuisine(r));
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async _resultRecetteCuisine(artData) {
|
||||
const baseQualite = (artData.rolled.isSuccess ? artData.oeuvre.data.niveau : artData.competence.data.niveau);
|
||||
artData.qualiteFinale = Math.min(baseQualite, artData.oeuvre.data.niveau) + artData.rolled.ptQualite;
|
||||
artData.exotismeFinal = Math.min(Math.min(artData.qualiteFinale, -Math.abs(artData.oeuvre.data.exotisme??0)), 0);
|
||||
artData.exotismeFinal = Math.min(Math.min(artData.qualiteFinale, -Math.abs(artData.oeuvre.data.exotisme ?? 0)), 0);
|
||||
console.log("OEUVRE", artData.art, artData)
|
||||
RdDResolutionTable.displayRollData(artData, this.name, `chat-resultat-${artData.art}.html`);
|
||||
}
|
||||
@ -2286,7 +2233,7 @@ export class RdDActor extends Actor {
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async chanceActuelleIncDec(value, limit=true) {
|
||||
async chanceActuelleIncDec(value, limit = true) {
|
||||
let chance = duplicate(this.data.data.compteurs.chance);
|
||||
chance.value = Math.max(chance.value + value, 0);
|
||||
if (limit) {
|
||||
@ -2443,22 +2390,21 @@ export class RdDActor extends Actor {
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
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) {
|
||||
countMonteeLaborieuse() { // Return +1 par queue/ombre/souffle Montée Laborieuse présente
|
||||
let countMonteeLaborieuse = this.data.items.filter(item => EffetsDraconiques.isMonteeLaborieuse(item)).length;
|
||||
if (countMonteeLaborieuse > 0) {
|
||||
ChatMessage.create({
|
||||
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.`,
|
||||
content: `Vous êtes sous le coup d'une Montée Laborieuse : vos montées en TMR coûtent ${countMonteeLaborieuse} Point de Rêve de plus.`,
|
||||
whisper: ChatMessage.getWhisperRecipients(game.user.name)
|
||||
});
|
||||
return countMonteLaborieuse;
|
||||
}
|
||||
return 0;
|
||||
return countMonteeLaborieuse;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
refreshTMRView( tmrData ) {
|
||||
refreshTMRView(tmrData) {
|
||||
console.log("REFRESH !!!!");
|
||||
if ( this.currentTMR ) {
|
||||
if (this.currentTMR) {
|
||||
this.currentTMR.forceDemiRevePositionView();
|
||||
}
|
||||
}
|
||||
@ -2467,22 +2413,18 @@ export class RdDActor extends Actor {
|
||||
async displayTMR(mode = "normal") {
|
||||
let isRapide = mode == "rapide";
|
||||
if (mode != "visu") {
|
||||
let minReveValue = (isRapide) ? 3 : 2;
|
||||
let minReveValue = (isRapide && !EffetsDraconiques.isDeplacementAccelere(this) ? 3 : 2) + this.countMonteeLaborieuse();
|
||||
if (this.getReveActuel() < minReveValue) {
|
||||
ChatMessage.create({
|
||||
content: "Vous n'avez plus assez de Points de Reve pour monter dans les Terres Médianes",
|
||||
content: `Vous n'avez les ${minReveValue} Points de Reve nécessaires pour monter dans les Terres Médianes`,
|
||||
whisper: ChatMessage.getWhisperRecipients(game.user.name)
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (mode != 'visu') {
|
||||
// Notification au MJ
|
||||
ChatMessage.create({ content: this.name + " est monté dans les TMR en mode : " + mode, whisper: ChatMessage.getWhisperRecipients("GM") });
|
||||
}
|
||||
|
||||
let data = {
|
||||
mode: mode,
|
||||
fatigue: {
|
||||
malus: RdDUtility.calculMalusFatigue(this.data.data.sante.fatigue.value, this.data.data.sante.endurance.max),
|
||||
html: "<table class='table-fatigue'>" + RdDUtility.makeHTMLfatigueMatrix(this.data.data.sante.fatigue.value, this.data.data.sante.endurance.max).html() + "</table>"
|
||||
@ -2494,7 +2436,7 @@ export class RdDActor extends Actor {
|
||||
isRapide: isRapide
|
||||
}
|
||||
let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-tmr.html', data);
|
||||
this.currentTMR = new RdDTMRDialog(html, this, data, mode);
|
||||
this.currentTMR = await RdDTMRDialog.create(html, this, data);
|
||||
this.currentTMR.render(true);
|
||||
}
|
||||
|
||||
@ -2549,7 +2491,7 @@ export class RdDActor extends Actor {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
computeArmure(attackerRoll) {
|
||||
let dmg = (attackerRoll.dmg.dmgArme ??0) + (attackerRoll.dmg.dmgActor ?? 0);
|
||||
let dmg = (attackerRoll.dmg.dmgArme ?? 0) + (attackerRoll.dmg.dmgActor ?? 0);
|
||||
let arme = attackerRoll.arme;
|
||||
let protection = 0;
|
||||
const armures = this.data.items.filter(it => it.type == "armure" && it.data.equipe);
|
||||
@ -2612,7 +2554,7 @@ export class RdDActor extends Actor {
|
||||
|
||||
this.ajouterBlessure(encaissement); // Will upate the result table
|
||||
const perteVie = this.isEntiteCauchemar()
|
||||
? { newValue: 0}
|
||||
? { newValue: 0 }
|
||||
: await this.santeIncDec("vie", - encaissement.vie);
|
||||
const perteEndurance = await this.santeIncDec("endurance", -encaissement.endurance, (encaissement.critiques > 0));
|
||||
|
||||
@ -2718,8 +2660,10 @@ export class RdDActor extends Actor {
|
||||
} else {
|
||||
// TODO: status effect dead
|
||||
this.addStatusEffectById('dead');
|
||||
ChatMessage.create({ content: `<img class="chat-icon" src="icons/svg/skull.svg" alt="charge" />
|
||||
<strong>${this.name} vient de succomber à une seconde blessure critique ! Que les Dragons gardent son Archétype en paix !</strong>` });
|
||||
ChatMessage.create({
|
||||
content: `<img class="chat-icon" src="icons/svg/skull.svg" alt="charge" />
|
||||
<strong>${this.name} vient de succomber à une seconde blessure critique ! Que les Dragons gardent son Archétype en paix !</strong>`
|
||||
});
|
||||
encaissement.critiques -= count;
|
||||
encaissement.mort = true;
|
||||
break;
|
||||
@ -2957,9 +2901,13 @@ export class RdDActor extends Actor {
|
||||
/* -------------------------------------------- */
|
||||
buildVehiculesList() {
|
||||
return this._buildActorLinksList(
|
||||
this.data.data.subacteurs?.vehicules??[],
|
||||
vehicle => {return { id: vehicle.id, name: vehicle.data.name, categorie: vehicle.data.data.categorie,
|
||||
structure: vehicle.data.data.structure, img: vehicle.data.img } ;});
|
||||
this.data.data.subacteurs?.vehicules ?? [],
|
||||
vehicle => {
|
||||
return {
|
||||
id: vehicle.id, name: vehicle.data.name, categorie: vehicle.data.data.categorie,
|
||||
structure: vehicle.data.data.structure, img: vehicle.data.img
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
@ -2972,35 +2920,35 @@ export class RdDActor extends Actor {
|
||||
return this._buildActorLinksList(this.data.data.subacteurs?.montures ?? []);
|
||||
}
|
||||
|
||||
_buildActorLinksList(links, actorTransformation=it => { return { id: it.id, name: it.data.name, img: it.data.img }; }) {
|
||||
_buildActorLinksList(links, actorTransformation = it => { return { id: it.id, name: it.data.name, img: it.data.img }; }) {
|
||||
return links.map(link => game.actors.get(link.id))
|
||||
.filter(it => it != null)
|
||||
.map(actorTransformation);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async pushSubacteur( actor, dataArray, dataPath, dataName ) {
|
||||
let alreadyPresent = dataArray.find( attached => attached.id == actor.data._id);
|
||||
if ( !alreadyPresent ) {
|
||||
async pushSubacteur(actor, dataArray, dataPath, dataName) {
|
||||
let alreadyPresent = dataArray.find(attached => attached.id == actor.data._id);
|
||||
if (!alreadyPresent) {
|
||||
let newArray = duplicate(dataArray);
|
||||
newArray.push( { id: actor.data._id });
|
||||
await this.update( { [dataPath]: newArray });
|
||||
newArray.push({ id: actor.data._id });
|
||||
await this.update({ [dataPath]: newArray });
|
||||
} else {
|
||||
ui.notifications.warn(dataName+" est déja attaché à ce Personnage.");
|
||||
ui.notifications.warn(dataName + " est déja attaché à ce Personnage.");
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
addSubacteur( actorId ) {
|
||||
let actor = game.actors.get( actorId );
|
||||
addSubacteur(actorId) {
|
||||
let actor = game.actors.get(actorId);
|
||||
//console.log("Ajout acteur : ", actor, this);
|
||||
if (actor && actor.owner ) {
|
||||
if (actor && actor.owner) {
|
||||
if (actor.data.type == 'vehicule') {
|
||||
this.pushSubacteur( actor, this.data.data.subacteurs.vehicules, 'data.subacteurs.vehicules', 'Ce Véhicule' );
|
||||
this.pushSubacteur(actor, this.data.data.subacteurs.vehicules, 'data.subacteurs.vehicules', 'Ce Véhicule');
|
||||
} else if (actor.data.type == 'creature') {
|
||||
this.pushSubacteur( actor, this.data.data.subacteurs.montures, 'data.subacteurs.montures', 'Cette Monture' );
|
||||
this.pushSubacteur(actor, this.data.data.subacteurs.montures, 'data.subacteurs.montures', 'Cette Monture');
|
||||
} else if (actor.data.type == 'personnage') {
|
||||
this.pushSubacteur( actor, this.data.data.subacteurs.suivants, 'data.subacteurs.suivants', 'Ce Suivant' );
|
||||
this.pushSubacteur(actor, this.data.data.subacteurs.suivants, 'data.subacteurs.suivants', 'Ce Suivant');
|
||||
}
|
||||
} else {
|
||||
ui.notifications.warn("Vous n'avez pas les droits sur l'acteur que vous attachez.")
|
||||
@ -3008,13 +2956,13 @@ export class RdDActor extends Actor {
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async removeSubacteur( actorId ) {
|
||||
let newVehicules = this.data.data.subacteurs.vehicules.filter(function(obj, index, arr){ return obj.id != actorId } );
|
||||
let newSuivants = this.data.data.subacteurs.suivants.filter(function(obj, index, arr){ return obj.id != actorId } );
|
||||
let newMontures = this.data.data.subacteurs.montures.filter(function(obj, index, arr){ return obj.id != actorId } );
|
||||
await this.update( { 'data.subacteurs.vehicules': newVehicules });
|
||||
await this.update( { 'data.subacteurs.suivants': newSuivants });
|
||||
await this.update( { 'data.subacteurs.montures': newMontures });
|
||||
async removeSubacteur(actorId) {
|
||||
let newVehicules = this.data.data.subacteurs.vehicules.filter(function (obj, index, arr) { return obj.id != actorId });
|
||||
let newSuivants = this.data.data.subacteurs.suivants.filter(function (obj, index, arr) { return obj.id != actorId });
|
||||
let newMontures = this.data.data.subacteurs.montures.filter(function (obj, index, arr) { return obj.id != actorId });
|
||||
await this.update({ 'data.subacteurs.vehicules': newVehicules });
|
||||
await this.update({ 'data.subacteurs.suivants': newSuivants });
|
||||
await this.update({ 'data.subacteurs.montures': newMontures });
|
||||
}
|
||||
/* -------------------------------------------- */
|
||||
async onUpdateActor(update, options, actorId) {
|
||||
@ -3082,14 +3030,14 @@ export class RdDActor extends Actor {
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
deleteStatusEffectById(id, options = { renderSheet: true}) {
|
||||
deleteStatusEffectById(id, options = { renderSheet: true }) {
|
||||
const effects = Array.from(this.effects?.values())
|
||||
.filter(it => it.data.flags.core?.statusId == id);
|
||||
this._deleteStatusEffects(effects, options);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
deleteStatusEffect(effect, options = { renderSheet: true}) {
|
||||
deleteStatusEffect(effect, options = { renderSheet: true }) {
|
||||
const toDelete = Array.from(this.effects?.values())
|
||||
.filter(it => StatusEffects.statusId(it.data) == StatusEffects.statusId(effect));
|
||||
this._deleteStatusEffects(toDelete, options);
|
||||
@ -3107,13 +3055,13 @@ export class RdDActor extends Actor {
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async addStatusEffectById(id, options = { renderSheet: true}) {
|
||||
async addStatusEffectById(id, options = { renderSheet: true }) {
|
||||
const statusEffect = CONFIG.statusEffects.find(it => it.id == id);
|
||||
await this.addStatusEffect(statusEffect, options);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async addStatusEffect(statusEffect, options = { renderSheet: true}) {
|
||||
async addStatusEffect(statusEffect, options = { renderSheet: true }) {
|
||||
this.deleteStatusEffectById(statusEffect.id, options);
|
||||
const effet = duplicate(statusEffect);
|
||||
effet["flags.core.statusId"] = effet.id;
|
||||
@ -3123,9 +3071,57 @@ export class RdDActor extends Actor {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async updateEmbeddedEntity(embeddedName, data, options) {
|
||||
if ( data && data['data.defaut_carac'] && data['data.xp'] ) { // C'est une compétence
|
||||
this.checkCompetenceXP(data['name'], data['data.xp'] );
|
||||
if (data && data['data.defaut_carac'] && data['data.xp']) { // C'est une compétence
|
||||
this.checkCompetenceXP(data['name'], data['data.xp']);
|
||||
}
|
||||
return super.updateEmbeddedEntity(embeddedName, data, options);
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async onCreateOwnedItem(item, options, id) {
|
||||
switch (item.type) {
|
||||
case 'tete':
|
||||
case 'queue':
|
||||
case 'ombre':
|
||||
case 'souffle':
|
||||
await this.onCreateOwnedDraconique(item, options, id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
async onDeleteOwnedItem(item, options, id) {
|
||||
switch (item.type) {
|
||||
case 'tete':
|
||||
case 'queue':
|
||||
case 'ombre':
|
||||
case 'souffle':
|
||||
await this.onDeleteOwnedDraconique(item, options, id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
async onCreateOwnedDraconique(item, options, id) {
|
||||
|
||||
let draconique = Draconique.all().find(it => it.match(item));
|
||||
if (draconique) {
|
||||
draconique.onActorCreateOwned(this, item)
|
||||
|
||||
this.notifyGestionTeteSouffleQueue(item, draconique.manualMessage());
|
||||
}
|
||||
}
|
||||
|
||||
async onDeleteOwnedDraconique(item, options, id) {
|
||||
|
||||
let draconique = Draconique.all().find(it => it.match(item));
|
||||
if (draconique) {
|
||||
draconique.onActorDeleteOwned(this, item)
|
||||
}
|
||||
}
|
||||
|
||||
notifyGestionTeteSouffleQueue(item, manualMessage=true){
|
||||
ChatMessage.create({
|
||||
whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name),
|
||||
content: `${this.name} a reçu un/une ${item.type}: ${item.name}, qui ${manualMessage ? "n'est pas" : "est" } géré automatiquement. ${manualMessage ?? ''}`
|
||||
});
|
||||
}}
|
||||
|
||||
|
@ -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';
|
||||
}
|
||||
|
||||
}
|
@ -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',
|
||||
|
@ -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 {
|
||||
|
@ -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"
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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,8 +396,12 @@ 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 + ")");
|
||||
@ -318,39 +411,18 @@ export class RdDTMRDialog extends Dialog {
|
||||
/* -------------------------------------------- */
|
||||
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') {
|
||||
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;
|
||||
@ -361,8 +433,6 @@ export class RdDTMRDialog extends Dialog {
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async manageCaseHumide(tmr) {
|
||||
@ -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;
|
||||
}
|
||||
);
|
||||
dialog.render(true);
|
||||
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é' }
|
||||
}
|
||||
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;
|
||||
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 }
|
||||
]
|
||||
}
|
||||
return false;
|
||||
);
|
||||
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;
|
||||
isCaseInondee(coord) {
|
||||
return this.casesSpeciales.find(c => EffetsDraconiques.isCaseInondee(c, coord));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
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;
|
||||
isTerreAttache(coord) {
|
||||
return this.casesSpeciales.find(c => EffetsDraconiques.isTerreAttache(c, coord));
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
isCaseMaitrisee(coord) {
|
||||
return this.casesSpeciales.find(c => EffetsDraconiques.isCaseMaitrisee(c, coord));
|
||||
}
|
||||
return false;
|
||||
|
||||
/* -------------------------------------------- */
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -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"
|
||||
};
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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>D’une argentine fontaine,
|
||||
<br>Qui d’une fuite lointaine,
|
||||
<br>Te rends au sein fluctueux
|
||||
<br>De l’Océ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:
|
||||
«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»`
|
||||
},
|
||||
]
|
||||
|
||||
/* -------------------------------------------- */
|
||||
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
20
module/tmr/carte-tmr.js
Normal 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
31
module/tmr/debordement.js
Normal 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
27
module/tmr/demi-reve.js
Normal 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
118
module/tmr/draconique.js
Normal 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() }
|
||||
});
|
||||
}
|
||||
|
||||
}
|
152
module/tmr/effets-draconiques.js
Normal file
152
module/tmr/effets-draconiques.js
Normal 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] : [];
|
||||
}
|
||||
|
||||
}
|
33
module/tmr/fermeture-cites.js
Normal file
33
module/tmr/fermeture-cites.js
Normal 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
148
module/tmr/pixi-tmr.js
Normal 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 };
|
||||
}
|
||||
|
||||
}
|
40
module/tmr/pont-impraticable.js
Normal file
40
module/tmr/pont-impraticable.js
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
42
module/tmr/present-cites.js
Normal file
42
module/tmr/present-cites.js
Normal 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
25
module/tmr/quete-eaux.js
Normal 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
22
module/tmr/rencontre.js
Normal 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 } });
|
||||
}
|
||||
}
|
28
module/tmr/reserve-extensible.js
Normal file
28
module/tmr/reserve-extensible.js
Normal 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);
|
||||
}
|
||||
|
||||
}
|
22
module/tmr/sort-reserve.js
Normal file
22
module/tmr/sort-reserve.js
Normal 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 });
|
||||
}
|
||||
}
|
25
module/tmr/terre-attache.js
Normal file
25
module/tmr/terre-attache.js
Normal 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
30
module/tmr/trou-noir.js
Normal 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);
|
||||
}
|
||||
}
|
@ -1,10 +1,11 @@
|
||||
{"_id":"5JccZSafqCXYqrwU","name":"Connaissance du fleuve","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"type":"tete","data":{"description":"<p>Cette tête permet de téléporter instantanément son demi-rêve de n’importe quelle case de fleuve à n’importe quelle autre. Il s’agit du fleuve seul, à l’exclusion des cases de lac et marais. La téléportation remplace le mouvement normal et coûte donc un point de fatigue. Comme après un mouvement normal, une rencontre doit être tirée dans la case de fleuve d’arrivée, puis cette dernière doit être maîtrisée selon la règle usuelle. Noter enfin que la téléportation n’est possible que si le demi-rêve est libre de son mouvement, c’est-à-dire s’il n’est sous l’emprise ni d’un Reflet, ni d’un Tourbillon. Unique.</p>","refoulement":null},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp"}
|
||||
{"_id":"BT18LAdIqEgSG2Hh","name":"Réserve en sécurité","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"type":"tete","data":{"description":"<p>Un haut-rêvant possédant cette tête peut pénétrer sur une case où il a un sort en réserve sans forcément le déclencher s’il ne le souhaite pas. Même chose si un Tourbillon l’abandonne sur une case de réserve. Déclencher un sort en réserve devient un acte volontaire. Même chose pour un échec total en réserve. Cette tête s’applique à toutes les cases. Unique.</p>","refoulement":null},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp"}
|
||||
{"_id":"SFxPAvWpEGYHI8mO","name":"Connaissance intuitive d'un nouveau sort","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"type":"tete","data":{"description":"<p>Le haut-rêvant bénéficiaire de cette tête se retrouve en possession d’un nouveau sort ou rituel, déterminé par un procédé aléatoire quelconque.</p>","refoulement":null},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp"}
|
||||
{"_id":"VWOXA0q6GB7o8oxz","name":"Don de double-rêve","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"type":"tete","data":{"description":"<p>Lors de la récupération des points de rêve, permet de jeter 1ddr toutes les demi-heures draconiques au lieu de toutes les heures. Unique.</p>","refoulement":null},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp"}
|
||||
{"_id":"YDo0a0ApM8iW9g82","name":"Terre d'attache","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"type":"tete","data":{"description":"<p>Par cette tête, le haut-rêvant peut désigner une case des TMR de son choix à l’exception d’une case humide. Ensuite, à n’importe quel round de son périple en TMR, il peut s’y téléporter directement quel que soit le nombre de cases qui l’en sépare. Cette téléportation instantanée remplace son mouvement normal et coûte donc un point de fatigue. De même, tout comme après un mouvement normal, 1d7 de rencontre doit être tiré dès l’arrivée dans la terre d’attache. Enfin, le haut-rêvant ne peut s’y téléporter que s’il est libre de son mouvement, c’est-à-dire s’il n’est sous l’emprise ni d’un Reflet d’ancien rêve, ni d’un Tourbillon. Cumulable : on peut avoir plusieurs terres d’attache.</p>","refoulement":null},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp"}
|
||||
{"_id":"ZVh8PLlAzAJulr37","name":"Don de déplacement accéléré","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"type":"tete","data":{"description":"<p>Permet à tout moment l’option du déplacement accéléré dans les TMR sans avoir à dépenser un point de rêve supplémentaire. Unique.</p>","refoulement":null},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp"}
|
||||
{"_id":"Zlt01O2sFrVR9pus","name":"Augmentation du seuil de rêve","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"type":"tete","data":{"description":"<p>Permet d’augmenter le seuil de rêve de 2 points. Cumulable jusqu’à un maximum du double de la caractéristique Rêve.</p>","refoulement":null},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp"}
|
||||
{"_id":"ZuTV36GyOhFgTlE7","name":"Présents de cités","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"type":"tete","data":{"description":"<p>Par cette tête, chaque cité offre automatiquement soit une Fleur des rêves, soit un Passeur, soit un Messager, dès qu’elle est visitée par le demi-rêve du haut-rêvant. Il n’y a pas de d7 à tirer, le haut-rêvant choisit librement ce qu’il préfère, et la rencontre est considérée automatiquement maîtrisée, Fleurs, Messagers et Passeurs ayant une force de 2d6 points. Dès qu’elle a offert son présent, la cité redevient normale en ce qui concerne le tirage des rencontres; il n’y a qu’un seul présent par cité. La tête de Dragon prend fin dès que toutes les cités (22) ont été visitées; entretemps, la tête ne peut être ré-obtenue.</p>","refoulement":null},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp"}
|
||||
{"_id":"a3Y5W0AX5EKxZRSL","name":"Quête des eaux","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"type":"tete","data":{"description":"<p>Sous l’influence de cette tête, le haut-rêvant est invité à se rendre dans un lac ou dans un marais de son choix. Dès que cette case, lac ou marais, a été maîtrisée selon les règles usuelles, la victoire devient définitive, et la case n’aura plus jamais besoin d’être maîtrisée. Une fois le lac ou le marais choisi, le haut-rêvant ne peut en changer, mais a droit à un nombre illimité d’essais pour le maîtriser. Cumulable : on peut maîtriser définitivement plusieurs lacs ou marais.</p>","refoulement":null},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp"}
|
||||
{"_id":"bFdU6ddgj4BAlJZX","name":"Réserve extensible","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"type":"tete","data":{"description":"<p>Une (seule) case spécifique librement choisie par le haut-rêvant à l’exception d’une case de fleuve, n’est plus dorénavant limitée à un seul sort en réserve. Le haut-rêvant peut y en stocker autant qu’il le désire (dans les limites du nombre permis par ses niveaux). En conséquence, tant que ce maximum n’est pas atteint, le demi-rêve peut entrer dans la case concernée sans déclencher les sorts qui s’y trouvent. Le haut-rêvant peut se contenter d’y passer, ou rajouter un sort aux précédents. Dès que le maximum permis est atteint, le fait de passer par la case déclenche automatiquement l’un des sorts (au choix). Les sorts peuvent être déclenchés l’un après l’autre (rappelons que déclencher un sort fait obligatoirement redescendre le demi-rêve), ou déclenchés ensemble (tout ou partie d’entre eux) à la condition expresse qu’ils aient la même cible (même créature, objet, ou centre de zone). Cumulable : on peut avoir plusieurs cases de réserve extensible.</p>","refoulement":null},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp"}
|
||||
{"_id":"5JccZSafqCXYqrwU","name":"Connaissance du fleuve","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"type":"tete","data":{"description":"<p>Cette tête permet de téléporter instantanément son demi-rêve de n’importe quelle case de fleuve à n’importe quelle autre. Il s’agit du fleuve seul, à l’exclusion des cases de lac et marais. La téléportation remplace le mouvement normal et coûte donc un point de fatigue. Comme après un mouvement normal, une rencontre doit être tirée dans la case de fleuve d’arrivée, puis cette dernière doit être maîtrisée selon la règle usuelle. Noter enfin que la téléportation n’est possible que si le demi-rêve est libre de son mouvement, c’est-à-dire s’il n’est sous l’emprise ni d’un Reflet, ni d’un Tourbillon. Unique.</p>","refoulement":null},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp","effects":[]}
|
||||
{"_id":"BT18LAdIqEgSG2Hh","name":"Réserve en sécurité","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"type":"tete","data":{"description":"<p>Un haut-rêvant possédant cette tête peut pénétrer sur une case où il a un sort en réserve sans forcément le déclencher s’il ne le souhaite pas. Même chose si un Tourbillon l’abandonne sur une case de réserve. Déclencher un sort en réserve devient un acte volontaire. Même chose pour un échec total en réserve. Cette tête s’applique à toutes les cases. Unique.</p>","refoulement":null},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp","effects":[]}
|
||||
{"name":"Présent des cités","permission":{"default":0,"Q2G6GTdrotKzYGUC":3},"type":"tete","data":{"description":"<p>Par cette tête, chaque cité offre automatiquement soit une Fleur des rêves, soit un Passeur, soit un Messager, dès qu’elle est visitée par le demi-rêve du haut-rêvant. Il n’y a pas de d7 à tirer, le haut-rêvant choisit librement ce qu’il préfère, et la rencontre est considérée automatiquement maîtrisée, Fleurs, Messagers et Passeurs ayant une force de 2d6 points. Dès qu’elle a offert son présent, la cité redevient normale en ce qui concerne le tirage des rencontres; il n’y a qu’un seul présent par cité. La tête de Dragon prend fin dès que toutes les cités (22) ont été visitées; entretemps, la tête ne peut être ré-obtenue.</p>"},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp","effects":[],"_id":"E4a4O1IdrgbNGpVy"}
|
||||
{"_id":"SFxPAvWpEGYHI8mO","name":"Connaissance intuitive d'un nouveau sort","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"type":"tete","data":{"description":"<p>Le haut-rêvant bénéficiaire de cette tête se retrouve en possession d’un nouveau sort ou rituel, déterminé par un procédé aléatoire quelconque.</p>","refoulement":null},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp","effects":[]}
|
||||
{"_id":"VWOXA0q6GB7o8oxz","name":"Don de double-rêve","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"type":"tete","data":{"description":"<p>Lors de la récupération des points de rêve, permet de jeter 1ddr toutes les demi-heures draconiques au lieu de toutes les heures. Unique.</p>","refoulement":null},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp","effects":[]}
|
||||
{"_id":"YDo0a0ApM8iW9g82","name":"Terre d'attache","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"type":"tete","data":{"description":"<p>Par cette tête, le haut-rêvant peut désigner une case des TMR de son choix à l’exception d’une case humide. Ensuite, à n’importe quel round de son périple en TMR, il peut s’y téléporter directement quel que soit le nombre de cases qui l’en sépare. Cette téléportation instantanée remplace son mouvement normal et coûte donc un point de fatigue. De même, tout comme après un mouvement normal, 1d7 de rencontre doit être tiré dès l’arrivée dans la terre d’attache. Enfin, le haut-rêvant ne peut s’y téléporter que s’il est libre de son mouvement, c’est-à-dire s’il n’est sous l’emprise ni d’un Reflet d’ancien rêve, ni d’un Tourbillon. Cumulable : on peut avoir plusieurs terres d’attache.</p>","refoulement":null},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp","effects":[]}
|
||||
{"_id":"ZVh8PLlAzAJulr37","name":"Don de déplacement accéléré","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"type":"tete","data":{"description":"<p>Permet à tout moment l’option du déplacement accéléré dans les TMR sans avoir à dépenser un point de rêve supplémentaire. Unique.</p>","refoulement":null},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp","effects":[]}
|
||||
{"_id":"Zlt01O2sFrVR9pus","name":"Augmentation du seuil de rêve","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"type":"tete","data":{"description":"<p>Permet d’augmenter le seuil de rêve de 2 points. Cumulable jusqu’à un maximum du double de la caractéristique Rêve.</p>","refoulement":null},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp","effects":[]}
|
||||
{"_id":"ZuTV36GyOhFgTlE7","name":"Présents de cités","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"type":"tete","data":{"description":"<p>Par cette tête, chaque cité offre automatiquement soit une Fleur des rêves, soit un Passeur, soit un Messager, dès qu’elle est visitée par le demi-rêve du haut-rêvant. Il n’y a pas de d7 à tirer, le haut-rêvant choisit librement ce qu’il préfère, et la rencontre est considérée automatiquement maîtrisée, Fleurs, Messagers et Passeurs ayant une force de 2d6 points. Dès qu’elle a offert son présent, la cité redevient normale en ce qui concerne le tirage des rencontres; il n’y a qu’un seul présent par cité. La tête de Dragon prend fin dès que toutes les cités (22) ont été visitées; entretemps, la tête ne peut être ré-obtenue.</p>","refoulement":null},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp","effects":[]}
|
||||
{"_id":"a3Y5W0AX5EKxZRSL","name":"Quête des eaux","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"type":"tete","data":{"description":"<p>Sous l’influence de cette tête, le haut-rêvant est invité à se rendre dans un lac ou dans un marais de son choix. Dès que cette case, lac ou marais, a été maîtrisée selon les règles usuelles, la victoire devient définitive, et la case n’aura plus jamais besoin d’être maîtrisée. Une fois le lac ou le marais choisi, le haut-rêvant ne peut en changer, mais a droit à un nombre illimité d’essais pour le maîtriser. Cumulable : on peut maîtriser définitivement plusieurs lacs ou marais.</p>","refoulement":null},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp","effects":[]}
|
||||
{"_id":"bFdU6ddgj4BAlJZX","name":"Réserve extensible","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"type":"tete","data":{"description":"<p>Une (seule) case spécifique librement choisie par le haut-rêvant à l’exception d’une case de fleuve, n’est plus dorénavant limitée à un seul sort en réserve. Le haut-rêvant peut y en stocker autant qu’il le désire (dans les limites du nombre permis par ses niveaux). En conséquence, tant que ce maximum n’est pas atteint, le demi-rêve peut entrer dans la case concernée sans déclencher les sorts qui s’y trouvent. Le haut-rêvant peut se contenter d’y passer, ou rajouter un sort aux précédents. Dès que le maximum permis est atteint, le fait de passer par la case déclenche automatiquement l’un des sorts (au choix). Les sorts peuvent être déclenchés l’un après l’autre (rappelons que déclencher un sort fait obligatoirement redescendre le demi-rêve), ou déclenchés ensemble (tout ou partie d’entre eux) à la condition expresse qu’ils aient la même cible (même créature, objet, ou centre de zone). Cumulable : on peut avoir plusieurs cases de réserve extensible.</p>","refoulement":null},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp","effects":[]}
|
||||
|
@ -3,3 +3,9 @@
|
||||
<option value="debordement">Débordement</option>
|
||||
<option value="reserve_extensible">Réserve extensible</option>
|
||||
<option value="maitrisee">Case humide maitrisée (Quête des Eaux)</option>
|
||||
<option value="fermeture">Fermeture cité</option>
|
||||
<option value="pont-impraticable">Pont impraticable</option>
|
||||
<option value="desorientation">Désoriantation</option>
|
||||
<option value="periple">Periple</option>
|
||||
<option value="pelerinage">Pèlerinage</option>
|
||||
<option value="present-cites">Présents des cités</option>
|
||||
|
@ -1,20 +1,20 @@
|
||||
<img class="chat-icon" src="{{competence.img}}" alt="{{competence.name}}"/>
|
||||
<h4 data-categorie="tmr" data-actor-id="{{actor._id}}">
|
||||
{{alias}} tente de maîtriser {{le tmr.genre}} {{tmr.label}} ({{tmr.coord}})
|
||||
{{alias}} tente de {{maitrise.verbe}} {{le tmr.genre}} {{tmr.label}} ({{tmr.coord}})
|
||||
</h4>
|
||||
{{#if previous}}
|
||||
{{#each previous as |rolled key|}}
|
||||
{{#with previous}}
|
||||
{{> "systems/foundryvtt-reve-de-dragon/templates/chat-infojet.html"}}
|
||||
<br>Double résistance du fleuve!
|
||||
{{/each}}
|
||||
<br><strong>Double résistance du fleuve!</strong><br>
|
||||
{{/with}}
|
||||
{{/if}}
|
||||
{{> "systems/foundryvtt-reve-de-dragon/templates/chat-infojet.html"}}
|
||||
<hr>
|
||||
<span>
|
||||
{{#if rolled.isSuccess}}
|
||||
Vous avez maîtrisé {{le tmr.genre}} {{#if (eq tmr.type 'fleuve')}}Fleuve de l'Oubli{{else}}{{tmr.label}}{{/if}} !
|
||||
Vous parvenez à {{maitrise.verbe}} {{le tmr.genre}} {{tmr.label}} !
|
||||
{{else}}
|
||||
Vous ne parvenez pas à surmonter {{le tmr.genre}} {{#if (eq tmr.type 'fleuve')}}Fleuve de l'Oubli{{else}}{{tmr.label}}{{/if}}.
|
||||
Vous ne parvenez pas à {{maitrise.verbe}} {{le tmr.genre}} {{tmr.label}}.
|
||||
Vous <strong>quittez les Terres Médianes</strong> !
|
||||
{{#if souffle}}
|
||||
<br>De plus, votre échec total vous fait subir un Souffle de Dragon : {{souffle.name}}
|
Loading…
Reference in New Issue
Block a user