forked from public/foundryvtt-reve-de-dragon
		
	Les migrations peuvent avoir besoin du temps courant. Le calendrier est recréé après.
		
			
				
	
	
		
			476 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			476 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| import { RdDCalendrierEditeur } from "./rdd-calendrier-editeur.js";
 | |
| import { RdDResolutionTable } from "./rdd-resolution-table.js";
 | |
| import { RdDUtility } from "./rdd-utility.js";
 | |
| import { RdDDice } from "./rdd-dice.js";
 | |
| import { Misc } from "./misc.js";
 | |
| import { HIDE_DICE, SHOW_DICE, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js";
 | |
| import { DialogChronologie } from "./dialog-chronologie.js";
 | |
| import { MAX_NOMBRE_ASTRAL, RdDTimestamp, WORLD_TIMESTAMP_SETTING } from "./rdd-timestamp.js";
 | |
| import { DialogChateauDormant } from "./sommeil/dialog-chateau-dormant.js";
 | |
| import { ReglesOptionelles } from "./settings/regles-optionelles.js";
 | |
| import { APP_ASTROLOGIE_REFRESH, AppAstrologie } from "./sommeil/app-astrologie.js";
 | |
| 
 | |
| /* -------------------------------------------- */
 | |
| export class RdDCalendrier extends Application {
 | |
| 
 | |
|   static get defaultOptions() {
 | |
|     return mergeObject(super.defaultOptions, {
 | |
|       template: "systems/foundryvtt-reve-de-dragon/templates/calendar-template.html",
 | |
|       popOut: false,
 | |
|       resizable: false
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   static createCalendrierPos() {
 | |
|     return { top: 200, left: 200 };
 | |
|   }
 | |
| 
 | |
|   constructor() {
 | |
|     super();
 | |
|     // position
 | |
|     this.calendrierPos = duplicate(game.settings.get(SYSTEM_RDD, "calendrier-pos"));
 | |
|     if (this.calendrierPos == undefined || this.calendrierPos.top == undefined) {
 | |
|       this.calendrierPos = RdDCalendrier.createCalendrierPos();
 | |
|       game.settings.set(SYSTEM_RDD, "calendrier-pos", this.calendrierPos);
 | |
|     }
 | |
|     // Calendrier
 | |
|     this.timestamp = RdDTimestamp.getWorldTime();
 | |
| 
 | |
|     if (Misc.isUniqueConnectedGM()) { // Uniquement si GM
 | |
|       RdDTimestamp.setWorldTime(this.timestamp);
 | |
|       this.nombresAstraux = this.getNombresAstraux();
 | |
|       this.rebuildNombresAstraux(HIDE_DICE); // Ensure always up-to-date
 | |
|     }
 | |
|     console.log('RdDCalendrier.constructor()', this.timestamp, this.timestamp.toCalendrier(), this.calendrierPos, this.nombresAstraux);
 | |
|     Hooks.on('updateSetting', async (setting, update, options, id) => this.onUpdateSetting(setting, update, options, id));
 | |
|   }
 | |
| 
 | |
|   display() {
 | |
|     let templatePath = "systems/foundryvtt-reve-de-dragon/templates/calendar-template.html";
 | |
|     renderTemplate(templatePath, {}).then(html => {
 | |
|       this.render(true);
 | |
|     });
 | |
|     return this;
 | |
|   }
 | |
| 
 | |
|   async onUpdateSetting(setting, update, options, id) {
 | |
|     if (setting.key == SYSTEM_RDD + '.' + WORLD_TIMESTAMP_SETTING) {
 | |
|       this.timestamp = RdDTimestamp.getWorldTime();
 | |
|       this.updateDisplay();
 | |
|       Hooks.callAll(APP_ASTROLOGIE_REFRESH);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   /** @override */
 | |
|   async activateListeners(html) {
 | |
|     super.activateListeners(html);
 | |
|     this.html = html;
 | |
| 
 | |
|     this.updateDisplay();
 | |
| 
 | |
|     this.html.find('.ajout-chronologie').click(ev => DialogChronologie.create());
 | |
| 
 | |
|     this.html.find('.calendar-btn').click(ev => this.onCalendarButton(ev));
 | |
| 
 | |
|     this.html.find('.calendar-set-datetime').click(ev => {
 | |
|       ev.preventDefault();
 | |
|       this.showCalendarEditor();
 | |
|     });
 | |
| 
 | |
|     this.html.find('.calendar-astrologie').click(ev => {
 | |
|       ev.preventDefault();
 | |
|       this.showAstrologieEditor();
 | |
|     });
 | |
| 
 | |
|     this.html.find('.calendar-title').mousedown(ev => {
 | |
|       ev.preventDefault();
 | |
|       ev = ev || window.event;
 | |
|       let isRightMB = false;
 | |
|       if ("which" in ev) { // Gecko (Firefox), WebKit (Safari/Chrome) & Opera
 | |
|         isRightMB = ev.which == 3;
 | |
|       } else if ("button" in ev) { // IE, Opera 
 | |
|         isRightMB = ev.button == 2;
 | |
|       }
 | |
| 
 | |
|       if (!isRightMB) {
 | |
|         dragElement(document.getElementById("calendar-time-container"));
 | |
|         let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
 | |
| 
 | |
|         function dragElement(elmnt) {
 | |
|           elmnt.onmousedown = dragMouseDown;
 | |
|           function dragMouseDown(e) {
 | |
|             e = e || window.event;
 | |
|             e.preventDefault();
 | |
|             pos3 = e.clientX;
 | |
|             pos4 = e.clientY;
 | |
| 
 | |
|             document.onmouseup = closeDragElement;
 | |
|             document.onmousemove = elementDrag;
 | |
|           }
 | |
| 
 | |
|           function elementDrag(e) {
 | |
|             e = e || window.event;
 | |
|             e.preventDefault();
 | |
|             // calculate the new cursor position:
 | |
|             pos1 = pos3 - e.clientX;
 | |
|             pos2 = pos4 - e.clientY;
 | |
|             pos3 = e.clientX;
 | |
|             pos4 = e.clientY;
 | |
|             // set the element's new position:
 | |
|             elmnt.style.bottom = undefined
 | |
|             elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
 | |
|             elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
 | |
|           }
 | |
| 
 | |
|           function closeDragElement() {
 | |
|             // stop moving when mouse button is released:
 | |
|             elmnt.onmousedown = undefined;
 | |
|             document.onmouseup = undefined;
 | |
|             document.onmousemove = undefined;
 | |
|             let xPos = (elmnt.offsetLeft - pos1) > window.innerWidth ? window.innerWidth - 200 : (elmnt.offsetLeft - pos1);
 | |
|             let yPos = (elmnt.offsetTop - pos2) > window.innerHeight - 20 ? window.innerHeight - 100 : (elmnt.offsetTop - pos2)
 | |
|             xPos = xPos < 0 ? 0 : xPos;
 | |
|             yPos = yPos < 0 ? 0 : yPos;
 | |
|             if (xPos != (elmnt.offsetLeft - pos1) || yPos != (elmnt.offsetTop - pos2)) {
 | |
|               elmnt.style.top = (yPos) + "px";
 | |
|               elmnt.style.left = (xPos) + "px";
 | |
|             }
 | |
|             game.system.rdd.calendrier.calendrierPos.top = yPos;
 | |
|             game.system.rdd.calendrier.calendrierPos.left = xPos;
 | |
|             if (game.user.isGM) {
 | |
|               game.settings.set(SYSTEM_RDD, "calendrier-pos", duplicate(game.system.rdd.calendrier.calendrierPos));
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|       } else if (isRightMB) {
 | |
|         game.system.rdd.calendrier.calendrierPos.top = 200;
 | |
|         game.system.rdd.calendrier.calendrierPos.left = 200;
 | |
|         if (game.user.isGM) {
 | |
|           game.settings.set(SYSTEM_RDD, "calendrier-pos", duplicate(game.system.rdd.calendrier.calendrierPos));
 | |
|         }
 | |
|         this.setPos(game.system.rdd.calendrier.calendrierPos);
 | |
|       }
 | |
|     });
 | |
|   }
 | |
|   /* -------------------------------------------- */
 | |
|   getNombresAstraux() {
 | |
|     return game.settings.get(SYSTEM_RDD, "liste-nombre-astral") ?? [];
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   dateCourante() {
 | |
|     return this.timestamp.formatDate();
 | |
|   }
 | |
| 
 | |
|   isAfterIndexDate(indexDate) {
 | |
|     // TODO: standardize
 | |
|     return indexDate < this.timestamp.indexDate;
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   heureCourante() { return RdDTimestamp.definition(this.timestamp.heure); }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   getCurrentMinute() { return this.timestamp.indexMinute; }
 | |
| 
 | |
|   getTimestamp() {
 | |
|     return this.timestamp;
 | |
|   }
 | |
|   getTimestampFinChateauDormant(nbJours = 0) {
 | |
|     return this.timestamp.nouveauJour().addJours(nbJours);
 | |
|   }
 | |
| 
 | |
|   getTimestampFinHeure(nbHeures = 0) {
 | |
|     return this.timestamp.nouvelleHeure().addHeures(nbHeures);
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   getIndexFromDate(jour, mois) {
 | |
|     const addYear = mois < this.timestamp.mois || (mois == this.timestamp.mois && jour < this.timestamp.jour)
 | |
|     const time = RdDTimestamp.timestamp(this.timestamp.annee + (addYear ? 1 : 0), mois, jour);
 | |
|     return time.indexDate;
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   getJoursSuivants(count) {
 | |
|     return Misc.intArray(this.timestamp.indexDate, this.timestamp.indexDate + count)
 | |
|       .map(i => { return { label: RdDTimestamp.formatIndexDate(i), index: i } })
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   async ajouterNombreAstral(indexDate, showDice = SHOW_DICE) {
 | |
|     const nombreAstral = await RdDDice.rollTotal("1dh", { showDice: showDice, rollMode: "selfroll" });
 | |
|     const dateFuture = RdDTimestamp.formatIndexDate(indexDate);
 | |
|     if (showDice != HIDE_DICE) {
 | |
|       ChatMessage.create({
 | |
|         whisper: ChatMessage.getWhisperRecipients("GM"),
 | |
|         content: `Le chiffre astrologique du ${dateFuture} sera le ${nombreAstral}`
 | |
|       });
 | |
|     }
 | |
|     return {
 | |
|       nombreAstral: nombreAstral,
 | |
|       valeursFausses: [],
 | |
|       index: indexDate
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   resetNombresAstraux() {
 | |
|     this.nombresAstraux = [];
 | |
|     game.settings.set(SYSTEM_RDD, "liste-nombre-astral", []);
 | |
| 
 | |
|     game.socket.emit(SYSTEM_SOCKET_ID, {
 | |
|       msg: "msg_reset_nombre_astral",
 | |
|       data: {}
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   /**
 | |
|    * 
 | |
|    * @param {*} indexDate la date pour laquelle obtenir le nombre astral. Si undefined, on prend la date du jour
 | |
|    * @returns le nombre astral pour la date, ou pour la date du jour si la date n'est pas fournie.
 | |
|    *  Si aucun nombre astral n'est trouvé, retourne 0 (cas où l'on demanderait un nombre astral en dehors des 12 jours courant et à venir)
 | |
|    */
 | |
|   getNombreAstral(indexDate = undefined) {
 | |
|     if (indexDate == undefined) {
 | |
|       indexDate = this.timestamp.indexDate;
 | |
|     }
 | |
|     this.nombresAstraux = this.getNombresAstraux();
 | |
|     let astralData = this.nombresAstraux.find((nombreAstral, i) => nombreAstral.index == indexDate);
 | |
|     return astralData?.nombreAstral ?? 0;
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   async rebuildNombresAstraux(showDice = HIDE_DICE) {
 | |
|     if (Misc.isUniqueConnectedGM()) {
 | |
|       let newList = [];
 | |
|       for (let i = 0; i < MAX_NOMBRE_ASTRAL; i++) {
 | |
|         let dayIndex = this.timestamp.indexDate + i;
 | |
|         let na = this.nombresAstraux.find(n => n.index == dayIndex);
 | |
|         if (na) {
 | |
|           newList[i] = na;
 | |
|         } else {
 | |
|           newList[i] = await this.ajouterNombreAstral(dayIndex, showDice);
 | |
|         }
 | |
|       }
 | |
|       this.nombresAstraux = newList;
 | |
|       game.settings.set(SYSTEM_RDD, "liste-nombre-astral", newList);
 | |
|       game.actors.filter(it => it.isPersonnage()).forEach(actor => actor.supprimerAnciensNombresAstraux());
 | |
|       this.notifyChangeNombresAstraux();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   notifyChangeNombresAstraux() {
 | |
|     Hooks.callAll(APP_ASTROLOGIE_REFRESH);
 | |
|     game.socket.emit(SYSTEM_SOCKET_ID, {
 | |
|       msg: "msg_refresh_nombre_astral",
 | |
|       data: {}
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   async setNewTimestamp(newTimestamp) {
 | |
|     const oldTimestamp = this.timestamp;
 | |
|     await Promise.all(game.actors.map(async actor => await actor.onTimeChanging(oldTimestamp, newTimestamp)));
 | |
|     RdDTimestamp.setWorldTime(newTimestamp);
 | |
|     if (oldTimestamp.indexDate + 1 == newTimestamp.indexDate && ReglesOptionelles.isUsing("chateau-dormant-gardien")) {
 | |
|       await DialogChateauDormant.create();
 | |
|     }
 | |
|     this.timestamp = newTimestamp;
 | |
|     await this.rebuildNombresAstraux();
 | |
|     this.updateDisplay();
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   async onCalendarButton(ev) {
 | |
|     ev.preventDefault();
 | |
|     const calendarAvance = ev.currentTarget.attributes['data-calendar-avance'];
 | |
|     const calendarSet = ev.currentTarget.attributes['data-calendar-set'];
 | |
|     if (calendarAvance) {
 | |
|       await this.incrementTime(Number(calendarAvance.value));
 | |
|     }
 | |
|     else if (calendarSet) {
 | |
|       this.positionnerHeure(Number(calendarSet.value));
 | |
|     }
 | |
|     this.updateDisplay();
 | |
|     Hooks.callAll(APP_ASTROLOGIE_REFRESH);
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   async incrementTime(minutes = 0) {
 | |
|     await this.setNewTimestamp(this.timestamp.addMinutes(minutes));
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   async incrementerJour() {
 | |
|     await this.setNewTimestamp(this.timestamp.nouveauJour());
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   async positionnerHeure(heure) {
 | |
|     const indexDate = this.timestamp.indexDate;
 | |
|     const addDay = this.timestamp.heure < heure ? 0 : 1;
 | |
|     const newTimestamp = new RdDTimestamp({ indexDate: indexDate + addDay }).addHeures(heure);
 | |
|     await this.setNewTimestamp(newTimestamp)
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   fillCalendrierData(formData = {}) {
 | |
|     mergeObject(formData, this.timestamp.toCalendrier());
 | |
|     formData.isGM = game.user.isGM;
 | |
|     return formData;
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   getLectureAstrologieDifficulte(dateIndex) {
 | |
|     let indexNow = this.timestamp.indexDate;
 | |
|     let diffDay = dateIndex - indexNow;
 | |
|     return - Math.floor(diffDay / 2);
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   async requestNombreAstral(request) {
 | |
|     const actor = game.actors.get(request.id);
 | |
|     if (Misc.isUniqueConnectedGM()) { // Only once
 | |
|       console.log(request);
 | |
|       let jourDiff = this.getLectureAstrologieDifficulte(request.date);
 | |
|       let niveau = Number(request.astrologie.system.niveau) + Number(request.conditions) + Number(jourDiff) + Number(request.etat);
 | |
|       let rollData = {
 | |
|         caracValue: request.carac_vue,
 | |
|         finalLevel: niveau,
 | |
|         showDice: HIDE_DICE,
 | |
|         rollMode: "blindroll"
 | |
|       };
 | |
|       await RdDResolutionTable.rollData(rollData);
 | |
|       request.rolled = rollData.rolled;
 | |
|       request.isValid = request.rolled.isSuccess;
 | |
|       request.nbAstral = this.getNombreAstral(request.date);
 | |
| 
 | |
|       if (request.rolled.isSuccess) {
 | |
|         if (request.rolled.isPart) {
 | |
|           // Gestion expérience (si existante)
 | |
|           request.competence = actor.getCompetence("astrologie")
 | |
|           request.selectedCarac = actor.system.carac["vue"];
 | |
|           actor.appliquerAjoutExperience(request, 'hide');
 | |
|         }
 | |
|       }
 | |
|       else {
 | |
|         request.nbAstral = await RdDDice.rollTotal("1dhr" + request.nbAstral, {
 | |
|           rollMode: "selfroll", showDice: HIDE_DICE
 | |
|         });
 | |
|         // Mise à jour des nombres astraux du joueur
 | |
|         this.addNbAstralIncorect(request.id, request.date, request.nbAstral);
 | |
|       }
 | |
| 
 | |
|       if (Misc.getActiveUser(request.userId)?.isGM) {
 | |
|         RdDUtility.responseNombreAstral(request);
 | |
|       } else {
 | |
|         game.socket.emit(SYSTEM_SOCKET_ID, {
 | |
|           msg: "msg_response_nombre_astral",
 | |
|           data: request
 | |
|         });
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   addNbAstralIncorect(actorId, date, nbAstral) {
 | |
|     let astralData = this.nombresAstraux.find((nombreAstral, i) => nombreAstral.index == date);
 | |
|     astralData.valeursFausses.push({ actorId: actorId, nombreAstral: nbAstral });
 | |
|     game.settings.set(SYSTEM_RDD, "liste-nombre-astral", this.nombresAstraux);
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   getAjustementAstrologique(heureNaissance, name = undefined) {
 | |
|     let defHeure = RdDTimestamp.findHeure(heureNaissance);
 | |
|     if (defHeure) {
 | |
|       return RdDTimestamp.ajustementAstrologiqueHeure(defHeure.heure, this.getNombreAstral(), this.timestamp.heure);
 | |
|     }
 | |
|     else if (name) {
 | |
|       ui.notifications.warn(name + " n'a pas d'heure de naissance, ou elle est incorrecte : " + heureNaissance);
 | |
|     }
 | |
|     else {
 | |
|       ui.notifications.warn(heureNaissance + " ne correspond pas à une heure de naissance");
 | |
|     }
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   getData() {
 | |
|     let formData = super.getData();
 | |
|     this.fillCalendrierData(formData);
 | |
|     this.setPos(this.calendrierPos);
 | |
|     return formData;
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   setPos(pos) {
 | |
|     return new Promise(resolve => {
 | |
|       function check() {
 | |
|         let elmnt = document.getElementById("calendar-time-container");
 | |
|         if (elmnt) {
 | |
|           elmnt.style.bottom = undefined;
 | |
|           let xPos = (pos.left) > window.innerWidth ? window.innerWidth - 200 : pos.left;
 | |
|           let yPos = (pos.top) > window.innerHeight - 20 ? window.innerHeight - 100 : pos.top;
 | |
|           elmnt.style.top = (yPos) + "px";
 | |
|           elmnt.style.left = (xPos) + "px";
 | |
|           resolve();
 | |
|         } else {
 | |
|           setTimeout(check, 30);
 | |
|         }
 | |
|       }
 | |
|       check();
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   updateDisplay() {
 | |
|     let calendrier = this.fillCalendrierData();
 | |
|     // Rebuild text du calendrier
 | |
|     let dateHTML = `${calendrier.jourDuMois} ${calendrier.mois.label} (${calendrier.mois.saison}) de l'année ${calendrier.annee}`
 | |
|     if (game.user.isGM) {
 | |
|       dateHTML = dateHTML + "<br>Nombre Astral: " + (this.getNombreAstral() ?? "?");
 | |
|     }
 | |
|     for (let handle of document.getElementsByClassName("calendar-title")) {
 | |
|       handle.innerHTML = dateHTML;
 | |
|     }
 | |
|     for (let heure of document.getElementsByClassName("calendar-heure-texte")) {
 | |
|       heure.innerHTML = calendrier.heure.label;
 | |
|     }
 | |
|     for (const minute of document.getElementsByClassName("calendar-minute-texte")) {
 | |
|       minute.innerHTML = `${calendrier.minute} minutes`;
 | |
|     }
 | |
|     for (const heureImg of document.getElementsByClassName("calendar-heure-img")) {
 | |
|       heureImg.src = calendrier.heure.icon;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   async saveEditeur(calendrierData) {
 | |
|     const newTimestamp = RdDTimestamp.timestamp(
 | |
|       Number.parseInt(calendrierData.annee),
 | |
|       calendrierData.mois.heure,
 | |
|       Number.parseInt(calendrierData.jourMois),
 | |
|       calendrierData.heure.heure,
 | |
|       Number.parseInt(calendrierData.minutes)
 | |
|     );
 | |
|     await this.setNewTimestamp(newTimestamp);
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   async showCalendarEditor() {
 | |
|     let calendrierData = this.fillCalendrierData();
 | |
|     if (this.editeur == undefined) {
 | |
|       let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/calendar-editor-template.html', calendrierData);
 | |
|       this.editeur = new RdDCalendrierEditeur(html, this, calendrierData)
 | |
|     }
 | |
|     this.editeur.updateData(calendrierData);
 | |
|     this.editeur.render(true);
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   async showAstrologieEditor() {
 | |
|     await AppAstrologie.create();
 | |
|   }
 | |
| } |