feat(repos): nouveau mode sommeil avec sélection heures, dialogs CD intégrés, toolbar équipement
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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" },
|
||||
|
||||
@@ -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')
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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('<p class="notification error">Vous n\'avez pas assez de fatigue pour dormir de jour.</p>')
|
||||
return false
|
||||
}
|
||||
if (aHeuresJour && aFatigue) {
|
||||
$av.append('<p class="notification warning">Vous vous réveillerez dès la fatigue récupérée (sommeil de jour).</p>')
|
||||
}
|
||||
if (reveilForce && heuresForce < duree) {
|
||||
$av.append('<p class="notification info">Vous serez réveillé après ' + heuresForce + ' heures.</p>')
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,13 @@
|
||||
<span class="item-name">
|
||||
<h4>Argent et Monnaies (fortune: {{calc.fortune.sols}} sols {{calc.fortune.deniers}} deniers)</h4>
|
||||
<div class="inventaire-toolbar">
|
||||
<span class="inventaire-toolbar-row">
|
||||
<span class="fortune-text">Argent (fortune: {{calc.fortune.sols}} sols {{calc.fortune.deniers}} deniers)</span>
|
||||
</span>
|
||||
{{#if @root.options.isOwner}}
|
||||
<a class="gerer-deniers chat-card-button" data-tooltip="Gérer ses deniers">Gérer ses deniers</a>
|
||||
{{#if herbes.length}}<a class="gerer-herbes chat-card-button">Gérer ses herbes</a>{{/if}}
|
||||
{{#if ingredients.length}}<a class="gerer-ingredients chat-card-button">Gérer ses ingrédients</a>{{/if}}
|
||||
{{#if potions.length}}<a class="gerer-potions chat-card-button">Gérer ses potions</a>{{/if}}
|
||||
<span class="inventaire-toolbar-row">
|
||||
<a class="chat-card-button gerer-deniers" data-tooltip="Gérer ses deniers"><i class="fas fa-coins"></i> Deniers</a>
|
||||
{{#if potions.length}}<a class="chat-card-button gerer-potions" data-tooltip="Gérer ses potions"><i class="fas fa-flask"></i> Potions</a>{{/if}}
|
||||
{{#if herbes.length}}<a class="chat-card-button gerer-herbes" data-tooltip="Gérer ses herbes"><i class="fas fa-leaf"></i> Herbes</a>{{/if}}
|
||||
{{#if ingredients.length}}<a class="chat-card-button gerer-ingredients" data-tooltip="Gérer ses ingrédients"><i class="fas fa-mortar-pestle"></i> Ingrédients</a>{{/if}}
|
||||
</span>
|
||||
{{/if}}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -1,29 +1,33 @@
|
||||
<span class="item-name">
|
||||
<div class="inventaire-toolbar">
|
||||
{{#if options.isOwner}}
|
||||
<a class="chat-card-button creer-un-objet" data-tooltip="Créer un nouvel objet">Nouvel objet</a>
|
||||
<a class="chat-card-button quick-add-ration" data-tooltip="Ajouter une ration">+ Ration</a>
|
||||
<a class="chat-card-button quick-add-eau" data-tooltip="Ajouter une outre d'eau">+ Eau</a>
|
||||
{{/if}}
|
||||
{{#if options.isGM}}
|
||||
<a class="chat-card-button nettoyer-conteneurs" data-tooltip="Vider tous les contenants">Tout vider</a>
|
||||
{{/if}}
|
||||
<span class="embed-inline">
|
||||
<input class="recherche flex-grow" type="text" value="{{options.recherche.text}}" name="recherche" size="8" data-dtype="String" placeholder="" data-tooltip="Rechercher dans l'équipement"/>
|
||||
<select class="tri-equipement" data-tooltip="Trier par">
|
||||
{{#select options.triEquipement}}
|
||||
<option value="name">Nom</option>
|
||||
<option value="type">Type</option>
|
||||
<option value="enc">Enc.</option>
|
||||
<option value="quantite">Quantité</option>
|
||||
{{/select}}
|
||||
</select>
|
||||
<span class="inventaire-toolbar-row">
|
||||
<a class="chat-card-button creer-un-objet" data-tooltip="Créer un nouvel objet"><i class="fas fa-plus-circle"></i> Nouvel objet</a>
|
||||
<a class="chat-card-button quick-add-ration" data-tooltip="Ajouter une ration"><i class="fas fa-bread-slice"></i> + Ration</a>
|
||||
<a class="chat-card-button quick-add-eau" data-tooltip="Ajouter une outre d'eau"><i class="fas fa-tint"></i> + Eau</a>
|
||||
{{#if options.isGM}}
|
||||
<a class="chat-card-button nettoyer-conteneurs" data-tooltip="Vider tous les contenants"><i class="fas fa-trash-alt"></i> Tout vider</a>
|
||||
{{/if}}
|
||||
</span>
|
||||
{{#if calc.surEncombrementMessage}}<b>{{calc.surEncombrementMessage}}</b> ‐{{/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}}
|
||||
</span>
|
||||
<span class="inventaire-toolbar-row">
|
||||
<span class="embed-inline">
|
||||
<input class="recherche flex-grow" type="text" value="{{options.recherche.text}}" name="recherche" size="8" data-dtype="String" placeholder="" data-tooltip="Rechercher dans l'équipement"/>
|
||||
<select class="tri-equipement" data-tooltip="Trier par">
|
||||
{{#select options.triEquipement}}
|
||||
<option value="name">Nom</option>
|
||||
<option value="type">Type</option>
|
||||
<option value="enc">Enc.</option>
|
||||
<option value="quantite">Quantité</option>
|
||||
{{/select}}
|
||||
</select>
|
||||
</span>
|
||||
{{#if calc.surEncombrementMessage}}<b>{{calc.surEncombrementMessage}}</b> ‐{{/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}}
|
||||
</span>
|
||||
</div>
|
||||
<ul class="item-list alterne-list liste-equipement">
|
||||
<li class="competence-header flexrow">
|
||||
<span class="equipement-nom">Nom</span>
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<form class="dialog-nouveau-repos-cd">
|
||||
<div class="flexcol">
|
||||
<h2>Château Dormant — {{name}}</h2>
|
||||
<p>La nuit touche à sa fin. Renseignez les informations pour le passage à Vaisseau.</p>
|
||||
<div class="form-group">
|
||||
<label>Points de stress du jour</label>
|
||||
<input type="number" name="cd-stress" value="0" min="0" max="200">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Condition du jet de moral</label>
|
||||
<div class="sommeil-actor-moral">
|
||||
{{> "systems/foundryvtt-reve-de-dragon/templates/sommeil/sommeil-actor-moral.hbs" this}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@@ -0,0 +1,28 @@
|
||||
<form class="dialog-nouveau-repos">
|
||||
<div class="flexcol">
|
||||
<div class="flexrow">
|
||||
<img class="chat-icon" src="{{img}}" data-tooltip="{{name}}" />
|
||||
<h2>{{name}} se repose</h2>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>De l'heure</label>
|
||||
<select name="heure-debut">
|
||||
{{selectOptions hourDefinitions selected=heureDebutDefaut labelAttr='label' valueAttr='heure'}}
|
||||
</select>
|
||||
<label>à l'heure</label>
|
||||
<select name="heure-fin">
|
||||
{{selectOptions hourDefinitions selected=heureFinDefaut labelAttr='label' valueAttr='heure'}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<span>Durée : <strong><span id="duree-sommeil">0</span> heures</strong></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="checkbox" name="reveil-force" id="reveil-force">
|
||||
<label for="reveil-force">Réveil forcé après</label>
|
||||
<input type="number" name="heures-force" value="1" min="1" max="12" disabled>
|
||||
<span>heures</span>
|
||||
</div>
|
||||
<div id="avertissements" class="hidden"></div>
|
||||
</div>
|
||||
</form>
|
||||
Reference in New Issue
Block a user