diff --git a/module/actor.js b/module/actor.js index ed4258c5..6e993730 100644 --- a/module/actor.js +++ b/module/actor.js @@ -1121,6 +1121,24 @@ export class RdDActor extends Actor { return queue; } + /* -------------------------------------------- */ + async reinsertionAleatoire(raison) { + ChatMessage.create({ + content: `${raison} : ré-insertion aléatoire.`, + whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name) + }); + const innaccessible = this.buildTMRInnaccessible(); + let tmr = TMRUtility.getTMRAleatoire(tmr => !innaccessible.includes(tmr.coord) ); + this.updateCoordTMR(tmr.coord); + return tmr; + } + + buildTMRInnaccessible() { + const tmrInnaccessibles = this.data.items.filter(it => Draconique.isCaseTMR(it) && + EffetsDraconiques.isInnaccessible(it)); + return tmrInnaccessibles.map(it => it.data.coord); + } + /* -------------------------------------------- */ displayTMRQueueSouffleInformation() { let messages = []; @@ -1137,9 +1155,6 @@ export class RdDActor extends Actor { 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) { @@ -1796,6 +1811,10 @@ export class RdDActor extends Actor { ui.notifications.info("Aucun sort disponible pour cette case !"); return; } + if (EffetsDraconiques.isConquete(this)) { + ui.notifications.error("Vous ne pouvez pas lancer de sort sous l'effet d'une conquête!"); + return; + } if (this.currentTMR) this.currentTMR.minimize(); // Hide let draconicList = this.filterDraconicList(sortList); @@ -2708,25 +2727,23 @@ export class RdDActor extends Actor { /* -------------------------------------------- */ async resetItemUse( ) { - await this.setFlag('foundryvtt-reve-de-dragon', 'itemUse', null ); + await this.unsetFlag('foundryvtt-reve-de-dragon', 'itemUse'); await this.setFlag('foundryvtt-reve-de-dragon', 'itemUse', {} ); } /* -------------------------------------------- */ async incItemUse( itemId ) { - let itemUse = this.getFlag('foundryvtt-reve-de-dragon', 'itemUse'); - itemUse = (itemUse) ? duplicate(itemUse) : {}; - itemUse[itemId] = (itemUse[itemId]) ? itemUse[itemId] + 1 : 1; + let itemUse = duplicate(this.getFlag('foundryvtt-reve-de-dragon', 'itemUse') ?? {}); + itemUse[itemId] = (itemUse[itemId] ?? 0) + 1; await this.setFlag( 'foundryvtt-reve-de-dragon', 'itemUse', itemUse); console.log("ITEM USE INC", itemUse); } /* -------------------------------------------- */ getItemUse( itemId ) { - let itemUse = this.getFlag('foundryvtt-reve-de-dragon', 'itemUse'); - itemUse = (itemUse) ? itemUse : {}; + let itemUse = this.getFlag('foundryvtt-reve-de-dragon', 'itemUse') ?? {}; console.log("ITEM USE GET", itemUse); - return itemUse[itemId] ? itemUse[itemId] : 0; + return itemUse[itemId] ?? 0; } /* -------------------------------------------- */ diff --git a/module/misc.js b/module/misc.js index 50566a09..f1220c35 100644 --- a/module/misc.js +++ b/module/misc.js @@ -59,4 +59,12 @@ export class Misc { } } + static rollOneOf(array) { + return array[new Roll("1d" + array.length).evaluate().total - 1]; + } + + static distinct(array) { + return [...new Set(array)]; + } + } \ No newline at end of file diff --git a/module/poetique.js b/module/poetique.js index 67f0d387..58127bf8 100644 --- a/module/poetique.js +++ b/module/poetique.js @@ -1,3 +1,4 @@ +import { Misc } from "./misc.js" const poesieHautReve = [ { @@ -64,7 +65,7 @@ const poesieHautReve = [ export class Poetique { static getExtrait(){ - return poesieHautReve[new Roll("1d" + poesieHautReve.length).evaluate().total - 1] + return Misc.rollOneOf(poesieHautReve); } } \ No newline at end of file diff --git a/module/rdd-namegen.js b/module/rdd-namegen.js index dd59097f..a5e6d05c 100644 --- a/module/rdd-namegen.js +++ b/module/rdd-namegen.js @@ -1,3 +1,4 @@ +import { Grammar } from "./grammar.js"; import { Misc } from "./misc.js"; const words = [ 'pore', 'pre', 'flor', 'lane', 'turlu', 'pin', 'a', 'alph', 'i', 'onse', 'iane', 'ane', 'zach', 'arri', 'ba', 'bo', 'bi', @@ -7,9 +8,7 @@ const words = [ 'pore', 'pre', 'flor', 'lane', 'turlu', 'pin', 'a', 'alph', 'i', export class RdDNameGen { static getName( msg, params ) { - let max = words.length; - let name = words[new Roll("1d"+max+" -1").roll().total]; - name += words[new Roll("1d"+max+" -1").roll().total]; + let name = Misc.upperFirst( Misc.rollOneOf(words) + Misc.rollOneOf(words) ) //console.log(name); ChatMessage.create( { content: `Nom : ${name}`, whisper: ChatMessage.getWhisperRecipients("GM") } ); } diff --git a/module/rdd-tmr-dialog.js b/module/rdd-tmr-dialog.js index db874e8e..823e8679 100644 --- a/module/rdd-tmr-dialog.js +++ b/module/rdd-tmr-dialog.js @@ -10,6 +10,7 @@ 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"; +import { Grammar } from "./grammar.js"; /* -------------------------------------------- */ export class RdDTMRDialog extends Dialog { @@ -50,7 +51,7 @@ export class RdDTMRDialog extends Dialog { this.cumulFatigue = 0; this.loadRencontres(); this.loadSortsReserve(); - this.casesSpeciales = this.actor.data.items.filter(item => Draconique.isCaseTMR(item)); + this.loadCasesSpeciales(); this.allTokens = []; this.rencontreState = 'aucune'; this.pixiApp = new PIXI.Application({ width: 720, height: 860 }); @@ -66,6 +67,10 @@ export class RdDTMRDialog extends Dialog { this.pixiTMR.load((loader, resources) => this.createPixiSprites()); } + loadCasesSpeciales() { + this.casesSpeciales = this.actor.data.items.filter(item => Draconique.isCaseTMR(item)); + } + loadSortsReserve() { this.sortsReserves = duplicate(this.actor.data.data.reve.reserve.list); } @@ -99,6 +104,7 @@ export class RdDTMRDialog extends Dialog { this._removeTokens(t => true); this.loadRencontres(); this.loadSortsReserve(); + this.loadCasesSpeciales(); this._createTokens(); } @@ -396,10 +402,13 @@ 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))) { - - + if (this.casesSpeciales.find(c => EffetsDraconiques.isPresentCite(c, tmr.coord))) { + + // TODO: dialog pour remplacer la rencontre par un présent + + } + + let myRoll = new Roll("1d7").evaluate().total; if (TMRUtility.isForceRencontre() || myRoll == 7) { return await this.rencontreTMRRoll(tmr, this.actor.isRencontreSpeciale()); @@ -415,30 +424,22 @@ export class RdDTMRDialog extends Dialog { ? await TMRRencontres.getMauvaiseRencontre() : await TMRRencontres.getRencontreAleatoire(tmr.type)); rencontre.coord = tmr.coord; - rencontre.date = game.system.rdd.calendrier.getDateFromIndex(); + rencontre.date = game.system.rdd.calendrier.getDateFromIndex(); rencontre.heure = game.system.rdd.calendrier.getCurrentHeure(); return rencontre; } /* -------------------------------------------- */ - async manageCaseSpeciale(tmr) { - 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) - }); + async manageTmrInnaccessible(tmr) { + const caseTmrInnaccessible = this.casesSpeciales.find(c => EffetsDraconiques.isInnaccessible(c, tmr.coord)); + if (caseTmrInnaccessible) { + return await this.actor.reinsertionAleatoire(caseTmrInnaccessible.name); } + return tmr; } /* -------------------------------------------- */ async manageCaseHumide(tmr) { - if (this.viewOnly || this.currentRencontre) { - return; - } if (this.isCaseHumide(tmr)) { let rollData = { actor: this.actor, @@ -449,8 +450,8 @@ export class RdDTMRDialog extends Dialog { forceCarac: { 'reve-actuel': { label: "Rêve Actuel", value: this.actor.getReveActuel() } }, maitrise: { verbe: 'maîtriser', action: 'Maîtriser le fleuve' } } + rollData.double = EffetsDraconiques.isDoubleResistanceFleuve(this.actor) ? true: undefined, rollData.competence.data.defaut_carac = 'reve-actuel'; - await this._rollMaitriseCaseHumide(rollData); } } @@ -465,8 +466,9 @@ export class RdDTMRDialog extends Dialog { rollData.souffle = await this.actor.ajouterSouffle({ chat: false }); } this.toclose = rollData.rolled.isEchec; - if (rollData.rolled.isSuccess && !rollData.previous && EffetsDraconiques.isDoubleResistanceFleuve(this.actor)) { + if (rollData.rolled.isSuccess && rollData.double) { rollData.previous = { rolled: rollData.rolled, ajustements: rollData.ajustements }; + rollData.double = undefined; await this._rollMaitriseCaseHumide(rollData); return; } @@ -513,29 +515,55 @@ export class RdDTMRDialog extends Dialog { } return false; } - + /* -------------------------------------------- */ 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é' } - } - rollData.competence.data.defaut_carac = 'reve-actuel'; - - await this._maitriserTMR(rollData, r => this._resultatConqueteCiteFermee(r)); + if (EffetsDraconiques.fermetureCites.find(this.casesSpeciales, tmr.coord)) { + await this._conquerir(tmr, { + difficulte: -9, + action: 'Conquérir la cité', + onConqueteReussie: r => EffetsDraconiques.fermetureCites.onConquete(r.actor, tmr.coord), + onConqueteEchec: r => this.close(), + canClose: false + }); } } - async _resultatConqueteCiteFermee(rollData) { + + removeToken(tmr, casetmr) { + this._removeTokens(t => t.coordTMR() == tmr.coord && t.caseSpeciale?._id == casetmr._id); + this.updateTokens() + } + + /* -------------------------------------------- */ + async conquerirTMR(tmr) { + if (EffetsDraconiques.conquete.find(this.casesSpeciales, tmr.coord)) { + await this._conquerir(tmr, { + difficulte: -7, + action: 'Conquérir', + onConqueteReussie: r => EffetsDraconiques.conquete.onConquete(r.actor, tmr.coord, (casetmr) => this.removeToken(tmr, casetmr)), + onConqueteEchec: r => {}, + canClose: false + }); + } + } + + /* -------------------------------------------- */ + async _conquerir(tmr, options) { + let rollData = { + actor: this.actor, + competence: duplicate(this.actor.getBestDraconic()), + tmr: tmr, + canClose: options.canClose ?? false, + diffLibre: options.difficulte ?? -7, + forceCarac: { 'reve-actuel': { label: "Rêve Actuel", value: this.actor.getReveActuel() } }, + maitrise: { verbe: 'conquérir', action: options.action } + }; + rollData.competence.data.defaut_carac = 'reve-actuel'; + + await this._maitriserTMR(rollData, r => this._onResultatConquerir(r, options)); + } + + async _onResultatConquerir(rollData, options) { if (rollData.rolled.isETotal) { rollData.souffle = await this.actor.ajouterSouffle({ chat: false }); } @@ -547,12 +575,11 @@ export class RdDTMRDialog extends Dialog { content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-resultat-maitrise-tmr.html`, rollData) }); if (rollData.rolled.isEchec) { - this.close(); + options.onConqueteEchec(rollData, options.effetDraconique); } 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); + await options.onConqueteReussie(rollData, options.effetDraconique); + this.updateTokens(); } } @@ -577,17 +604,18 @@ export class RdDTMRDialog extends Dialog { } /* -------------------------------------------- */ - async declencheSortEnReserve(coordTMR) { - if (this.viewOnly) { - return; - } - - let sortReserveList = TMRUtility.getSortReserveList(this.sortsReserves, coordTMR); + async declencheSortEnReserve(coord) { + + let sortReserveList = TMRUtility.getSortReserveList(this.sortsReserves, coord); if (sortReserveList.length > 0) { - if (EffetsDraconiques.isReserveEnSecurite(this.actor) || this.isReserveExtensible(coordTMR)) { + if (EffetsDraconiques.isConquete(this.actor)) { + ui.notifications.error("Vous ne pouvez pas déclencher de sort sous l'effet d'une conquête!"); + return; + } + if (EffetsDraconiques.isReserveEnSecurite(this.actor) || this.isReserveExtensible(coord)) { 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 :