Gestion de la périodicité/temporalité

This commit is contained in:
2023-01-07 23:28:30 +01:00
parent 739fcbdf09
commit c3076fdbfc
18 changed files with 269 additions and 315 deletions

View File

@@ -136,6 +136,11 @@ export class RdDBaseActor extends Actor {
async onUpdateActor(update, options, actorId) { }
async onTimeChanging(oldTimestamp, newTimestamp) {
this.items.filter(it => it.isFinPeriode(oldTimestamp, newTimestamp))
.forEach(async it => await it.onFinPeriodeTemporel(oldTimestamp, newTimestamp))
}
/* -------------------------------------------- */
getFortune() {
return Monnaie.getFortune(this.itemTypes['monnaie']);

View File

@@ -152,17 +152,20 @@ export class RdDItem extends Item {
isCompetenceCreature() { return this.type == 'competencecreature' }
isConteneur() { return this.type == 'conteneur'; }
isMonnaie() { return this.type == 'monnaie'; }
isPotion() { return this.type == 'potion'; }
isNourritureBoisson() { return this.type == 'nourritureboisson'; }
isService() { return this.type == 'service'; }
isCompetence() { return typesObjetsCompetence.includes(this.type) }
isTemporel() { return typesObjetsTemporels.includes(this.type) }
isOeuvre() { return typesObjetsOeuvres.includes(this.type) }
isDraconique() { return typesObjetsDraconiques.includes(this.type) }
isEffet() { return typesObjetsEffet.includes(this.type) }
isConnaissance() { return typesObjetsConnaissance.includes(this.type) }
isInventaire(mode = 'materiel') { return RdDItem.getItemTypesInventaire(mode).includes(this.type); }
isAlcool() { return this.isNourritureBoisson() && this.system.boisson && this.system.alcoolise; }
isHerbeAPotion() { return this.type == 'herbe' && (this.system.categorie == 'Soin' || this.system.categorie == 'Repos'); }
getItemGroup() {
if (this.isInventaire()) return "equipement";
@@ -174,16 +177,43 @@ export class RdDItem extends Item {
return "autres";
}
isConteneurNonVide() {
return this.isConteneur() && (this.system.contenu?.length ?? 0) > 0;
isConteneurNonVide() { return this.isConteneur() && (this.system.contenu?.length ?? 0) > 0; }
isConteneurVide() { return this.isConteneur() && (this.system.contenu?.length ?? 0) == 0; }
isVideOuNonConteneur() { return !this.isConteneur() || (this.system.contenu?.length ?? 0) == 0; }
isFinPeriode(oldTimestamp, newTimestamp) {
if (!this.isTemporel()) {
return false;
}
const finPeriode = new RdDTimestamp(this.system.temporel.fin);
return oldTimestamp.compare(finPeriode) < 0 && finPeriode.compare(newTimestamp) <= 0
}
isConteneurVide() {
return this.isConteneur() && (this.system.contenu?.length ?? 0) == 0;
async onCreateItemTemporel(actor) {
if (this.isTemporel()) {
const timestampDebut = game.system.rdd.calendrier.timestamp;
const timestampFin = await this.calculerFinPeriodeTemporel(timestampDebut);
await actor.updateEmbeddedDocuments('Item', [{
_id: this.id,
'system.temporel.debut': duplicate(timestampDebut),
'system.temporel.fin': duplicate(timestampFin),
}])
}
}
isVideOuNonConteneur() {
return !this.isConteneur() || (this.system.contenu?.length ?? 0) == 0;
async calculerFinPeriodeTemporel(timestampDebut) {
return timestampDebut;
}
async onFinPeriodeTemporel(oldTimestamp, newTimestamp) {
if (this.isTemporel() && this.actor) {
await this.onFinPeriode(oldTimestamp, newTimestamp);
}
}
async onFinPeriode(oldTimestamp, newTimestamp) {
console.log(`${this.actor.name}: l'objet ${this.name} a expiré et été supprimé`);
await this.actor?.deleteEmbeddedDocuments('Item', [this.id]);
}
isComestible() {
@@ -197,16 +227,6 @@ export class RdDItem extends Item {
return '';
}
isAlcool() {
return this.isNourritureBoisson() && this.system.boisson && this.system.alcoolise;
}
isHerbeAPotion() {
return this.type == 'herbe' && (this.system.categorie == 'Soin' || this.system.categorie == 'Repos');
}
isPotion() {
return this.type == 'potion';
}
isCristalAlchimique() {
return this.type == 'objet' && Grammar.toLowerCaseNoAccent(this.name) == 'cristal alchimique' && this.system.quantite > 0;
}
@@ -215,6 +235,10 @@ export class RdDItem extends Item {
return this.system.magique
}
isItemCommerce() {
return this.parent?.type == 'commerce';
}
getQuantite() {
return this.isService() ? undefined : Math.round(this.system.quantite ?? 0)
}
@@ -252,10 +276,6 @@ export class RdDItem extends Item {
return this.system.cout ?? 0
}
isItemCommerce() {
return this.parent?.type == 'commerce';
}
calculerPrixCommercant() {
if (this.isItemCommerce()) {
// appliquer le pourcentage
@@ -376,40 +396,6 @@ export class RdDItem extends Item {
await item.delete();
}
async onCreateItemTemporel(actor) {
if (this.isTemporel()) {
const timestampDebut = game.system.rdd.calendrier.timestamp;
const timestampFin = await this.calculerFinPeriodeTemporel(timestampDebut);
await actor.updateEmbeddedDocuments('Item', [{
_id: this.id,
'system.temporel.debut': duplicate(timestampDebut),
'system.temporel.fin': duplicate(timestampFin),
}])
}
}
async calculerFinPeriodeTemporel(timestampDebut) {
return timestampDebut;
}
async prolongerPeriode() {
if (this.actor) {
const current = game.system.rdd.calendrier.timestamp;
const finPeriode = new RdDTimestamp(this.system.temporel.fin)
const periodeSuivante = (finPeriode.compare(current)>0 ? finPeriode : current);
const timestampFin = await this.calculerFinPeriodeTemporel(periodeSuivante);
await this.actor.updateEmbeddedDocuments('Item', [{
_id: this.id,
'system.temporel.fin': duplicate(timestampFin),
}])
}
}
async onFinTemporel() {
await this.actor?.deleteEmbeddedDocuments('Item', [this.id]);
}
async quantiteIncDec(nombre, options = { supprimerSiZero: false }) {
const quantite = Number(this.system.quantite ?? -1);
if (quantite >= 0) {

View File

@@ -1,4 +1,6 @@
import { RdDItem } from "../item.js";
import { Misc } from "../misc.js";
import { RdDTimestamp } from "../rdd-timestamp.js";
export class RdDItemMaladie extends RdDItem {
@@ -7,7 +9,39 @@ export class RdDItemMaladie extends RdDItem {
}
async calculerFinPeriodeTemporel(debut) {
return await debut.addPeriode(this.system.periode.nombre, this.system.periode.unite) ;
return await debut.addPeriode(this.system.periode.nombre, this.system.periode.unite);
}
async onFinPeriode(oldTimestamp, newTimestamp) {
await RdDItemMaladie.notifierMaladiePoison(this, oldTimestamp, newTimestamp)
}
static async notifierMaladiePoison(mal, oldTimestamp, newTimestamp) {
if (mal.actor) {
const souffrance = mal.system.identifie
? `de ${mal.name}`
: `d'un mal inconnu`
ChatMessage.create({ content: `${mal.actor.name} souffre ${souffrance} (${Misc.typeName('Item', mal.type)}): vérifiez que les effets ne se sont pas aggravés !` });
mal.postItemToChat('gmroll');
await RdDItemMaladie.prolongerPeriode(mal,oldTimestamp, newTimestamp);
}
}
static async prolongerPeriode(mal, oldTimestamp, newTimestamp) {
if (mal.actor) {
// TODO: déterminer le nombre de périodes écoulées
console.log(`${mal.actor.name}: le mal ${mal.name} a atteint la fin de sa période et été prolongé`);
const current = newTimestamp;
const finPeriode = new RdDTimestamp(mal.system.temporel.fin)
const periodeSuivante = (finPeriode.compare(current) > 0 ? finPeriode : current);
const timestampFin = await mal.calculerFinPeriodeTemporel(periodeSuivante);
await mal.actor.updateEmbeddedDocuments('Item', [{
_id: mal.id,
'system.temporel.fin': duplicate(timestampFin),
}])
}
}
}

View File

@@ -1,7 +1,6 @@
import { RdDItem } from "../item.js";
export class RdDItemOmbre extends RdDItem {
static get defaultIcon() {
return "systems/foundryvtt-reve-de-dragon/icons/queue_dragon.webp";
}
@@ -9,10 +8,4 @@ export class RdDItemOmbre extends RdDItem {
async calculerFinPeriodeTemporel(debut) {
return await debut.appliquerDuree(this.system.duree, this.parent);
}
async onFinTemporel() {
// fin de période
await this.prolongerPeriode();
}
}
}

View File

@@ -1,4 +1,5 @@
import { RdDItem } from "../item.js";
import { RdDItemMaladie } from "./maladie.js";
export class RdDItemPoison extends RdDItem {
@@ -10,8 +11,7 @@ export class RdDItemPoison extends RdDItem {
return await debut.addPeriode(this.system.periode.nombre, this.system.periode.unite) ;
}
async onFinTemporel() {
// fin de période
await this.prolongerPeriode();
async onFinPeriode(oldTimestamp, newTimestamp) {
RdDItemMaladie.notifierMaladiePoison(this, oldTimestamp, newTimestamp)
}
}

View File

@@ -44,7 +44,7 @@ export class RdDCalendrier extends Application {
this.listeNombreAstral = this.getListeNombreAstral();
this.rebuildListeNombreAstral(HIDE_DICE); // Ensure always up-to-date
}
console.log('RdDCalendrier.constructor()', this.timestamp, this.timestamp.toOldCalendrier(), this.calendrierPos, this.listeNombreAstral);
console.log('RdDCalendrier.constructor()', this.timestamp, this.timestamp.toCalendrier(), this.calendrierPos, this.listeNombreAstral);
Hooks.on('updateSetting', async (setting, update, options, id) => this.onUpdateSetting(setting, update, options, id));
}
@@ -253,65 +253,13 @@ export class RdDCalendrier extends Application {
/* -------------------------------------------- */
async setNewTimestamp(newTimestamp) {
this.checkMaladiePoison(this.timestamp, newTimestamp);
this.checkMaladie("round");
this.checkMaladie("minute");
if (this.timestamp.heure != newTimestamp.heure || this.timestamp.indexDate != newTimestamp.indexDate) {
this.checkMaladie("heure");
}
if (this.timestamp.indexDate != newTimestamp.indexDate) {
this.checkMaladie("jour");
}
game.actors.forEach(actor => actor.onTimeChanging(this.timestamp, newTimestamp));
RdDTimestamp.setWorldTime(newTimestamp);
this.timestamp = newTimestamp;
await this.rebuildListeNombreAstral();
this.updateDisplay();
}
/* -------------------------------------------- */
checkMaladie(periode) {
for (let actor of game.actors) {
if (actor.type == 'personnage') {
let maladies = actor.items.filter(item => (item.type == 'maladie' || (item.type == 'poison' && item.system.active)) && item.system.periodicite.toLowerCase().includes(periode));
for (let maladie of maladies) {
if (maladie.system.identifie) {
ChatMessage.create({ content: `${actor.name} souffre de ${maladie.name} (${maladie.type}): vérifiez que les effets ne se sont pas aggravés !` });
} else {
ChatMessage.create({ content: `${actor.name} souffre d'un mal inconnu (${maladie.type}): vérifiez que les effets ne se sont pas aggravés !` });
}
let itemMaladie = actor.getItem(maladie.id)
itemMaladie.postItemToChat('gmroll');
}
}
}
}
checkMaladiePoison(oldTimestamp, newTimestamp) {
// TODO
const isInPeriod = maladie => {
//TODO: utiliser les timestamp
return false;
}
game.actors.filter(it => it.type == 'personnage')
.forEach(actor => {
actor.items.filter(it => it.type == 'maladie' || (it.type == 'poison' && it.system.active))
.filter(m => isInPeriod(m))
.forEach(m => {
if (m.system.identifie) {
ChatMessage.create({ content: `${actor.name} souffre de ${m.name} (${m.type}): vérifiez que les effets ne se sont pas aggravés !` });
} else {
ChatMessage.create({ content: `${actor.name} souffre d'un mal inconnu (${m.type}): vérifiez que les effets ne se sont pas aggravés !` });
}
let itemMaladie = actor.getItem(m.id)
itemMaladie.postItemToChat('gmroll');
})
});
}
/* -------------------------------------------- */
async onCalendarButton(ev) {
ev.preventDefault();
@@ -343,30 +291,8 @@ export class RdDCalendrier extends Application {
/* -------------------------------------------- */
fillCalendrierData(formData = {}) {
const mois = RdDTimestamp.definition(this.timestamp.mois);
const heure = RdDTimestamp.definition(this.timestamp.heure);
formData.timestamp = this.timestamp;
formData.annee = this.timestamp.annee;
formData.mois = mois;
formData.jourDuMois = this.timestamp.jour + 1;
formData.jour = this.timestamp.jour;
formData.heure = heure;
formData.minute = this.timestamp.minute;
// formData.nomMois = mois.label; // heures et mois nommés identiques
// formData.iconMois = mois.icon;
// formData.heureKey = heure.key;
// formData.heureRdD = formData.heure.heure;
// formData.nomHeure = heure.label;
// formData.iconHeure = heure.icon;
// formData.minutes = this.timestamp.minute;
mergeObject(formData, this.timestamp.toCalendrier());
formData.isGM = game.user.isGM;
console.log('fillCalendrierData', this.timestamp, mois, heure, formData);
return formData;
}

View File

@@ -39,6 +39,12 @@ const FORMULES_DUREE = [
// { 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: 'rounds', label: "Rounds", calcul: async (t, nombre) => t.addMinutes(nombre / 10) },
{ code: 'minutes', label: "Minutes", calcul: async (t, nombre) => t.addMinutes(nombre) },
{ code: 'heures', label: "Heures", calcul: async (t, nombre) => t.addHeures(nombre) },
{ code: 'jours', label: "Jours", calcul: async (t, nombre) => t.addJours(nombre) },
]
export class RdDTimestamp {
@@ -80,6 +86,9 @@ export class RdDTimestamp {
static formulesDuree() {
return FORMULES_DUREE
}
static formulesPeriode() {
return FORMULES_PERIODE
}
static imgSigneHeure(heure) {
return RdDTimestamp.imgSigne(RdDTimestamp.definition(heure));
@@ -103,23 +112,9 @@ export class RdDTimestamp {
return undefined;
}
static signeHeure(key, value) {
const signe = RdDTimestamp.definition(value);
if (signe && ['key', 'webp', 'label', 'lettreFont', 'saison', 'heure', 'icon'].includes(key)) {
return signe[key];
}
console.error(`Appel à getSigneAs('${key}', ${value}) avec une clé/heure incorrects`);
return value;
}
static getCalendrier(indexDate, indexMinute = 0) {
return new RdDTimestamp({ indexDate, indexMinute }).toOldCalendrier();
}
/**
*
* @param indexMinute: la version formattée de la date
* @param indexDate: la date (depuis le jour 0)
* @return la version formattée de la date
*/
static formatIndexDate(indexDate) {
return new RdDTimestamp({ indexDate }).formatDate()
@@ -142,9 +137,9 @@ export class RdDTimestamp {
indexMinute: worldTime.heureRdD * 120 + worldTime.minutesRelative
};
RdDTimestamp.setWorldTime(new RdDTimestamp(worldTime))
}
return new RdDTimestamp(worldTime);
return new RdDTimestamp(worldTime);
}
static setWorldTime(timestamp) {
@@ -175,6 +170,11 @@ export class RdDTimestamp {
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,
@@ -187,21 +187,6 @@ export class RdDTimestamp {
};
}
/**
* Convertit un timestamp en donnée utile à l'affichage d'un calendrier
*/
toOldCalendrier() {
return {
indexJour: this.indexDate,
annee: this.annee,
moisRdD: this.mois,
jour: this.jour,
heureRdD: this.heure,
moisLabel: RdDTimestamp.definition(this.mois).label,
heureLabel: RdDTimestamp.definition(this.heure).label,
minutesRelative: 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 }
@@ -256,11 +241,12 @@ export class RdDTimestamp {
}
addPeriode(nombre, unite) {
switch (unite) {
case 'heures': return this.addHeures(nombre)
case 'minutes': return this.addMinutes(nombre)
case 'jours': return this.addJours(nombre)
case 'rounds': return this.addMinutes(nombre / 10)
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;
}
@@ -275,7 +261,10 @@ export class RdDTimestamp {
}
compare(timestamp) {
let diff = (this.indexDate - timestamp.indexDate) ?? (this.indexMinute - timestamp.indexMinute);
let diff = this.indexDate - timestamp.indexDate
if (diff == 0) {
diff = this.indexMinute - timestamp.indexMinute
}
return diff < 0 ? -1 : diff > 0 ? 1 : 0;
}

View File

@@ -199,9 +199,11 @@ export class RdDUtility {
'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-queue.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-draconic.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-tmr-type.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-periode.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-tmr-effet.html',
// Partials
'systems/foundryvtt-reve-de-dragon/templates/common/timestamp.hbs',
'systems/foundryvtt-reve-de-dragon/templates/common/periodicite.hbs',
'systems/foundryvtt-reve-de-dragon/templates/common/enum-duree.hbs',
'systems/foundryvtt-reve-de-dragon/templates/common/compendium-link.hbs',
'systems/foundryvtt-reve-de-dragon/templates/partial-description-overflow.html',
@@ -282,12 +284,12 @@ export class RdDUtility {
Handlebars.registerHelper('caseTmr-type', coord => TMRUtility.getTMRType(coord));
Handlebars.registerHelper('typeTmr-name', type => TMRUtility.typeTmrName(type));
Handlebars.registerHelper('effetRencontre-name', coord => TMRUtility.typeTmrName(coord));
// TODO: upgrade
Handlebars.registerHelper('signeHeure', (key, heure) => RdDTimestamp.signeHeure(key, heure));
Handlebars.registerHelper('timestamp-imgSigneHeure', (heure) => { return new Handlebars.SafeString(RdDTimestamp.imgSigneHeure(heure)) });
Handlebars.registerHelper('timestamp-imgSigne', (heure) => { return new Handlebars.SafeString(RdDTimestamp.imgSigne(heure)) });
Handlebars.registerHelper('timestamp-extract', timestamp => new RdDTimestamp(timestamp).toCalendrier());
Handlebars.registerHelper('timestamp-formulesDuree', () => RdDTimestamp.formulesDuree());
Handlebars.registerHelper('timestamp-formulesPeriode', () => RdDTimestamp.formulesPeriode());
Handlebars.registerHelper('min', (...args) => Math.min(...args.slice(0, -1)));
Handlebars.registerHelper('regle-optionnelle', (option) => ReglesOptionelles.isUsing(option));