foundryvtt-reve-de-dragon/module/rdd-timestamp.js
Vincent Vandemeulebrouck 5a66e4e741 Fix horloge aiguille des heures
L'aiguille des heures pointe sur l'heure au début de l'heure
draconique, comme pour une montre.

Début couronne, l'aiguille des heures et des minutes sont donc
toutes les deux en haut.
2023-03-21 22:00:24 +01:00

341 lines
12 KiB
JavaScript

import { SHOW_DICE, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js";
import { Grammar } from "./grammar.js";
import { Misc } from "./misc.js";
import { RdDDice } from "./rdd-dice.js";
export const WORLD_TIMESTAMP_SETTING = "calendrier";
const RDD_JOURS_PAR_AN = 336; //RDD_JOURS_PAR_MOIS * RDD_MOIS_PAR_AN;
const RDD_MOIS_PAR_AN = 12;
export const RDD_JOURS_PAR_MOIS = 28;
export const RDD_HEURES_PAR_JOUR = 12;
export const MAX_NOMBRE_ASTRAL = 12;
export const RDD_MINUTES_PAR_HEURES = 120;
export const RDD_MINUTES_PAR_JOUR = 1440; //RDD_HEURES_PAR_JOUR * RDD_MINUTES_PAR_HEURES;
const ROUNDS_PAR_MINUTE = 10;
const DEFINITION_HEURES = [
{ key: "vaisseau", label: "Vaisseau", lettreFont: 'v', saison: "Printemps" },
{ key: "sirene", label: "Sirène", lettreFont: 'i', saison: "Printemps" },
{ key: "faucon", label: "Faucon", lettreFont: 'f', saison: "Printemps" },
{ key: "couronne", label: "Couronne", lettreFont: '', saison: "Eté" },
{ key: "dragon", label: "Dragon", lettreFont: 'd', saison: "Eté" },
{ key: "epees", label: "Epées", lettreFont: 'e', saison: "Eté" },
{ key: "lyre", label: "Lyre", lettreFont: 'l', saison: "Automne" },
{ key: "serpent", label: "Serpent", lettreFont: 's', saison: "Automne" },
{ key: "poissonacrobate", label: "Poisson Acrobate", lettreFont: 'p', saison: "Automne" },
{ key: "araignee", label: "Araignée", lettreFont: 'a', saison: "Hiver" },
{ key: "roseau", label: "Roseau", lettreFont: 'r', saison: "Hiver" },
{ key: "chateaudormant", label: "Château Dormant", lettreFont: 'c', saison: "Hiver" },
]
const FORMULES_DUREE = [
{ code: "", label: "", calcul: async (t, actor) => t.addJours(100 * RDD_JOURS_PAR_AN) },
{ code: "jour", label: "1 jour", calcul: async (t, actor) => t.nouveauJour().addJours(1) },
{ code: "1d7jours", label: "1d7 jour", calcul: async (t, actor) => t.nouveauJour().addJours(await RdDDice.rollTotal('1d7', { showDice: SHOW_DICE })) },
{ code: "1ddr", label: "Un dé draconique jours", calcul: async (t, actor) => t.nouveauJour().addJours(await RdDDice.rollTotal('1dr+7', { showDice: SHOW_DICE })) },
{ code: "hn", label: "Fin de l'Heure de Naissance", calcul: async (t, actor) => t.finHeure(actor.getHeureNaissance()) },
// { code: "1h", label: "Une heure", calcul: async (t, actor) => t.nouvelleHeure().addHeures(1) },
// { code: "12h", label: "12 heures", calcul: async (t, actor) => t.nouvelleHeure().addHeures(12) },
// { code: "chateaudormant", label: "Fin Chateau dormant", calcul: async (t, actor) => t.nouveauJour() },
// { code: "special", label: "Spéciale", calcul: async (t, actor) => t.addJours(100 * RDD_JOURS_PAR_AN) },
]
const FORMULES_PERIODE = [
{ code: 'round', label: "Rounds", calcul: async (t, nombre) => t.addMinutes(nombre / 10) },
{ code: 'minute', label: "Minutes", calcul: async (t, nombre) => t.addMinutes(nombre) },
{ code: 'heure', label: "Heures", calcul: async (t, nombre) => t.addHeures(nombre) },
{ code: 'jour', label: "Jours", calcul: async (t, nombre) => t.addJours(nombre) },
]
export class RdDTimestamp {
static init() {
game.settings.register(SYSTEM_RDD, WORLD_TIMESTAMP_SETTING, {
name: WORLD_TIMESTAMP_SETTING,
scope: "world",
config: false,
default: { indexDate: 0, indexMinute: 0 },
type: Object
});
for (let i = 0; i < DEFINITION_HEURES.length; i++) {
DEFINITION_HEURES[i].heure = i;
DEFINITION_HEURES[i].hh = RdDTimestamp.hh(i);
DEFINITION_HEURES[i].icon = RdDTimestamp.iconeHeure(i);
DEFINITION_HEURES[i].webp = DEFINITION_HEURES[i].icon.replace(".svg", ".webp");
}
}
static hh(heure) {
return heure < 9 ? `0${heure + 1}` : `${heure + 1}`;
}
static iconeHeure(heure) {
return `systems/foundryvtt-reve-de-dragon/icons/heures/hd${RdDTimestamp.hh(heure)}.svg`;
}
static definitions() {
return DEFINITION_HEURES
}
static formulesDuree() {
return FORMULES_DUREE
}
static formulesPeriode() {
return FORMULES_PERIODE
}
static heures() {
return Misc.intArray(0, RDD_HEURES_PAR_JOUR)
}
/**
* @param signe
* @returns L'entrée de DEFINITION_HEURES correspondant au signe
*/
static definition(signe) {
if (signe == undefined) {
signe = 0;
}
if (Number.isInteger(signe)) {
return DEFINITION_HEURES[signe % RDD_HEURES_PAR_JOUR];
}
let definition = DEFINITION_HEURES.find(it => it.key == signe);
if (!definition) {
definition = Misc.findFirstLike(signe, DEFINITION_HEURES, { mapper: it => it.label, description: 'signe' });
}
return definition
}
static imgSigneHeure(heure) {
return RdDTimestamp.imgSigne(RdDTimestamp.definition(heure));
}
static imgSigne(signe) {
return signe == undefined ? '' : `<img class="img-signe-heure" src="${signe.webp}" alt="${signe.label}" title="${signe.label}"/>`
}
static ajustementAstrologiqueHeure(hn, nbAstral, heure) {
let ecart = (hn + nbAstral - heure) % RDD_HEURES_PAR_JOUR;
if (ecart < 0) {
ecart = (ecart + RDD_HEURES_PAR_JOUR) % RDD_HEURES_PAR_JOUR;
}
switch (ecart) {
case 0: return 4;
case 4: case 8: return 2;
case 6: return -4;
case 3: case 9: return -2;
}
return 0;
}
static handleTimestampEditor(html, path, consumeTimestamp = async (path, timestamp) => { }) {
const fields = {
annee: html.find(`input[name="${path}.annee"]`),
mois: html.find(`select[name="${path}.mois"]`),
jourDuMois: html.find(`input[name="${path}.jourDuMois"]`),
heure: html.find(`select[name="${path}.heure"]`),
minute: html.find(`input[name="${path}.minute"]`)
};
async function onChangeTimestamp(fields, path) {
const annee = Number(fields.annee.val());
const mois = fields.mois.val();
const jour = Number(fields.jourDuMois.val());
const heure = fields.heure.val();
const minute = Number(fields.minute.val());
await consumeTimestamp(path, RdDTimestamp.timestamp(annee, mois, jour, heure, minute));
}
fields.annee.change(async (event) => await onChangeTimestamp(fields, path));
fields.mois.change(async (event) => await onChangeTimestamp(fields, path));
fields.jourDuMois.change(async (event) => await onChangeTimestamp(fields, path));
fields.heure.change(async (event) => await onChangeTimestamp(fields, path));
fields.minute.change(async (event) => await onChangeTimestamp(fields, path));
}
static findHeure(heure) {
heure = Grammar.toLowerCaseNoAccentNoSpace(heure);
let parHeureOuLabel = DEFINITION_HEURES.filter(it => (it.heure) == parseInt(heure) % RDD_HEURES_PAR_JOUR || Grammar.toLowerCaseNoAccentNoSpace(it.label) == heure);
if (parHeureOuLabel.length == 1) {
return parHeureOuLabel[0];
}
let parLabelPartiel = DEFINITION_HEURES.filter(it => Grammar.toLowerCaseNoAccentNoSpace(it.label).includes(heure));
if (parLabelPartiel.length > 0) {
parLabelPartiel.sort(Misc.ascending(h => h.label.length));
return parLabelPartiel[0];
}
return undefined;
}
/**
* @param indexDate: la date (depuis le jour 0)
* @return la version formattée de la date
*/
static formatIndexDate(indexDate) {
return new RdDTimestamp({ indexDate }).formatDate()
}
static splitIndexDate(indexDate) {
const timestamp = new RdDTimestamp({ indexDate });
return {
jour: timestamp.jour + 1,
mois: RdDTimestamp.definition(timestamp.mois).key
}
}
static getWorldTime() {
let worldTime = game.settings.get(SYSTEM_RDD, WORLD_TIMESTAMP_SETTING);
if (worldTime.indexJour != undefined && worldTime.heureRdD != undefined) {
// Migration
worldTime = {
indexDate: worldTime.indexJour,
indexMinute: worldTime.heureRdD * 120 + worldTime.minutesRelative
};
RdDTimestamp.setWorldTime(new RdDTimestamp(worldTime))
}
return new RdDTimestamp(worldTime);
}
static setWorldTime(timestamp) {
game.settings.set(SYSTEM_RDD, WORLD_TIMESTAMP_SETTING, duplicate(timestamp));
}
/** construit un RdDTimestamp à partir de l'année/mois/jour/heure?/minute? */
static timestamp(annee, mois, jour, heure = 0, minute = 0) {
mois = this.definition(mois)?.heure
heure = this.definition(heure)?.heure
return new RdDTimestamp({
indexDate: (jour - 1) + (mois + annee * RDD_MOIS_PAR_AN) * RDD_JOURS_PAR_MOIS,
indexMinute: heure * RDD_MINUTES_PAR_HEURES + minute
})
}
/**
* Constructeur d'un timestamp.
* Selon les paramètres, l'objet construit se base su:
* - le timestamp
* - la date numérique + minute (dans la journée)
* @param indexDate: la date à utiliser pour ce timestamp
* @param indexMinute: la minute de la journée à utiliser pour ce timestamp
*
*/
constructor({ indexDate, indexMinute = undefined }) {
this.indexDate = indexDate
this.indexMinute = indexMinute ?? 0
}
/**
* Convertit le timestamp en une structure avec les informations utiles
* pour afficher la date et l'heure
*/
toCalendrier() {
return {
timestamp: this,
annee: this.annee,
mois: RdDTimestamp.definition(this.mois),
jour: this.jour,
jourDuMois: this.jour + 1,
heure: RdDTimestamp.definition(this.heure),
minute: this.minute
};
}
get annee() { return Math.floor(this.indexDate / RDD_JOURS_PAR_AN) }
get mois() { return Math.floor((this.indexDate % RDD_JOURS_PAR_AN) / RDD_JOURS_PAR_MOIS) }
get jour() { return (this.indexDate % RDD_JOURS_PAR_AN) % RDD_JOURS_PAR_MOIS }
get heure() { return Math.floor(this.indexMinute / RDD_MINUTES_PAR_HEURES) }
get minute() { return this.indexMinute % RDD_MINUTES_PAR_HEURES }
get round() { return ROUNDS_PAR_MINUTE * (this.indexMinute - Math.floor(this.indexMinute)) }
get angleHeure() { return this.indexMinute / RDD_MINUTES_PAR_JOUR * 360 - 45 }
get angleMinute() { return this.indexMinute / RDD_MINUTES_PAR_HEURES * 360 + 45}
formatDate() {
const jour = this.jour + 1;
const mois = RdDTimestamp.definition(this.mois).label;
const annee = this.annee ?? '';
return `${jour} ${mois}` + (annee ? ' ' + annee : '');
}
nouveauJour() { return new RdDTimestamp({ indexDate: this.indexDate + 1, indexMinute: 0 }) }
nouvelleHeure() {
return this.heure >= RDD_HEURES_PAR_JOUR ? this.nouveauJour() : new RdDTimestamp({
indexDate: this.indexDate,
indexMinute: (this.heure + 1) * RDD_MINUTES_PAR_HEURES
})
}
addJours(jours) {
return jours == 0 ? this : new RdDTimestamp({
indexDate: this.indexDate + jours,
indexMinute: this.indexMinute
})
}
addHeures(heures) {
if (heures == 0) {
return this
}
const heure = this.heure + heures;
return new RdDTimestamp({
indexDate: this.indexDate + Math.floor(heure / RDD_HEURES_PAR_JOUR),
indexMinute: this.indexMinute + (heure % RDD_HEURES_PAR_JOUR) * RDD_MINUTES_PAR_HEURES
})
}
addMinutes(minutes) {
if (minutes == 0) {
return this;
}
const indexMinute = this.indexMinute + minutes;
const jours = Math.floor(indexMinute / RDD_MINUTES_PAR_JOUR)
return new RdDTimestamp({
indexDate: this.indexDate + jours,
indexMinute: indexMinute - (jours * RDD_MINUTES_PAR_JOUR)
})
}
addPeriode(nombre, unite) {
const formule = FORMULES_PERIODE.find(it => it.code == unite);
if (formule) {
return formule.calcul(this, nombre);
}
else {
ui.notifications.info(`Pas de période pour ${unite ?? 'Aucune uinité définie'}`)
}
return this;
}
finHeure(heure) {
return this.nouvelleHeure().addHeures((12 + heure - this.heure) % 12);
}
async appliquerDuree(duree, actor) {
const formule = FORMULES_DUREE.find(it => it.code == duree) ?? FORMULES_DUREE.find(it => it.code == "");
return await formule.calcul(this, actor);
}
compare(timestamp) {
let diff = this.indexDate - timestamp.indexDate
if (diff == 0) {
diff = this.indexMinute - timestamp.indexMinute
}
return diff < 0 ? -1 : diff > 0 ? 1 : 0;
}
difference(timestamp) {
const jours = this.indexDate - timestamp.indexDate;
const minutes = this.indexMinute - timestamp.indexMinute;
return {
jours: jours,
heures: Math.floor(minutes / RDD_MINUTES_PAR_HEURES),
minutes: minutes % RDD_MINUTES_PAR_HEURES
}
}
}