/** * Extend the base Dialog entity by defining a custom window to perform spell. * @extends {Dialog} */ import { RdDUtility } from "./rdd-utility.js"; import { TMRUtility } from "./tmr-utility.js"; import { RdDRollTables } from "./rdd-rolltables.js"; import { RdDResolutionTable } from "./rdd-resolution-table.js"; const tmrConstants = { col1_y: 30, col2_y: 55, cellw: 55, cellh: 55, gridx: 28, gridy: 28 } export class RdDTMRDialog extends Dialog { /* -------------------------------------------- */ constructor(html, actor, tmrData, viewOnly) { const dialogConf = { title: "Terres Médianes de Rêve", content: html, buttons: { closeButton: { label: "Fermer", callback: html => this.close(html) } }, default: "closeButton" } const dialogOptions = { classes: ["tmrdialog"], width: 920, height: 980, 'z-index': 20 } super(dialogConf, dialogOptions); this.tmrdata = duplicate(tmrData); this.actor = actor; this.viewOnly = viewOnly this.nbFatigue = this.viewOnly ? 0 : 1; // 1 premier point de fatigue du à la montée this.rencontresExistantes = duplicate(this.actor.data.data.reve.rencontre.list); this.sortReserves = duplicate(this.actor.data.data.reve.reserve.list); this.allTokens = [] this.pixiApp = new PIXI.Application({ width: 720, height: 860 }); } /* -------------------------------------------- */ close() { this.actor.santeIncDec("fatigue", this.nbFatigue).then(super.close()); // moving 1 cell costs 1 fatigue } /* -------------------------------------------- */ displaySortReserve() { console.debug("displaySortReserve", this.sortReserves); for (let sort of this.sortReserves) { this._trackToken(this._tokenSortEnReserve(sort)); } } /* -------------------------------------------- */ 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() { await this.actor.addTMRRencontre(this.currentRencontre); console.log("-> derober", this.currentRencontre); this._tellToGM(this.actor.name + " s'est dérobé et quitte les TMR."); this.close(); } /* -------------------------------------------- */ async refouler(data) { this._tellToGM(this.actor.name + " a refoulé une rencontre."); await this.actor.deleteTMRRencontreAtPosition(); // Remove the stored rencontre if necessary let result = await this.actor.ajouterRefoulement(1); this.updatePreviousRencontres(); console.log("-> refouler", this.currentRencontre) this.updateValuesDisplay(); } /* -------------------------------------------- */ async maitriser(data) { this.actor.deleteTMRRencontreAtPosition(); // Remove the stored rencontre if necessary this.updatePreviousRencontres(); const draconic = this.actor.getBestDraconic(); const carac = this.actor.getReveActuel(); // TODO: ajouter l'état général? const etatGeneral = this.actor.data.data.compteurs.etat.value const difficulte = draconic.data.niveau - this.currentRencontre.force; console.log("Maitriser", carac, draconic.data.niveau, this.currentRencontre.force); let rolled = RdDResolutionTable.roll(carac, difficulte); let message = "
Test : Rêve actuel / " + draconic.name + " / " + this.currentRencontre.name + "" + "
" + "
Jet : " + carac + " / " + difficulte + " -> " + rolled.score + "%
Résutat : " + rolled.roll; if (rolled.isEchec) { TMRUtility.processRencontreEchec(this.actor, this.currentRencontre); this._tellToUser("Vous avez échoué à maîtriser un " + this.currentRencontre.name + " de force " + this.currentRencontre.force + "
Vous quittez brutalement les Terres Médianes !" + message);; this.close(); } else { TMRUtility.processRencontreReussite(this.actor, this.currentRencontre); this._tellToUser("Vous avez réussi à maîtriser un " + this.currentRencontre.name + " de force " + this.currentRencontre.force + message); } console.log("-> matriser", this.currentRencontre); this.updateValuesDisplay(); } _tellToUser(message) { ChatMessage.create({ title: "TMR", content: message, user: game.user._id, whisper: ChatMessage.getWhisperRecipients("GM") }); } /* -------------------------------------------- */ async manageRencontre(coordTMR, cellDescr) { if (this.viewOnly) { return; } this.currentRencontre = undefined; let rencontre = this.rencontresExistantes.find(prev => prev.coord == coordTMR); let deRencontre = new Roll("d7").roll(); console.log("manageRencontre", deRencontre, rencontre); if (rencontre == undefined) { if (deRencontre.total == 7) { rencontre = TMRUtility.rencontreTMRRoll(coordTMR, cellDescr); } } if (rencontre) { // Manages it console.log("manageRencontre", rencontre) this.currentRencontre = duplicate(rencontre); let dialog = new Dialog({ title: "Rencontre en TMR!", content: "Vous recontrez un " + rencontre.name + " de force " + rencontre.force + "
", buttons: { derober: { icon: '', label: "Se dérober", callback: () => this.derober() }, refouler: { icon: '', label: "Refouler", callback: () => this.refouler() }, maitiser: { icon: '', label: "Maîtriser", callback: () => this.maitriser() } }, default: "derober" }); dialog.render(true); } } /* -------------------------------------------- */ performRoll(html) { if (this.viewOnly) { return; } this.actor.performRoll(this.rollData); } /* -------------------------------------------- */ updateValuesDisplay() { let ptsreve = document.getElementById("pointsreve-value"); ptsreve.innerHTML = this.actor.data.data.reve.reve.value; let tmrpos = document.getElementById("tmr-pos"); let tmr = TMRUtility.getTMRDescription(this.actor.data.data.reve.tmrpos.coord); console.log("updateValuesDisplay", this.actor.data.data.reve.tmrpos, tmr); tmrpos.innerHTML = this.actor.data.data.reve.tmrpos.coord + " (" + tmr.label + ")"; let etat = document.getElementById("etatgeneral-value"); etat.innerHTML = this.actor.data.data.compteurs.etat.value; let refoulement = document.getElementById("refoulement-value"); refoulement.innerHTML = this.actor.data.data.reve.refoulement.value; let fatigueItem = document.getElementById("fatigue-table"); fatigueItem.innerHTML = "" + RdDUtility.makeHTMLfatigueMatrix(this.actor.data.data.sante.fatigue.value, this.actor.data.data.sante.endurance.max).html() + "
"; } /* -------------------------------------------- */ manageCaseHumideResult() { if (this.toclose) this.close(); } /* -------------------------------------------- */ async manageCaseHumide(cellDescr) { if (this.viewOnly) { return; } if (cellDescr.type == "lac" || cellDescr.type == "fleuve" || cellDescr.type == "marais") { let draconic = this.actor.getBestDraconic(); let carac = this.actor.getReveActuel(); // TODO: ajouter l'état général? const etatGeneral = this.actor.data.data.compteurs.etat.value let difficulte = draconic.data.niveau - 7; let rolled = RdDResolutionTable.roll(carac, difficulte); console.log("manageCaseHumide >>", rolled); let explication = ""; this.toclose = rolled.isEchec; if (rolled.isEchec) { explication += "Vous êtes entré sur une case humide, et vous avez raté votre maîtrise ! Vous quittez les Terres Médianes !" } else { explication += "Vous êtes entré sur une case humide, et vous avez réussi votre maîtrise !" } explication += "
Test : Rêve actuel / " + draconic.name + " / " + cellDescr.type + "" + "
Jet : " + carac + " / " + difficulte + " -> " + rolled.score + "%
Résutat : " + rolled.roll; if (rolled.isETotal) { let souffle = RdDRollTables.getSouffle(); explication += "
Vous avez fait un Echec Total. Vous subissez un Souffle de Dragon : " + souffle.name; this.actor.createOwnedItem(souffle); } if (rolled.isPart) { explication += "
Vous avez fait une Réussite Particulière"; explication += RdDResolutionTable.buildXpMessage(rolled, difficulte) } let humideDiag = new Dialog({ title: "Case humide", content: explication, buttons: { choice: { icon: '', label: "Fermer", callback: () => this.manageCaseHumideResult() } } } ); humideDiag.render(true); } } /* -------------------------------------------- */ async declencheSortEnReserve(coordTMR) { if (this.viewOnly) { return; } let sortReserve = this.sortReserves.find(it => it.coord == coordTMR) if (sortReserve != undefined) { await this.actor.deleteSortReserve(sortReserve.coord); this.updateSortReserve(); console.log("declencheSortEnReserve", sortReserve) const declenchementSort = "Vous avez déclenché le sort " + sortReserve.sort.name + " en réserve en " + sortReserve.coord + " (" + TMRUtility.getTMRDescription(sortReserve.coord).label + ") avec " + sortReserve.sort.ptreve_reel + " points de Rêve"; this._tellToUser(declenchementSort); this.close(); } } /* -------------------------------------------- */ async deplacerDemiReve(event) { if (this.viewOnly) { return; } let origEvent = event.data.originalEvent; let myself = event.target.tmrObject; let eventCoord = RdDTMRDialog._computeEventCoord(origEvent); let cellx = eventCoord.cellx; let celly = eventCoord.celly; console.log("deplacerDemiReve >>>>", cellx, celly); let currentPos = TMRUtility.convertToCellCoord(myself.actor.data.data.reve.tmrpos.coord); if (RdDTMRDialog._horsDePortee(currentPos, cellx, celly)) { ui.notifications.error("Vous ne pouvez vous déplacer que sur des cases adjacentes à votre position"); } else { let coordTMR = TMRUtility.convertToTMRCoord(cellx, celly); let cellDescr = TMRUtility.getTMRDescription(coordTMR); console.log("deplacerDemiReve: TMR column is", coordTMR, cellx, celly, cellDescr, this); let tmrPos = duplicate(myself.actor.data.data.reve.tmrpos); tmrPos.coord = coordTMR; await myself.actor.update({ "data.reve.tmrpos": tmrPos }); myself._updateDemiReve(myself); myself.nbFatigue += 1; myself.updateValuesDisplay(); myself.manageRencontre(coordTMR, cellDescr); myself.manageCaseHumide(cellDescr); await myself.declencheSortEnReserve(coordTMR); } } /* -------------------------------------------- */ async activateListeners(html) { super.activateListeners(html); var row = document.getElementById("tmrrow1"); var cell1 = row.insertCell(1); cell1.append(this.pixiApp.view); if (this.viewOnly) { html.find('#lancer-sort').remove(); } else { // Roll Sort html.find('#lancer-sort').click((event) => { this.actor.rollUnSort(this.actor.data.data.reve.tmrpos.coord); }); } // load the texture we need await this.pixiApp.loader .add('tmr', 'systems/foundryvtt-reve-de-dragon/styles/ui/tmp_main_r1.webp') .add('demi-reve', "icons/svg/sun.svg") .load((loader, resources) => { // This creates a texture from a TMR image const mytmr = new PIXI.Sprite(resources.tmr.texture); // Setup the position of the TMR mytmr.x = 0; mytmr.y = 0; mytmr.width = 720; mytmr.height = 860; // Rotate around the center mytmr.anchor.x = 0; mytmr.anchor.y = 0; mytmr.interactive = true; mytmr.buttonMode = true; mytmr.tmrObject = this; if (!this.viewOnly) { mytmr.on('pointerdown', this.deplacerDemiReve); } this.pixiApp.stage.addChild(mytmr); this._addDemiReve(); this.displayPreviousRencontres(); this.displaySortReserve(); }); if (this.viewOnly) { return; } await this.actor.updatePointsDeReve((this.tmrdata.isRapide) ? -2 : -1); // 1 point defatigue this.updateValuesDisplay(); let cellDescr = TMRUtility.getTMRDescription(this.actor.data.data.reve.tmrpos.coord); this.manageRencontre(this.actor.data.data.reve.tmrpos.coord, cellDescr); this.manageCaseHumide(cellDescr); } /* -------------------------------------------- */ static _computeEventCoord(origEvent) { let canvasRect = origEvent.target.getBoundingClientRect(); let x = origEvent.clientX - canvasRect.left; let y = origEvent.clientY - canvasRect.top; let cellx = Math.floor(x / tmrConstants.cellw); // [From 0 -> 12] y -= (cellx % 2 == 0) ? tmrConstants.col1_y : tmrConstants.col2_y; let celly = Math.floor(y / tmrConstants.cellh); // [From 0 -> 14] return { cellx, celly }; } static _horsDePortee(pos, cellx, celly) { return Math.abs(cellx - pos.x) > 1 || Math.abs(celly - pos.y) > 1 || (pos.y == 0 && celly > pos.y && cellx != pos.x && pos.x % 2 == 0) || (celly == 0 && celly < pos.y && cellx != pos.x && pos.x % 2 == 1); } _tokenRencontre(rencontre) { let sprite = new PIXI.Graphics(); sprite.beginFill(0x767610, 0.6); sprite.drawCircle(0, 0, 6); sprite.endFill(); sprite.decallage = { x: (tmrConstants.cellw / 2) - 16, y: 16 - (tmrConstants.cellh / 2) }; return { sprite: sprite, rencontre: rencontre, coordTMR: () => rencontre.coord }; } _tokenSortEnReserve(sort) { let sprite = new PIXI.Graphics(); sprite.beginFill(0x101010, 0.8); sprite.drawCircle(0, 0, 6); sprite.endFill(); sprite.decallage = { x: 16 - (tmrConstants.cellw / 2), y: 16 - (tmrConstants.cellh / 2) } return { sprite: sprite, sort: sort, coordTMR: () => sort.coord } } _tokenDemiReve() { let texture = PIXI.utils.TextureCache['demi-reve']; let sprite = new PIXI.Sprite(texture); sprite.width = tmrConstants.cellw * 0.7; sprite.height = tmrConstants.cellh * 0.7; sprite.anchor.set(0.5); sprite.tint = 0x00FFEE; return { sprite: sprite, actor: this.actor, coordTMR: () => this.actor.data.data.reve.tmrpos.coord } } /* -------------------------------------------- */ _addDemiReve() { this.demiReve = this._tokenDemiReve(); this._setTokenPosition(this.demiReve); this.pixiApp.stage.addChild(this.demiReve.sprite); } _updateDemiReve(myself) { myself._setTokenPosition(myself.demiReve); } _setTokenPosition(token) { let coordXY = TMRUtility.convertToCellCoord(token.coordTMR()); let decallagePairImpair = (coordXY.x % 2 == 0) ? tmrConstants.col1_y : tmrConstants.col2_y; let dx = (token.sprite.decallage == undefined) ? 0 : token.sprite.decallage.x; let dy = (token.sprite.decallage == undefined) ? 0 : token.sprite.decallage.y; token.sprite.x = tmrConstants.gridx + (coordXY.x * tmrConstants.cellw) + dx; token.sprite.y = tmrConstants.gridy + (coordXY.y * tmrConstants.cellh) + dy + decallagePairImpair; } _removeTokens(filter) { const tokensToRemove = this.allTokens.filter(filter); for (let token of tokensToRemove) { this.pixiApp.stage.removeChild(token.sprite); } } _trackToken(token) { this.allTokens.push(token) this._setTokenPosition(token); this.pixiApp.stage.addChild(token.sprite); } }