foundryvtt-reve-de-dragon/module/time/rdd-calendrier.js
Vincent Vandemeulebrouck a103239288 Passe de vérification mergeObject
Quand mergeObject est utilisé pour retourner une valeur, faire très
attention à ne pas passer un Item/Actor, ou une de ses sous parties
en premier paramètre sans préciser l'option { inplace: false }

Sinon, le premier paramètre subit une mutation!
2024-06-01 01:53:14 +02:00

434 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 { 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()
}
}