From b226e5c09ad01602d25f337ff951545e2714af24 Mon Sep 17 00:00:00 2001 From: Vincent Vandemeulebrouck Date: Thu, 11 Feb 2021 02:48:27 +0100 Subject: [PATCH] =?UTF-8?q?Am=C3=A9liorations=20des=20tmr?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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 --- icons/svg/gift.svg | 118 ++++ icons/svg/wave.svg | 94 +++ module/actor-sheet.js | 1 - module/actor-vehicule-sheet.js | 1 - module/actor.js | 526 +++++++------- module/item.js | 41 -- module/poetique.js | 3 +- module/rdd-commands.js | 15 +- module/rdd-compendium-organiser.js | 22 +- module/rdd-main.js | 2 + module/rdd-roll.js | 6 - module/rdd-rolltables.js | 5 + module/rdd-tmr-dialog.js | 649 ++++++++---------- module/rdd-tmr-rencontre-dialog.js | 2 +- module/rolldata-ajustements.js | 4 +- module/tmr-rencontres.js | 8 +- module/tmr-utility.js | 106 ++- module/tmr/carte-tmr.js | 20 + module/tmr/debordement.js | 31 + module/tmr/demi-reve.js | 27 + module/tmr/draconique.js | 118 ++++ module/tmr/effets-draconiques.js | 152 ++++ module/tmr/fermeture-cites.js | 33 + module/tmr/pixi-tmr.js | 148 ++++ module/tmr/pont-impraticable.js | 40 ++ module/tmr/present-cites.js | 42 ++ module/tmr/quete-eaux.js | 25 + module/tmr/rencontre.js | 22 + module/tmr/reserve-extensible.js | 28 + module/tmr/sort-reserve.js | 22 + module/tmr/terre-attache.js | 25 + module/tmr/trou-noir.js | 30 + packs/tetes-de-dragon-pour-haut-revants.db | 21 +- templates/casetmr-specific-list.html | 6 + ...r.html => chat-resultat-maitrise-tmr.html} | 12 +- ...ide.html => dialog-roll-maitrise-tmr.html} | 0 36 files changed, 1631 insertions(+), 774 deletions(-) create mode 100644 icons/svg/gift.svg create mode 100644 icons/svg/wave.svg delete mode 100644 module/item.js create mode 100644 module/tmr/carte-tmr.js create mode 100644 module/tmr/debordement.js create mode 100644 module/tmr/demi-reve.js create mode 100644 module/tmr/draconique.js create mode 100644 module/tmr/effets-draconiques.js create mode 100644 module/tmr/fermeture-cites.js create mode 100644 module/tmr/pixi-tmr.js create mode 100644 module/tmr/pont-impraticable.js create mode 100644 module/tmr/present-cites.js create mode 100644 module/tmr/quete-eaux.js create mode 100644 module/tmr/rencontre.js create mode 100644 module/tmr/reserve-extensible.js create mode 100644 module/tmr/sort-reserve.js create mode 100644 module/tmr/terre-attache.js create mode 100644 module/tmr/trou-noir.js rename templates/{chat-fleuve-tmr.html => chat-resultat-maitrise-tmr.html} (62%) rename templates/{dialog-roll-tmr-humide.html => dialog-roll-maitrise-tmr.html} (100%) diff --git a/icons/svg/gift.svg b/icons/svg/gift.svg new file mode 100644 index 00000000..d62ef934 --- /dev/null +++ b/icons/svg/gift.svg @@ -0,0 +1,118 @@ + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/svg/wave.svg b/icons/svg/wave.svg new file mode 100644 index 00000000..ac6af981 --- /dev/null +++ b/icons/svg/wave.svg @@ -0,0 +1,94 @@ + + + + + + + +Created by potrace 1.15, written by Peter Selinger 2001-2017 + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/module/actor-sheet.js b/module/actor-sheet.js index 15d2eb2f..13d5c932 100644 --- a/module/actor-sheet.js +++ b/module/actor-sheet.js @@ -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"; diff --git a/module/actor-vehicule-sheet.js b/module/actor-vehicule-sheet.js index 0a2c90d7..a2dd0cb3 100644 --- a/module/actor-vehicule-sheet.js +++ b/module/actor-vehicule-sheet.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"; /* -------------------------------------------- */ diff --git a/module/actor.js b/module/actor.js index d82abf96..b50a99ca 100644 --- a/module/actor.js +++ b/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,14 +587,14 @@ 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; rollData.competence.data.defaut_carac = 'reve-actuel'; const dialog = await RdDRoll.create(this, rollData, - { + { html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-reve-de-dragon.html', options: { height: 400 } }, @@ -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,20 +749,20 @@ export class RdDActor extends Actor { } else { console.log("Competence not found", compName); } - RdDUtility.checkThanatosXP( compName ); + RdDUtility.checkThanatosXP(compName); } - /* -------------------------------------------- */ - async updateCompetenceXPSort(compName, compValue) { - let comp = this.getCompetence(compName); - if (comp) { - const update = { _id: comp._id, 'data.xp_sort': compValue }; - const updated = await this.updateEmbeddedEntity("OwnedItem", update); // Updates one EmbeddedEntity - } else { - console.log("Competence not found", compName); - } + /* -------------------------------------------- */ + async updateCompetenceXPSort(compName, compValue) { + let comp = this.getCompetence(compName); + if (comp) { + const update = { _id: comp._id, 'data.xp_sort': compValue }; + const updated = await this.updateEmbeddedEntity("OwnedItem", update); // Updates one EmbeddedEntity + } else { + console.log("Competence not found", compName); } - + } + /* -------------------------------------------- */ async updateCompetenceArchetype(compName, compValue) { let comp = this.getCompetence(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,42 +926,42 @@ 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 + else maxEnc = this.data.data.attributs.encombrement.value; let diffEnc = Number(this.encTotal) - Number(maxEnc); return Math.max(0, Math.ceil(diffEnc)); @@ -999,7 +998,7 @@ export class RdDActor extends Actor { await this.update({ "data.attributs.malusarmure": malusArmureData }); } } - + /* -------------------------------------------- */ async computePrixTotalEquipement() { let prixTotalEquipement = 0; @@ -1011,7 +1010,7 @@ export class RdDActor extends Actor { if (item.data.quantite == undefined) item.data.quantite = 1; // Auto-fix if (item.data.cout < 0) item.data.cout = 0; // Auto-fix prixTotalEquipement += Number(item.data.cout) * Number(item.data.quantite); - } + } } // Mise à jour valeur totale de l'équipement this.prixTotalEquipement = prixTotalEquipement; @@ -1067,7 +1066,7 @@ export class RdDActor extends Actor { data.compteurs.surenc.value = - this.detectSurEncombrement(); } } - + /* -------------------------------------------- */ async ajouterRefoulement(value = 1) { let ret = "none"; @@ -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 Conquête : " + item.data.description; - } - else if (item.name.toLowerCase() == 'pélerinage') { - content = "RAPPEL ! Vous souffrez d'un Pélerinage : " + item.data.description; - } - else if (item.name.toLowerCase() == 'urgence draconique') { - content = "RAPPEL ! Vous souffrez d'une Urgence Draconique : " + item.data.description; - } - } else if (item.type == 'souffle') { - if (item.name.toLowerCase() == 'périple') { - content = "RAPPEL ! Vous souffrez du Souffle Périple. Vous devez gérer manuellement le détail du Périple.
" + item.data.description; - } else if (item.name.toLowerCase() == 'fermeture des cités') { - content = "RAPPEL ! Vous souffrez du Souffle Fermeture des Cités. Vous devez gérer manuellement le détail des Citées ré-ouvertes.
" + item.data.description; - } else if (item.name.toLowerCase() == 'désorientation') { - content = "RAPPEL ! Vous souffrez du Souffle Désorientation. Vous devez gérer avec votre MJ les effets de ce souffle.
" + item.data.description; - } else if (item.name.toLowerCase() == 'double résistance du fleuve') { - content = "RAPPEL ! Vous souffrez du Souffle Double Résistance du Fleuve. Vous devez gérer avec votre MJ les effets de ce souffle.
" + item.data.description; - } + if (EffetsDraconiques.isConquete(item)) { + messages.push("Vous souffrez d'une Conquête : " + item.data.description); } - if (content) { - ChatMessage.create({ - whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name), - content: content - }); + if (EffetsDraconiques.isPelerinage(item)) { + messages.push("Vous souffrez d'un Pélerinage : " + item.data.description); } + if (EffetsDraconiques.isUrgenceDraconique(item)) { + messages.push("Vous souffrez d'une Urgence Draconique : " + item.data.description); + } + if (EffetsDraconiques.isPeriple(item)) { + messages.push("Vous souffrez du Souffle Périple. Vous devez gérer manuellement le détail du Périple.
" + item.data.description); + } + if (EffetsDraconiques.isDesorientation(item)) { + messages.push("Vous souffrez du Souffle Désorientation. Vous devez gérer avec votre MJ les effets de ce souffle.
" + item.data.description); + } + } + + if (messages.length > 0) { + ChatMessage.create({ + whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name), + content: "RAPPEL !
" + messages.join('
') + }); } } + /* -------------------------------------------- */ + 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 }); } } @@ -1236,7 +1236,7 @@ export class RdDActor extends Actor { chance.value = value; await this.update({ "data.compteurs.chance": chance }); } - + /* -------------------------------------------- */ getSonne() { return !this.isEntiteCauchemar() && (this.data.data.sante.sonne?.value ?? false); @@ -1247,7 +1247,7 @@ export class RdDActor extends Actor { } await this.setStatusSonne(sonne); await this.setStateSonne(sonne); - } + } async setStateSonne(sonne) { if (this.isEntiteCauchemar()) { return; @@ -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 + "
"; - 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() } /* -------------------------------------------- */ @@ -1583,11 +1583,11 @@ export class RdDActor extends Actor { if (stress <= 0) { return; } - + const stressRoll = await this._stressRoll(this.getReveActuel()); - + const conversion = Math.floor(stress * stressRoll.factor / 100); - let dissolution = Math.max(0, Misc.toInt(this.data.data.compteurs.dissolution.value)); + let dissolution = Math.max(0, Misc.toInt(this.data.data.compteurs.dissolution.value)); let exaltation = Math.max(0, Misc.toInt(this.data.data.compteurs.exaltation.value)); const annule = Math.min(dissolution, exaltation); dissolution -= annule; @@ -1601,7 +1601,7 @@ export class RdDActor extends Actor { stress: stress, perte: Math.min(conversion, stress), convertis: conversion - perteDissolution, - xp: conversion - perteDissolution + exaltation, + xp: conversion - perteDissolution + exaltation, dissolution: dissolution, exaltation: exaltation }; @@ -1610,13 +1610,13 @@ export class RdDActor extends Actor { whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name), content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-resultat-transformer-stress.html`, stressRollData) }); - + let compteurs = duplicate(this.data.data.compteurs); compteurs.stress.value = Math.max(stress - stressRollData.perte - 1, 0); compteurs.experience.value += stressRollData.xp; compteurs.dissolution.value = dissolution - perteDissolution; compteurs.exaltation.value = 0; - await this.update({ "data.compteurs": compteurs }); + await this.update({ "data.compteurs": compteurs }); } /* -------------------------------------------- */ @@ -1628,7 +1628,7 @@ export class RdDActor extends Actor { result.factor = this._getFacteurStress(result); return result; } - + _getFacteurStress(stressRoll) { switch (stressRoll.code) { case "sign": return 75; @@ -1637,11 +1637,11 @@ export class RdDActor extends Actor { case "epart": return 10; case "etotal": return 0; case "part": - if (stressRoll.second.isSign) { - stressRoll.quality = "Double Particulière"; - return 150; - } - return 100; + if (stressRoll.second.isSign) { + stressRoll.quality = "Double Particulière"; + return 150; + } + return 100; } return 0; } @@ -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; @@ -1711,10 +1711,10 @@ export class RdDActor extends Actor { let xpResult = this.appliquerExperience(rollData.rolled, rollData.selectedCarac.label, rollData.competence); if (display && xpResult.result) { let xpmsg = "
Points d'expérience gagnés ! Carac: " + xpResult.xpCarac + ", Comp: " + xpResult.xpCompetence; - let message = { - whisher: ChatMessage.getWhisperRecipients( ["GM", this.name ] ), - content: "" + rollData.selectedCarac.label + "" + xpmsg, - } + let message = { + whisher: ChatMessage.getWhisperRecipients(["GM", this.name]), + content: "" + rollData.selectedCarac.label + "" + xpmsg, + } ChatMessage.create(message); } if (xpResult && xpResult.xpComp > 0 && rollData.competence) { @@ -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); @@ -2010,7 +1957,7 @@ export class RdDActor extends Actor { /* -------------------------------------------- */ async rollCompetence(name) { let rollData = { competence: this.getCompetence(name) } - + if (rollData.competence.type == 'competencecreature') { if (rollData.competence.data.iscombat) { const arme = RdDItemCompetenceCreature.toArme(competence); @@ -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`); } @@ -2190,13 +2137,13 @@ export class RdDActor extends Actor { const oeuvre = duplicate(this.getJeu(id)); await this._rollArt(artData, oeuvre.data?.caraccomp.toLowerCase() ?? 'chance', oeuvre); } - + async rollOeuvre(id) { const artData = { art: 'oeuvre', verbe: 'Interpréter' }; const oeuvre = duplicate(this.getOeuvre(id)); await this._rollArt(artData, oeuvre.data.default_carac, oeuvre); } - + /* -------------------------------------------- */ async rollMeditation(id) { let meditation = duplicate(this.getMeditation(id)); @@ -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: "" + RdDUtility.makeHTMLfatigueMatrix(this.data.data.sante.fatigue.value, this.data.data.sante.endurance.max).html() + "
" @@ -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: `charge - ${this.name} vient de succomber à une seconde blessure critique ! Que les Dragons gardent son Archétype en paix !` }); + ChatMessage.create({ + content: `charge + ${this.name} vient de succomber à une seconde blessure critique ! Que les Dragons gardent son Archétype en paix !` + }); 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,49 +2920,49 @@ 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.") } } - + /* -------------------------------------------- */ - 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,16 +3030,16 @@ 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); + .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)); + .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 ?? ''}` + }); + }} + diff --git a/module/item.js b/module/item.js deleted file mode 100644 index a7d43f62..00000000 --- a/module/item.js +++ /dev/null @@ -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'; - } - -} \ No newline at end of file diff --git a/module/poetique.js b/module/poetique.js index dcbb15f1..67f0d387 100644 --- a/module/poetique.js +++ b/module/poetique.js @@ -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. +
Les jours des Dragons sont les Ages des Hommes.` }, { reference: 'Denis Gerfaud', diff --git a/module/rdd-commands.js b/module/rdd-commands.js index d45ff108..3e5c3280 100644 --- a/module/rdd-commands.js +++ b/module/rdd-commands.js @@ -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 { diff --git a/module/rdd-compendium-organiser.js b/module/rdd-compendium-organiser.js index 2017af97..ad7e1ea2 100644 --- a/module/rdd-compendium-organiser.js +++ b/module/rdd-compendium-organiser.js @@ -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" } diff --git a/module/rdd-main.js b/module/rdd-main.js index ac026714..2b6b990f 100644 --- a/module/rdd-main.js +++ b/module/rdd-main.js @@ -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(); diff --git a/module/rdd-roll.js b/module/rdd-roll.js index bbde3323..16763b70 100644 --- a/module/rdd-roll.js +++ b/module/rdd-roll.js @@ -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(); diff --git a/module/rdd-rolltables.js b/module/rdd-rolltables.js index 629a180a..61b1fb19 100644 --- a/module/rdd-rolltables.js +++ b/module/rdd-rolltables.js @@ -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); diff --git a/module/rdd-tmr-dialog.js b/module/rdd-tmr-dialog.js index 924b020a..db874e8e 100644 --- a/module/rdd-tmr-dialog.js +++ b/module/rdd-tmr-dialog.js @@ -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 = "" + RdDUtility.makeHTMLfatigueMatrix(this.actor.data.data.sante.fatigue.value, this.actor.data.data.sante.endurance.max).html() + "
"; } /* -------------------------------------------- */ @@ -63,51 +196,6 @@ export class RdDTMRDialog extends Dialog { } } - /* -------------------------------------------- */ - displaySortReserve() { - console.debug("displaySortReserve", this.sortReserves); - for (let sort of this.sortReserves) { - this._trackToken(this._tokenSortEnReserve(sort)); - } - } - - /* -------------------------------------------- */ - displaySpecificCase() { - for (let caseSpeciale of this.casesSpeciales) { - console.log("SPEC CASE ", caseSpeciale); - if (caseSpeciale.data.specific == 'trounoir') { - this._trackToken(this._tokenTrouNoir(caseSpeciale.data.coord)); - } else if (caseSpeciale.data.specific == 'attache') { - this._trackToken(this._tokenTerreAttache(caseSpeciale.data.coord)); - } else if (caseSpeciale.data.specific == 'debordement') { - this._trackToken(this._tokenDebordement(caseSpeciale.data.coord)); - } else if (caseSpeciale.data.specific == 'maitrisee') { - this._trackToken(this._tokenMaitrisee(caseSpeciale.data.coord)); - } - } - } - - /* -------------------------------------------- */ - displayPreviousRencontres() { - console.debug("displayPreviousRencontres", this.rencontresExistantes); - for (let rencontre of this.rencontresExistantes) { - this._trackToken(this._tokenRencontre(rencontre)); - } - } - - /* -------------------------------------------- */ - updatePreviousRencontres() { - this._removeTokens(t => t.rencontre != undefined); - this.rencontresExistantes = duplicate(this.actor.data.data.reve.rencontre.list); - this.displayPreviousRencontres(); - } - - /* -------------------------------------------- */ - updateSortReserve() { - this._removeTokens(t => t.sort != undefined); - this.sortReserves = duplicate(this.actor.data.data.reve.reserve.list); - this.displaySortReserve(); - } /* -------------------------------------------- */ async derober() { @@ -121,7 +209,7 @@ export class RdDTMRDialog extends Dialog { this._tellToGM(this.actor.name + " a refoulé : " + this.currentRencontre.name); await this.actor.deleteTMRRencontreAtPosition(); // Remove the stored rencontre if necessary await this.actor.ajouterRefoulement(this.currentRencontre.refoulement ?? 1); - this.updatePreviousRencontres(); + this.updateTokens(); console.log("-> refouler", this.currentRencontre) this.updateValuesDisplay(); this.nettoyerRencontre(); @@ -131,7 +219,7 @@ export class RdDTMRDialog extends Dialog { async ignorerRencontre() { this._tellToGM(this.actor.name + " a ignoré : " + this.currentRencontre.name); await this.actor.deleteTMRRencontreAtPosition(); // Remove the stored rencontre if necessary - this.updatePreviousRencontres(); + this.updateTokens(); this.updateValuesDisplay(); this.nettoyerRencontre(); } @@ -166,7 +254,7 @@ export class RdDTMRDialog extends Dialog { } async choisirCaseType(type) { - const locList = TMRUtility.getListCoordTMR(type); + const locList = TMRUtility.filterTMR(it => it.type == type).map(it => it.coord); this.colorierZoneRencontre(locList); } @@ -200,9 +288,9 @@ export class RdDTMRDialog extends Dialog { } /* -------------------------------------------- */ - async maitriser() { + async maitriserRencontre() { this.actor.deleteTMRRencontreAtPosition(); - this.updatePreviousRencontres(); + this.updateTokens(); let rencontreData = { actor: this.actor, @@ -218,6 +306,7 @@ export class RdDTMRDialog extends Dialog { await this._tentativeMaitrise(rencontreData); } + /* -------------------------------------------- */ async _tentativeMaitrise(rencontreData) { console.log("-> matriser", rencontreData); @@ -227,7 +316,6 @@ export class RdDTMRDialog extends Dialog { RollDataAjustements.calcul(rencontreData, this.actor); rencontreData.rolled = await RdDResolutionTable.roll(rencontreData.reve, RollDataAjustements.sum(rencontreData.ajustements)); - let postProcess = await TMRRencontres.gererRencontre(this, rencontreData); ChatMessage.create({ @@ -257,6 +345,7 @@ export class RdDTMRDialog extends Dialog { } } + /* -------------------------------------------- */ _deleteTmrMessages(actor, nbRounds = -1) { setTimeout(() => { if (nbRounds < 0) { @@ -307,60 +396,41 @@ export class RdDTMRDialog extends Dialog { if (rencontre) { return rencontre; } + // TODO: dialog pour remplacer la rencontre par un présent + //if (this.casesSpeciales.find(c => EffetsDraconiques.isPresentCite(c, tmr.coord))) { + + let myRoll = new Roll("1d7").evaluate().total; - if (TMRUtility.isForceRencontre() || myRoll== 7) { + if (TMRUtility.isForceRencontre() || myRoll == 7) { return await this.rencontreTMRRoll(tmr, this.actor.isRencontreSpeciale()); } this._tellToUser(myRoll + ": Pas de rencontre en " + tmr.label + " (" + tmr.coord + ")"); } - + /* -------------------------------------------- */ async rencontreTMRRoll(tmr, isMauvaise = false) { let rencontre = TMRUtility.utiliseForceRencontre() ?? - isMauvaise ? await TMRRencontres.getMauvaiseRencontre() - : await TMRRencontres.getRencontreAleatoire(tmr.type); + (isMauvaise + ? await TMRRencontres.getMauvaiseRencontre() + : await TMRRencontres.getRencontreAleatoire(tmr.type)); rencontre.coord = tmr.coord; rencontre.date = game.system.rdd.calendrier.getDateFromIndex(); rencontre.heure = game.system.rdd.calendrier.getCurrentHeure(); return rencontre; } - /* -------------------------------------------- */ - updateValuesDisplay() { - let ptsreve = document.getElementById("tmr-pointsreve-value"); - ptsreve.innerHTML = this.actor.data.data.reve.reve.value; - - let tmrpos = document.getElementById("tmr-pos"); - let tmr = TMRUtility.getTMR(this.actor.data.data.reve.tmrpos.coord); - tmrpos.innerHTML = this.actor.data.data.reve.tmrpos.coord + " (" + tmr.label + ")"; - - let etat = document.getElementById("tmr-etatgeneral-value"); - etat.innerHTML = this.actor.getEtatGeneral(); - - let refoulement = document.getElementById("tmr-refoulement-value"); - refoulement.innerHTML = this.actor.data.data.reve.refoulement.value; - - let fatigueItem = document.getElementById("tmr-fatigue-table"); - //console.log("Refresh : ", this.actor.data.data.sante.fatigue.value); - fatigueItem.innerHTML = "" + RdDUtility.makeHTMLfatigueMatrix(this.actor.data.data.sante.fatigue.value, this.actor.data.data.sante.endurance.max).html() + "
"; - } - /* -------------------------------------------- */ async manageCaseSpeciale(tmr) { - for (let caseTMR of this.casesSpeciales) { - if (caseTMR.data.coord == tmr.coord) { // Match ! - if (caseTMR.data.specific == 'trounoir') { - let newTMR = TMRUtility.getTMRAleatoire(); - let tmrPos = duplicate(this.actor.data.data.reve.tmrpos); - tmrPos.coord = newTMR.coord; - await this.actor.update({ "data.reve.tmrpos": tmrPos }); - ChatMessage.create({ - content: "Vous êtes rentré sur un Trou Noir : ré-insertion aléatoire.", - whisper: ChatMessage.getWhisperRecipients(game.user.name) - }); - } - } + if (this.casesSpeciales.find(c => EffetsDraconiques.isCaseTrouNoir(c, tmr.coord))) { + let newTMR = TMRUtility.getTMRAleatoire(); + let tmrPos = duplicate(this.actor.data.data.reve.tmrpos); + tmrPos.coord = newTMR.coord; + await this.actor.update({ "data.reve.tmrpos": tmrPos }); + ChatMessage.create({ + content: "Vous êtes rentré sur un Trou Noir : ré-insertion aléatoire.", + whisper: ChatMessage.getWhisperRecipients(game.user.name) + }); } } @@ -376,7 +446,8 @@ export class RdDTMRDialog extends Dialog { tmr: tmr, canClose: false, diffLibre: -7, - forceCarac: { 'reve-actuel': { label: "Rêve Actuel", value: this.actor.getReveActuel() } } + forceCarac: { 'reve-actuel': { label: "Rêve Actuel", value: this.actor.getReveActuel() } }, + maitrise: { verbe: 'maîtriser', action: 'Maîtriser le fleuve' } } rollData.competence.data.defaut_carac = 'reve-actuel'; @@ -384,80 +455,125 @@ export class RdDTMRDialog extends Dialog { } } + /* -------------------------------------------- */ + async _rollMaitriseCaseHumide(rollData) { + await this._maitriserTMR(rollData, r => this._resultatMaitriseCaseHumide(r)); + } + + async _resultatMaitriseCaseHumide(rollData) { + if (rollData.rolled.isETotal) { + rollData.souffle = await this.actor.ajouterSouffle({ chat: false }); + } + this.toclose = rollData.rolled.isEchec; + if (rollData.rolled.isSuccess && !rollData.previous && EffetsDraconiques.isDoubleResistanceFleuve(this.actor)) { + rollData.previous = { rolled: rollData.rolled, ajustements: rollData.ajustements }; + await this._rollMaitriseCaseHumide(rollData); + return; + } + rollData.poesie = Poetique.getExtrait(); + ChatMessage.create({ + whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name), + content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-resultat-maitrise-tmr.html`, rollData) + }); + if (rollData.rolled.isEchec) { + this.close(); + } + } + /* -------------------------------------------- */ isCaseHumide(tmr) { + if (!(TMRUtility.isCaseHumide(tmr) || this.isCaseHumideAdditionelle(tmr))) { + return undefined; + } if (this.isCaseMaitrisee(tmr.coord)) { ChatMessage.create({ content: tmr.label + ": cette case humide est déja maitrisée grâce à votre Tête Quête des Eaux", whisper: ChatMessage.getWhisperRecipients(game.user.name) }); - return false; + return undefined; } - if (this.actor.isCaseHumideAdditionelle(tmr)) { - return true; - } - return tmr.type == "lac" || tmr.type == "fleuve" || tmr.type == "marais"; + return -7; } /* -------------------------------------------- */ - isCaseMaitrisee(coordTMR) { - return this.casesSpeciales.find(it => it.data.coord = coordTMR && it.data.specific == 'maitrisee'); + isCaseHumideAdditionelle(tmr) { + if (tmr.type == 'pont' && EffetsDraconiques.isPontImpraticable(this.actor)) { + ChatMessage.create({ + content: tmr.label + ": Vous êtes sous le coup d'une Impraticabilité des Ponts : ce pont doit être maîtrisé comme une case humide.", + whisper: ChatMessage.getWhisperRecipients(game.user.name) + }); + return true; + } + if (this.isCaseInondee(tmr.coord)) { + ChatMessage.create({ + content: tmr.label + ": cette case est inondée, elle doit être maîtrisée comme une case humide.", + whisper: ChatMessage.getWhisperRecipients(game.user.name) + }); + return true; + } + return false; } - async _rollMaitriseCaseHumide(rollData) { - this.minimize(); // Hide - const dialog = await RdDRoll.create(this.actor, rollData, - { - html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-tmr-humide.html', - options:{ height: 350 }, - close: html => { this.maximize(); } // Re-display TMR - }, - { - name: 'maitrise', - label: 'Maîtriser le fleuve', - callbacks: [ - this.actor.createCallbackExperience(), - { action: r => this._maitriseCaseHumide(r) } - ] + /* -------------------------------------------- */ + async conquerirCiteFermee(tmr) { + if (this.viewOnly || this.currentRencontre) { + return; + } + const citeFermee = this.isCiteFermee(tmr.coord); + if (citeFermee) { + let rollData = { + actor: this.actor, + competence: duplicate(this.actor.getBestDraconic()), + tmr: tmr, + canClose: false, + diffLibre: -9, + forceCarac: { 'reve-actuel': { label: "Rêve Actuel", value: this.actor.getReveActuel() } }, + maitrise: { verbe: 'conquérir', action: 'Conquérir la cité' } } - ); - dialog.render(true); - } + rollData.competence.data.defaut_carac = 'reve-actuel'; - async _maitriseCaseHumide(rollData) { + await this._maitriserTMR(rollData, r => this._resultatConqueteCiteFermee(r)); + } + } + async _resultatConqueteCiteFermee(rollData) { if (rollData.rolled.isETotal) { rollData.souffle = await this.actor.ajouterSouffle({ chat: false }); } this.toclose = rollData.rolled.isEchec; - if (rollData.rolled.isSuccess) { - if (!rollData.previous && this.actor.isDoubleResistanceFleuve()) { - ChatMessage.create({ - content: `Double résistance du fleuve: `, - whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name) - }); - rollData.previous = [rollData.rolled]; - await this._rollMaitriseCaseHumide(rollData); - return; - } - } + rollData.poesie = Poetique.getExtrait(); ChatMessage.create({ whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name), - content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-fleuve-tmr.html`, rollData) + content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-resultat-maitrise-tmr.html`, rollData) }); if (rollData.rolled.isEchec) { this.close(); } - + else { + const citeFermee = this.actor.data.items.find(it => EffetsDraconiques.isCiteFermee(it, rollData.tmr.coord)); + this.actor.deleteOwnedItem(citeFermee._id); + this._removeTokens(t => t.coordTMR() == citeFermee.data.coord && t.caseSpeciale?._id == citeFermee._id); + } } /* -------------------------------------------- */ - isReserveExtensible(coordTMR) { - for (let caseTMR of this.casesSpeciales) { - if (caseTMR.data.specific == 'reserve_extensible' && caseTMR.data.coord == coordTMR) - return true; - } - return false; + async _maitriserTMR(rollData, callbackMaitrise) { + this.minimize(); // Hide + const dialog = await RdDRoll.create(this.actor, rollData, + { + html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-maitrise-tmr.html', + options: { height: 350 }, + close: html => { this.maximize(); } // Re-display TMR + }, + { + name: rollData.maitrise.verbe, label: rollData.maitrise.action, + callbacks: [ + this.actor.createCallbackExperience(), + { action: callbackMaitrise } + ] + } + ); + dialog.render(true); } /* -------------------------------------------- */ @@ -466,9 +582,9 @@ export class RdDTMRDialog extends Dialog { return; } - let sortReserveList = TMRUtility.getSortReserveList(this.sortReserves, coordTMR); + let sortReserveList = TMRUtility.getSortReserveList(this.sortsReserves, coordTMR); if (sortReserveList.length > 0) { - if (this.actor.isReserveEnSecurite() || this.isReserveExtensible(coordTMR)) { + if (EffetsDraconiques.isReserveEnSecurite(this.actor) || this.isReserveExtensible(coordTMR)) { let msg = "Vous êtes sur une case avec un Sort en Réserve. Grâce à votre Tête Reserve en Sécurité ou Réserve Exensible, vous pouvez contrôler le déclenchement. Cliquez si vous souhaitez le déclencher :