forked from public/foundryvtt-reve-de-dragon
		
	Import de foundry.applications.handlebars.renderTemplate au travers d'une constante pour éliminer les warnings
		
			
				
	
	
		
			435 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			435 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| import { MAX_NOMBRE_ASTRAL, RdDTimestamp, WORLD_TIMESTAMP_SETTING } from "./rdd-timestamp.js";
 | |
| import { RdDCalendrierEditor } from "./rdd-calendrier-editor.js";
 | |
| import { RdDResolutionTable } from "../rdd-resolution-table.js";
 | |
| import { RdDDice } from "../rdd-dice.js";
 | |
| import { Misc } from "../misc.js";
 | |
| import { DialogChronologie } from "../dialog-chronologie.js";
 | |
| import { HIDE_DICE, renderTemplate, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "../constants.js";
 | |
| import { ReglesOptionnelles } from "../settings/regles-optionnelles.js";
 | |
| import { DialogChateauDormant } from "../sommeil/dialog-chateau-dormant.js";
 | |
| import { APP_ASTROLOGIE_REFRESH, AppAstrologie } from "../sommeil/app-astrologie.js";
 | |
| import { AutoAdjustDarkness } from "./auto-adjust-darkness.js";
 | |
| 
 | |
| const TEMPLATE_CALENDRIER = "systems/foundryvtt-reve-de-dragon/templates/time/calendar.hbs";
 | |
| 
 | |
| const INITIAL_CALENDAR_POS = { top: 200, left: 200, horlogeAnalogique: true };
 | |
| /* -------------------------------------------- */
 | |
| export class RdDCalendrier extends Application {
 | |
|   static _warnedAppV1 = true
 | |
| 
 | |
|   static initSettings() {
 | |
|     game.settings.register(SYSTEM_RDD, "liste-nombre-astral", {
 | |
|       name: "liste-nombre-astral",
 | |
|       scope: "world",
 | |
|       config: false,
 | |
|       default: [],
 | |
|       type: Object
 | |
|     });
 | |
| 
 | |
|     game.settings.register(SYSTEM_RDD, "calendrier-pos", {
 | |
|       name: "calendrierPos",
 | |
|       scope: "client",
 | |
|       config: false,
 | |
|       default: INITIAL_CALENDAR_POS,
 | |
|       type: Object
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   static get defaultOptions() {
 | |
|     return foundry.utils.mergeObject(super.defaultOptions, {
 | |
|       title: "Calendrier",
 | |
|       template: TEMPLATE_CALENDRIER,
 | |
|       classes: ["calendar"],
 | |
|       popOut: true,
 | |
|       resizable: false,
 | |
|       width: 'fit-content',
 | |
|       height: 'fit-content',
 | |
|     }, { inplace: false })
 | |
|   }
 | |
| 
 | |
|   constructor() {
 | |
|     super();
 | |
|     this.timestamp = RdDTimestamp.getWorldTime();
 | |
|     if (Misc.isFirstConnectedGM()) { // Uniquement si GM
 | |
|       RdDTimestamp.setWorldTime(this.timestamp);
 | |
|       this.rebuildNombresAstraux(); // Ensure always up-to-date
 | |
|     }
 | |
|     Hooks.on('updateSetting', async (setting, update, options, id) => this.onUpdateSetting(setting, update, options, id));
 | |
|   }
 | |
| 
 | |
|   get title() {
 | |
|     const calendrier = this.timestamp.toCalendrier();
 | |
|     return `${calendrier.heure.label}, ${calendrier.jourDuMois} ${calendrier.mois.label} ${calendrier.annee} (${calendrier.mois.saison})`;
 | |
|   }
 | |
| 
 | |
|   savePosition() {
 | |
|     game.settings.set(SYSTEM_RDD, "calendrier-pos", {
 | |
|       top: this.position.top,
 | |
|       left: this.position.left,
 | |
|       horlogeAnalogique: this.horlogeAnalogique
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   getSavePosition() {
 | |
|     const pos = game.settings.get(SYSTEM_RDD, "calendrier-pos");
 | |
|     if (pos?.top == undefined) {
 | |
|       return INITIAL_CALENDAR_POS;
 | |
|     }
 | |
|     this.horlogeAnalogique = pos.horlogeAnalogique;
 | |
|     return pos
 | |
|   }
 | |
| 
 | |
|   setPosition(position) {
 | |
|     super.setPosition(position)
 | |
|     this.savePosition()
 | |
|   }
 | |
| 
 | |
|   display() {
 | |
|     AutoAdjustDarkness.adjust(RdDTimestamp.getWorldTime().darkness);
 | |
|     const pos = this.getSavePosition()
 | |
|     this.render(true, { left: pos.left, top: pos.top });
 | |
|     return this;
 | |
|   }
 | |
| 
 | |
|   _getHeaderButtons() {
 | |
|     if (game.user.isGM) {
 | |
|       return [
 | |
|         { class: "calendar-astrologie", icon: "fa-solid fa-moon-over-sun", onclick: ev => this.showAstrologieEditor() },
 | |
|         { class: "calendar-set-datetime", icon: "fa-solid fa-calendar-pen", onclick: ev => this.showCalendarEditor() },
 | |
|       ]
 | |
|     }
 | |
|     return []
 | |
|   }
 | |
| 
 | |
|   async close() { }
 | |
| 
 | |
|   async onUpdateSetting(setting, update, options, id) {
 | |
|     if (setting.key == SYSTEM_RDD + '.' + WORLD_TIMESTAMP_SETTING) {
 | |
|       this.timestamp = RdDTimestamp.getWorldTime();
 | |
|       this.positionAiguilles()
 | |
|       this.render(false);
 | |
|       Hooks.callAll(APP_ASTROLOGIE_REFRESH)
 | |
|     }
 | |
|     if (setting.key == SYSTEM_RDD + '.' + "liste-nombre-astral") {
 | |
|       Hooks.callAll(APP_ASTROLOGIE_REFRESH)
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   getData() {
 | |
|     const formData = super.getData();
 | |
|     this.fillCalendrierData(formData);
 | |
|     return formData;
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   fillCalendrierData(formData = {}) {
 | |
|     foundry.utils.mergeObject(formData, this.timestamp.toCalendrier());
 | |
|     formData.isGM = game.user.isGM
 | |
|     formData.heures = RdDTimestamp.definitions()
 | |
|     formData.horlogeAnalogique = this.horlogeAnalogique
 | |
|     formData.autoDarkness = AutoAdjustDarkness.isAuto()
 | |
|     return formData;
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   /** @override */
 | |
|   async activateListeners(html) {
 | |
|     super.activateListeners(html);
 | |
|     this.html = html;
 | |
|     this.html.find('.ajout-chronologie').click(ev => DialogChronologie.create());
 | |
|     this.html.find('.toggle-horloge-analogique').click(ev => this.onToggleHorlogeAnalogique())
 | |
|     this.html.find('.toggle-auto-darkness').click(ev => this.onToggleAutoDarkness())
 | |
|     this.html.find('.calendar-btn').click(ev => this.onCalendarButton(ev));
 | |
|     this.html.find('.horloge-roue .horloge-heure').click(event => {
 | |
|       const h = this.html.find(event.currentTarget)?.data('heure');
 | |
|       this.positionnerHeure(Number(h));
 | |
|     })
 | |
|     this.html.find('.calendar-set-datetime').click(ev => {
 | |
|       ev.preventDefault();
 | |
|       this.showCalendarEditor();
 | |
|     });
 | |
| 
 | |
|     this.html.find('.calendar-astrologie').click(ev => {
 | |
|       ev.preventDefault();
 | |
|       this.showAstrologieEditor();
 | |
|     });
 | |
|     this.positionAiguilles()
 | |
|   }
 | |
| 
 | |
|   positionAiguilles() {
 | |
|     const timestamp = this.getTimestamp();
 | |
|     this.html.find(`div.horloge-roue div.horloge-aiguille-heure img`).css(Misc.cssRotation(timestamp.angleHeure));
 | |
|     this.html.find(`div.horloge-roue div.horloge-aiguille-minute img`).css(Misc.cssRotation(timestamp.angleMinute));
 | |
|   }
 | |
| 
 | |
|   onToggleHorlogeAnalogique() {
 | |
|     this.horlogeAnalogique = !this.horlogeAnalogique;
 | |
|     this.savePosition()
 | |
|     this.display()
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   getNombresAstraux() {
 | |
|     return game.settings.get(SYSTEM_RDD, "liste-nombre-astral") ?? []
 | |
|   }
 | |
| 
 | |
|   async setNombresAstraux(nombresAstraux) {
 | |
|     await game.settings.set(SYSTEM_RDD, "liste-nombre-astral", nombresAstraux)
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   dateCourante() {
 | |
|     return this.timestamp.formatDate();
 | |
|   }
 | |
| 
 | |
|   dateReel() {
 | |
|     return new Date().toLocaleString("sv-SE", {
 | |
|       year: "numeric",
 | |
|       month: "2-digit",
 | |
|       day: "2-digit",
 | |
|       hour: "2-digit",
 | |
|       minute: "2-digit"
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   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) {
 | |
|     const nombreAstral = await RdDDice.rollHeure( { showDice: HIDE_DICE, rollMode: "selfroll" });
 | |
|     return {
 | |
|       nombreAstral: nombreAstral,
 | |
|       lectures: [],
 | |
|       index: indexDate
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   async resetNombresAstraux() {
 | |
|     await Promise.all(game.actors.filter(it => it.type == "personnage").map(async it => await it.deleteNombresAstraux()))
 | |
|     await this.setNombresAstraux([])
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    *
 | |
|    * @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;
 | |
|     }
 | |
|     const nombresAstraux = this.getNombresAstraux()
 | |
|     let astralData = nombresAstraux.find(it => it.index == indexDate);
 | |
|     return astralData?.nombreAstral ?? 0;
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   async rebuildNombresAstraux() {
 | |
|     if (Misc.isFirstConnectedGM()) {
 | |
|       const nombresAstraux = this.getNombresAstraux()
 | |
|       let newNombresAstraux = [];
 | |
|       for (let i = 0; i < MAX_NOMBRE_ASTRAL; i++) {
 | |
|         let dayIndex = this.timestamp.indexDate + i;
 | |
|         let na = nombresAstraux.find(it => it.index == dayIndex);
 | |
|         if (na) {
 | |
|           newNombresAstraux[i] = na;
 | |
|         } else {
 | |
|           newNombresAstraux[i] = await this.ajouterNombreAstral(dayIndex);
 | |
|         }
 | |
|       }
 | |
|       game.actors.filter(it => it.isPersonnage()).forEach(actor => actor.supprimerAnciensNombresAstraux());
 | |
|       await this.setNombresAstraux(newNombresAstraux);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   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 && ReglesOptionnelles.isUsing("chateau-dormant-gardien")) {
 | |
|       await DialogChateauDormant.create();
 | |
|     }
 | |
|     this.timestamp = newTimestamp;
 | |
|     await this.rebuildNombresAstraux();
 | |
|     this.positionAiguilles()
 | |
|     this.display();
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   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.positionAiguilles()
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   async incrementTime(minutes = 0) {
 | |
|     if (game.user.isGM) {
 | |
|       await this.setNewTimestamp(this.timestamp.addMinutes(minutes));
 | |
|       Hooks.callAll(APP_ASTROLOGIE_REFRESH);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   async incrementerJour() {
 | |
|     await this.setNewTimestamp(this.timestamp.nouveauJour());
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   async positionnerHeure(heure) {
 | |
|     if (game.user.isGM) {
 | |
|       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)
 | |
|       Hooks.callAll(APP_ASTROLOGIE_REFRESH);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   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.isFirstConnectedGM()) { // 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
 | |
|       await this.addNbAstralJoueur(actor, request.date, request.nbAstral, request.isValid)
 | |
|       Hooks.callAll(APP_ASTROLOGIE_REFRESH)
 | |
|       game.socket.emit(SYSTEM_SOCKET_ID, { msg: "msg_app_astrologie_refresh", data: {} })
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   async addNbAstralJoueur(actor, date, nbAstral, isValid) {
 | |
|     const nombresAstraux = this.getNombresAstraux()
 | |
|     const astralData = nombresAstraux.find(it => it.index == date)
 | |
|     if (astralData) {
 | |
|       astralData.lectures.push({ actorId: actor.id, nombreAstral: nbAstral });
 | |
|       await this.setNombresAstraux(nombresAstraux);
 | |
|       await actor.ajouteNombreAstral(date, nbAstral, isValid);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   getAjustementAstrologique(heureNaissance, name = undefined) {
 | |
|     const 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;
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   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() {
 | |
|     const calendrierData = this.fillCalendrierData();
 | |
|     if (this.editeur == undefined) {
 | |
|       const html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/time/calendar-editor.hbs', calendrierData);
 | |
|       this.editeur = new RdDCalendrierEditor(html, this, calendrierData)
 | |
|     }
 | |
|     this.editeur.updateData(calendrierData);
 | |
|     this.editeur.render(true);
 | |
|   }
 | |
| 
 | |
|   /* -------------------------------------------- */
 | |
|   async showAstrologieEditor() {
 | |
|     await AppAstrologie.create();
 | |
|   }
 | |
| 
 | |
|   async onToggleAutoDarkness() {
 | |
|     await AutoAdjustDarkness.toggle()
 | |
|     this.display()
 | |
|   }
 | |
| }
 |