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 { RdDUtility } from "../rdd-utility.js"; import { RdDDice } from "../rdd-dice.js"; import { Misc } from "../misc.js"; import { DialogChronologie } from "../dialog-chronologie.js"; import { HIDE_DICE, 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 init() { 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.isUniqueConnectedGM()) { // 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.rollTotal("1dh", { 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.isUniqueConnectedGM()) { 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.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 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() } }