diff --git a/css/foundryvtt-reve-de-dragon.css b/css/foundryvtt-reve-de-dragon.css index ca25cd53..fc498b82 100644 --- a/css/foundryvtt-reve-de-dragon.css +++ b/css/foundryvtt-reve-de-dragon.css @@ -5263,6 +5263,23 @@ body { .system-foundryvtt-reve-de-dragon .conteneur-parent-badge::after { content: ")"; } +.system-foundryvtt-reve-de-dragon .inventaire-toolbar { + display: flex; + flex-direction: column; + gap: 0.3rem; + margin-bottom: 0.5rem; +} +.system-foundryvtt-reve-de-dragon .inventaire-toolbar-row { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.25rem; +} +.system-foundryvtt-reve-de-dragon .fortune-text { + font-weight: bold; + font-size: 0.9rem; + white-space: nowrap; +} .system-foundryvtt-reve-de-dragon .blessure-control { flex-grow: 1; flex-direction: row; diff --git a/less/foundryvtt-reve-de-dragon.less b/less/foundryvtt-reve-de-dragon.less index da676817..20cc20fc 100644 --- a/less/foundryvtt-reve-de-dragon.less +++ b/less/foundryvtt-reve-de-dragon.less @@ -486,6 +486,24 @@ } } + .inventaire-toolbar { + display: flex; + flex-direction: column; + gap: 0.3rem; + margin-bottom: 0.5rem; + } + .inventaire-toolbar-row { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.25rem; + } + .fortune-text { + font-weight: bold; + font-size: 0.9rem; + white-space: nowrap; + } + .blessure-control { flex-grow: 1; flex-direction: row; diff --git a/module/settings/regles-optionnelles.js b/module/settings/regles-optionnelles.js index cd8fe8dc..26dd897a 100644 --- a/module/settings/regles-optionnelles.js +++ b/module/settings/regles-optionnelles.js @@ -9,6 +9,7 @@ const listeReglesOptionnelles = [ { group: 'Récupération', name: 'recuperation-ethylisme', descr: "Récupérer l'éthylisme"}, { group: 'Récupération', name: 'recuperation-reve', descr: "Récupérer le rêve pendant la nuit (les jets sont toujours faits pour les Rêves de Dragons)"}, { group: 'Récupération', name: 'recuperation-moral', descr: "Le moral revient vers 0 durant Château Dormant"}, + { group: 'Récupération', name: 'nouveau-mode-repos', descr: "Utiliser le nouveau mode de repos (sélection heures + dialogue Château Dormant intégré)", default: false}, { group: 'Règles de combat', name: 'armes-equipees', descr: "Proposer uniquement les armes équipées au combat", default: false}, { group: 'Règles de combat', name: 'localisation-aleatoire', descr: "Proposer une localisation aléatoire des blessures" }, diff --git a/module/sommeil/dialog-nouveau-repos-cd.js b/module/sommeil/dialog-nouveau-repos-cd.js new file mode 100644 index 00000000..9b4603b5 --- /dev/null +++ b/module/sommeil/dialog-nouveau-repos-cd.js @@ -0,0 +1,46 @@ +import { renderTemplate } from "../constants.js" + +export class DialogNouveauReposCD { + static prompt(actor) { + return new Promise((resolve) => { + const date = game.system.rdd.calendrier.dateCourante() + let moralChoisi = actor.system.sommeil?.moral ?? 'neutre' + const data = { name: actor.name, moral: moralChoisi } + + renderTemplate( + "systems/foundryvtt-reve-de-dragon/templates/sommeil/dialog-nouveau-repos-cd.hbs", + data + ).then(async (html) => { + const dialog = new Dialog({ + title: "Château Dormant — " + actor.name, + content: html, + buttons: { + valider: { + label: "Valider le Château Dormant", + callback: ($html) => { + const stress = Number($html.find('input[name="cd-stress"]').val()) || 0 + resolve({ + stress: { valeur: stress, motif: `Nuit du ${date}` }, + moral: moralChoisi + }) + } + } + }, + close: () => resolve({ stress: { valeur: 0, motif: `Nuit du ${date}` }, moral: 'neutre' }) + }, { + width: 400, + height: 'fit-content', + 'z-index': 99999 + }) + await dialog.render(true) + const $win = $(`#${dialog.id}`) + $win.find('.sommeil-actor-moral a').click((event) => { + const target = $(event.currentTarget) + moralChoisi = target.data('moral') + $win.find('.sommeil-actor-moral a i').removeClass('fa-solid').addClass('fa-regular') + target.find('i').removeClass('fa-regular').addClass('fa-solid') + }) + }) + }) + } +} diff --git a/module/sommeil/dialog-nouveau-repos.js b/module/sommeil/dialog-nouveau-repos.js new file mode 100644 index 00000000..940133e2 --- /dev/null +++ b/module/sommeil/dialog-nouveau-repos.js @@ -0,0 +1,196 @@ +import { renderTemplate } from "../constants.js" +import { RdDTimestamp, RDD_HEURES_PAR_JOUR } from "../time/rdd-timestamp.js" +import { ChatUtility } from "../chat-utility.js" +import { DialogNouveauReposCD } from "./dialog-nouveau-repos-cd.js" + +export class DialogNouveauRepos extends Dialog { + static async create(actor) { + if (!actor.isPersonnage()) return + + const now = game.system.rdd.calendrier.timestamp + const heureCourante = now.heure + const hourDefs = RdDTimestamp.definitions() + + const data = { + name: actor.name, + img: actor.img, + hourDefinitions: hourDefs, + heureDebutDefaut: heureCourante, + heureFinDefaut: Math.min(heureCourante + 7, 11), + } + + const html = await renderTemplate( + "systems/foundryvtt-reve-de-dragon/templates/sommeil/dialog-nouveau-repos.hbs", + data + ) + + new DialogNouveauRepos(html, actor).render(true) + } + + constructor(html, actor) { + super({ + title: "Se reposer — " + actor.name, + content: html, + buttons: { + dormir: { label: "Dormir", callback: () => this._onDormir() }, + chateauDormant: { label: "Passer Château Dormant", callback: () => this._onChateauDormant() } + } + }, { + classes: ["dialog-nouveau-repos"], + width: 450, + height: 'fit-content', + 'z-index': 99999 + }) + this.actor = actor + } + + activateListeners($html) { + super.activateListeners($html) + this.$html = $html + this.$html.find('select[name="heure-debut"], select[name="heure-fin"]').change(() => this._updateDuree()) + this.$html.find('input[name="reveil-force"]').change(() => this._toggleReveilForce()) + this._updateDuree() + } + + _updateDuree() { + const debut = Number(this.$html.find('select[name="heure-debut"]').val()) + const fin = Number(this.$html.find('select[name="heure-fin"]').val()) + const duree = fin >= debut ? fin - debut + 1 : (RDD_HEURES_PAR_JOUR - debut) + fin + 1 + this.$html.find('#duree-sommeil').text(duree) + } + + _toggleReveilForce() { + const checked = this.$html.find('input[name="reveil-force"]').is(':checked') + this.$html.find('input[name="heures-force"]').prop('disabled', !checked) + } + + async _onDormir() { + const debut = Number(this.$html.find('select[name="heure-debut"]').val()) + const fin = Number(this.$html.find('select[name="heure-fin"]').val()) + const reveilForce = this.$html.find('input[name="reveil-force"]').is(':checked') + const heuresForce = Number(this.$html.find('input[name="heures-force"]').val()) + const duree = fin >= debut ? fin - debut + 1 : (RDD_HEURES_PAR_JOUR - debut) + fin + 1 + + const valide = await this._valider(debut, duree, reveilForce, heuresForce) + if (!valide) return + + this.close() + await this._executerSommeil(debut, duree, reveilForce, heuresForce) + } + + async _onChateauDormant() { + this.close() + const cdData = await DialogNouveauReposCD.prompt(this.actor) + if (cdData.stress.valeur > 0) { + await this.actor.distribuerStress('stress', cdData.stress.valeur, cdData.stress.motif) + } + await this.actor.update({ + 'system.sommeil': { + nouveaujour: true, + date: game.system.rdd.calendrier.getTimestampFinChateauDormant(), + moral: cdData.moral, + heures: 0 + } + }) + await this.actor.$dormirChateauDormant() + } + + async _valider(debut, duree, reveilForce, heuresForce) { + const $av = this.$html.find('#avertissements') + $av.empty().show() + + const fatigue = this.actor.system.sante.fatigue.value + const fatigueMin = this.actor.getFatigueMin() + const aFatigue = fatigue > fatigueMin + const aHeuresJour = this._aHeuresJour(debut, duree) + + if (aHeuresJour && !aFatigue) { + $av.html('

Vous n\'avez pas assez de fatigue pour dormir de jour.

') + return false + } + if (aHeuresJour && aFatigue) { + $av.append('

Vous vous réveillerez dès la fatigue récupérée (sommeil de jour).

') + } + if (reveilForce && heuresForce < duree) { + $av.append('

Vous serez réveillé après ' + heuresForce + ' heures.

') + } + return true + } + + _aHeuresJour(debut, duree) { + for (let h = 0; h < duree; h++) { + const heure = (debut + h) % RDD_HEURES_PAR_JOUR + if (heure > 0 && heure < 6) return true + } + return false + } + + async _executerSommeil(heureDebut, duree, reveilForce, heuresForce) { + const message = { + whisper: ChatUtility.getOwners(this.actor), + content: this.actor.name + ': ' + } + const dormi = { heures: 0, etat: 'dort', jetsReve: [] } + const insomnie = this.actor.system.sommeil?.insomnie || duree === 0 + + await this.actor.recupereEndurance({ message, demi: insomnie }) + + if (insomnie) { + message.content += 'Vous ne trouvez pas le sommeil' + } else { + const avaitFatigue = this.actor.system.sante.fatigue.value > this.actor.getFatigueMin() + if (!avaitFatigue && heureDebut >= 6 && heureDebut <= 11) { + duree = RDD_HEURES_PAR_JOUR - heureDebut + 1 + } + let cdProcessed = false + let h = 0 + + while (true) { + if (dormi.etat !== 'dort') break + if (reveilForce && dormi.heures >= heuresForce) break + if (!avaitFatigue && h >= duree) break + if (avaitFatigue && this.actor.system.sante.fatigue.value <= this.actor.getFatigueMin()) break + + const heureActuelle = (heureDebut + h) % RDD_HEURES_PAR_JOUR + + await this.actor.$recupererEthylisme(message) + await this.actor.$recupererFatigue(message) + await this.actor.$jetRecuperationReve(dormi, message) + dormi.heures++ + + if (dormi.etat !== 'dort') break + + if (heureActuelle === RDD_HEURES_PAR_JOUR - 1 && !cdProcessed) { + cdProcessed = true + const cdData = await DialogNouveauReposCD.prompt(this.actor) + if (cdData.stress.valeur > 0) { + await this.actor.distribuerStress('stress', cdData.stress.valeur, cdData.stress.motif) + } + await this.actor.update({ + 'system.sommeil': { + nouveaujour: true, + date: game.system.rdd.calendrier.getTimestampFinChateauDormant(), + moral: cdData.moral, + heures: Math.max(0, duree - h - 1) + } + }) + await this.actor.$dormirChateauDormant() + } + + h++ + } + + if (dormi.jetsReve.length > 0) { + message.content += 'Vous récupérez ' + + dormi.jetsReve.map(it => it < 0 ? '0 (réveil)' : it).join('+') + + ' Points de rêve. ' + } + if (dormi.etat === 'eveil') { + await this.actor.reveilReveDeDragon(message, dormi.heures) + } + message.content += `Vous avez dormi ${dormi.heures <= 1 ? 'une heure' : (dormi.heures + ' heures')}. ` + } + + await ChatMessage.create(message) + } +} diff --git a/templates/actor/inventaire-monnaie.hbs b/templates/actor/inventaire-monnaie.hbs index 25048d55..4b0c2b30 100644 --- a/templates/actor/inventaire-monnaie.hbs +++ b/templates/actor/inventaire-monnaie.hbs @@ -1,9 +1,13 @@ - -

Argent et Monnaies (fortune: {{calc.fortune.sols}} sols {{calc.fortune.deniers}} deniers)

+
+ + Argent (fortune: {{calc.fortune.sols}} sols {{calc.fortune.deniers}} deniers) + {{#if @root.options.isOwner}} - Gérer ses deniers - {{#if herbes.length}}Gérer ses herbes{{/if}} - {{#if ingredients.length}}Gérer ses ingrédients{{/if}} - {{#if potions.length}}Gérer ses potions{{/if}} + + Deniers + {{#if potions.length}} Potions{{/if}} + {{#if herbes.length}} Herbes{{/if}} + {{#if ingredients.length}} Ingrédients{{/if}} + {{/if}} - +
diff --git a/templates/actor/inventaire.hbs b/templates/actor/inventaire.hbs index bfb19e0d..8a108973 100644 --- a/templates/actor/inventaire.hbs +++ b/templates/actor/inventaire.hbs @@ -1,29 +1,33 @@ - +
{{#if options.isOwner}} - Nouvel objet - + Ration - + Eau - {{/if}} - {{#if options.isGM}} - Tout vider - {{/if}} - - - + + Nouvel objet + + Ration + + Eau + {{#if options.isGM}} + Tout vider + {{/if}} - {{#if calc.surEncombrementMessage}}{{calc.surEncombrementMessage}} ‐{{/if}} - Encombrement: {{numberFormat calc.encTotal decimals=2}} (max: {{system.attributs.encombrement.value}}) - {{#if (regle-optionnelle 'afficher-prix-joueurs')}} - ‐ Valeur: {{numberFormat calc.prixTotalEquipement decimals=2}} Sols {{/if}} - + + + + + + {{#if calc.surEncombrementMessage}}{{calc.surEncombrementMessage}} ‐{{/if}} + Encombrement: {{numberFormat calc.encTotal decimals=2}} (max: {{system.attributs.encombrement.value}}) + {{#if (regle-optionnelle 'afficher-prix-joueurs')}} + ‐ Valeur: {{numberFormat calc.prixTotalEquipement decimals=2}} Sols + {{/if}} + +