diff --git a/changelog.md b/changelog.md index 12c5d0fd..a585d0dd 100644 --- a/changelog.md +++ b/changelog.md @@ -1,4 +1,7 @@ # 11.2 +## 11.2.13 - Les cent pas d'Akarlikarlikar +- Ajout de la commande /voyage pour gérer la fatigue de marche des voyageurs + ## 11.2.12 - Le somnifère d'Akarlikarlikar - Fix: les potions enchantées n'empêchent plus de finir correctement Château Dormant diff --git a/module/rdd-combat.js b/module/rdd-combat.js index fbdfa154..32887eed 100644 --- a/module/rdd-combat.js +++ b/module/rdd-combat.js @@ -1307,6 +1307,7 @@ export class RdDCombat { blessuresStatus: actor.computeResumeBlessure(), SConst: actor.getSConst(), actorId: actor.id, + actor: actor, tokenId: tokenId, isGrave: actor.countBlessures(it => it.isGrave()) > 0, isCritique: actor.countBlessures(it => it.isCritique()) > 0 diff --git a/module/rdd-commands.js b/module/rdd-commands.js index 74dd1e61..b907c0ce 100644 --- a/module/rdd-commands.js +++ b/module/rdd-commands.js @@ -16,6 +16,7 @@ import { RdDRollTables } from "./rdd-rolltables.js"; import { RdDUtility } from "./rdd-utility.js"; import { FenetreRechercheTirage } from "./tirage/fenetre-recherche-tirage.js"; import { TMRUtility } from "./tmr-utility.js"; +import { DialogFatigueVoyage } from "./voyage/dialog-fatigue-voyage.js"; const rddRollNumeric = /^(\d+)\s*([\+\-]?\d+)?\s*(s)?/; @@ -76,6 +77,7 @@ export class RdDCommands { this.registerCommand({ path: ["/tirer", "desir"], func: (content, msg, params) => RdDRollTables.getDesirLancinant('chat'), descr: "Tire un Désir Lancinant" }); this.registerCommand({ path: ["/tirer", "rencontre"], func: (content, msg, params) => this.getRencontreTMR(params), descr: `Détermine une rencontre dans les TMR (synonyme de "/tmrr")` }); this.registerCommand({ path: ["/tirage"], func: (content, msg, params) => this.tirage(), descr: "Ouvre la fenêtre de recherche et tirage" }); + this.registerCommand({ path: ["/voyage"], func: (content, msg, params) => this.voyage(msg, params), descr: "Gérer le voyage" }); this.registerCommand({ path: ["/sommeil"], func: (content, msg, params) => this.sommeil(msg, params), descr: "Prépare le passage de journée pour chateau dormant" }); this.registerCommand({ path: ["/meteo"], func: (content, msg, params) => this.getMeteo(msg, params), descr: "Propose une météo marine" }); @@ -485,10 +487,13 @@ export class RdDCommands { } async tirage() { - FenetreRechercheTirage.create(); + FenetreRechercheTirage.create() + } + async voyage() { + DialogFatigueVoyage.create() } async sommeil() { - DialogChateauDormant.create(); + DialogChateauDormant.create() } } diff --git a/module/rdd-utility.js b/module/rdd-utility.js index 5baefcfe..44068635 100644 --- a/module/rdd-utility.js +++ b/module/rdd-utility.js @@ -63,12 +63,6 @@ const cumulFatigueMatrix = _cumulSegmentsFatigue(fatigueMatrix); const fatigueMalus = [0, 0, 0, -1, -1, -1, -2, -3, -4, -5, -6, -7]; // Provides the malus for each segment of fatigue const fatigueLineSize = [3, 6, 7, 8, 9, 10, 11, 12]; const fatigueLineMalus = [0, -1, -2, -3, -4, -5, -6, -7]; -const fatigueMarche = { - "aise": { "4": 1, "6": 2, "8": 3, "10": 4, "12": 6 }, - "malaise": { "4": 2, "6": 3, "8": 4, "10": 6 }, - "difficile": { "4": 3, "6": 4, "8": 6 }, - "tresdifficile": { "4": 4, "6": 6 } -} /* -------------------------------------------- */ const nomEthylisme = ["Emeché", "Gris", "Pinté", "Pas frais", "Ivre", "Bu", "Complètement fait", "Ivre mort"]; @@ -208,6 +202,8 @@ export class RdDUtility { 'systems/foundryvtt-reve-de-dragon/templates/coeur/afficher-coeur.hbs', 'systems/foundryvtt-reve-de-dragon/templates/tirage/liste-resultats-recherche.hbs', 'systems/foundryvtt-reve-de-dragon/templates/time/horloge.hbs', + 'systems/foundryvtt-reve-de-dragon/templates/voyage/fatigue-actor.hbs', + 'systems/foundryvtt-reve-de-dragon/templates/voyage/option-vitesse-fatigue.hbs', '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', @@ -288,6 +284,7 @@ export class RdDUtility { Handlebars.registerHelper('timestamp-formulesDuree', () => RdDTimestamp.formulesDuree()); Handlebars.registerHelper('timestamp-formulesPeriode', () => RdDTimestamp.formulesPeriode()); + Handlebars.registerHelper('array-includes', (array, value) => array.includes(value)); Handlebars.registerHelper('min', (...args) => Math.min(...args.slice(0, -1))); Handlebars.registerHelper('regle-optionnelle', (option) => ReglesOptionnelles.isUsing(option)); Handlebars.registerHelper('trier', list => list.sort((a, b) => a.name.localeCompare(b.name))); diff --git a/module/sommeil/app-astrologie.js b/module/sommeil/app-astrologie.js index 8f7e1e20..d993eaf7 100644 --- a/module/sommeil/app-astrologie.js +++ b/module/sommeil/app-astrologie.js @@ -116,10 +116,10 @@ export class AppAstrologie extends Application { super.activateListeners(html); this.html = html; this.html.find('select[name="signe-astral"]').change(event => { - this.selectNombreAstral(this.html.find('select[name="signe-astral"]').val()); + this.selectNombreAstral(event.currentTarget.value); }) this.html.find('select[name="signe-naissance"]').change(event => { - this.selectHeureNaissance(this.html.find('select[name="signe-naissance"]').val()); + this.selectHeureNaissance(event.currentTarget.value); }) this.html.find('td.nombre-astral').click(event => { this.selectNombreAstral(Number.parseInt(event.currentTarget.attributes['data-nombre-astral'].value) - 1); diff --git a/module/sommeil/dialog-repos.js b/module/sommeil/dialog-repos.js index d895c8ee..406914c8 100644 --- a/module/sommeil/dialog-repos.js +++ b/module/sommeil/dialog-repos.js @@ -21,7 +21,7 @@ export class DialogRepos extends Dialog { } constructor(html, actor) { - let options = { classes: ["DialogCreateSigneDraconiqueActorsActors"], width: 400, height: 'fit-content', 'z-index': 99999 }; + let options = { classes: ["dialog-repos"], width: 400, height: 'fit-content', 'z-index': 99999 }; let conf = { title: "Se reposer", content: html, diff --git a/module/voyage/dialog-fatigue-voyage.js b/module/voyage/dialog-fatigue-voyage.js new file mode 100644 index 00000000..a874d1fc --- /dev/null +++ b/module/voyage/dialog-fatigue-voyage.js @@ -0,0 +1,182 @@ +import { TYPES } from "../item.js" +import { RdDItemCompetence } from "../item-competence.js" +import { ChatUtility } from "../chat-utility.js" + +const CODES_COMPETENCES_VOYAGE = ['Extérieur', 'Forêt', 'Montagne', 'Marais', 'Glace', 'Equitation'] +const TABLEAU_FATIGUE_MARCHE = [ + { + code: "aise", label: "Aisé", description: "Route ou chemin", + survies: ['Extérieur', 'Equitation'], + vitesses: [{ vitesse: 4, fatigue: 1 }, { vitesse: 6, fatigue: 2 }, { vitesse: 8, fatigue: 3 }, { vitesse: 10, fatigue: 4 }, { vitesse: 12, fatigue: 6 }], + }, + { + code: "malaise", label: "Malaisé", description: "Hors piste (herbes et buissons)", + survies: ['Extérieur', 'Equitation'], + vitesses: [{ vitesse: 4, fatigue: 2 }, { vitesse: 6, fatigue: 3 }, { vitesse: 8, fatigue: 4 }, { vitesse: 10, fatigue: 6 }], + }, + { + code: "difficile", label: "Difficile", description: "Hors piste (collines, forêt)", + survies: ['Extérieur', 'Forêt', 'Glace', 'Equitation'], + vitesses: [{ vitesse: 4, fatigue: 3 }, { vitesse: 6, fatigue: 4 }, { vitesse: 8, fatigue: 6 }], + }, + { + code: "tresdifficile", label: "Très difficile", description: "Hors piste (montagne, jungle, marais)", + survies: ['Forêt', 'Montagne', 'Marais', 'Glace'], + vitesses: [{ vitesse: 4, fatigue: 4 }, { vitesse: 6, fatigue: 6 }], + }, +] + +export class DialogFatigueVoyage extends Dialog { + static dialog = undefined + static async create() { + if (!game.user.isGM) { + return + } + if (!DialogFatigueVoyage.dialog) { + const playerActors = game.actors.filter(actor => actor.hasPlayerOwner && actor.isPersonnage()) + .map(actor => DialogFatigueVoyage.prepareActor(actor)) + const parameters = { + tableauFatigueMarche: TABLEAU_FATIGUE_MARCHE, + playerActors: playerActors, + nombreHeures: 1, + } + DialogFatigueVoyage.setModeDeplacement(parameters, undefined, undefined) + + const html = await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/voyage/dialog-fatigue-voyage.hbs", parameters); + DialogFatigueVoyage.dialog = new DialogFatigueVoyage(html, parameters); + } + DialogFatigueVoyage.dialog.render(true); + } + + static setModeDeplacement(parameters, code, vitesse) { + const ligneFatigueMarche = TABLEAU_FATIGUE_MARCHE.find(it => it.code == code) ?? TABLEAU_FATIGUE_MARCHE[0] + const rythme = ligneFatigueMarche.vitesses.find(it => it.vitesse == vitesse) ?? ligneFatigueMarche.vitesses[0] + parameters.typeTerrain = ligneFatigueMarche + parameters.vitesseDeplacement = rythme.vitesse + parameters.fatigueHoraire = rythme.fatigue + } + + static prepareActor(actor) { + const competencesVoyage = {} + CODES_COMPETENCES_VOYAGE.forEach(codeSurvie => + competencesVoyage[codeSurvie] = RdDItemCompetence.findCompetence(actor.itemTypes[TYPES.competence], codeSurvie, { onMessage: () => { } }) + ) + return { + actor: actor, + selected: true, + ajustementFatigue: 0, + competencesVoyage: competencesVoyage + } + } + + + constructor(html, parameters) { + const options = { + classes: ["dialog-fatigue-voyage"], + width: 600, + height: 'fit-content', + 'max-height': 900, + 'z-index': 99999 + } + const conf = { + title: "Fatigue de voyage", + content: html, + buttons: {} + } + super(conf, options); + this.parameters = parameters + this.controls = {} + } + + activateListeners(html) { + if (this.html == undefined) { + html.find('select[name="code-terrain"]').trigger("focus") + } + this.html = html; + super.activateListeners(html); + + this.html.find('select[name="code-terrain"]').change(event => this.changeParameters()) + this.html.find('select[name="vitesse-deplacement"]').change(event => this.changeParameters()) + this.html.find('input[name="nombre-heures"]').change(event => this.changeParameters()) + this.html.find('button[name="appliquer-fatigue"]').click(event => this.appliquerFatigue()) + } + + changeParameters() { + this.changeTerrain(this.html.find('select[name="code-terrain"]').val()) + this.changeVitesse(this.html.find('select[name="vitesse-deplacement"]').val()) + this.changeNombreHeures(this.html.find('input[name="nombre-heures"]').val()) + this.setFatigue() + } + + async changeTerrain(codeTerrain) { + if (this.parameters.typeTerrain.code != codeTerrain) { + const selectVitesseDeplacement = this.html.find('select[name="vitesse-deplacement"]') + const vitesse = selectVitesseDeplacement.val() + selectVitesseDeplacement.empty() + + DialogFatigueVoyage.setModeDeplacement(this.parameters, codeTerrain, vitesse) + this.parameters.typeTerrain.vitesses.forEach(async rythme => { + selectVitesseDeplacement.append(await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/voyage/option-vitesse-fatigue.hbs', rythme)) + }) + selectVitesseDeplacement.val(this.parameters.vitesseDeplacement).change() + } + } + + async changeVitesse(vitesse) { + if (this.parameters.vitesseDeplacement != vitesse) { + DialogFatigueVoyage.setModeDeplacement(this.parameters, this.parameters.typeTerrain.code, vitesse) + } + } + + async changeNombreHeures(nombreHeures) { + this.parameters.nombreHeures = parseInt(nombreHeures) + } + + async setFatigue() { + this.html.find('input[name="base-fatigue"]').val(this.parameters.nombreHeures * this.parameters.fatigueHoraire) + } + + async appliquerFatigue() { + const fatigueBase = parseInt(this.html.find('input[name="base-fatigue"]').val() ?? 0) + const actors = jQuery.map( + this.html.find('div.fatigue-actors-list li.list-item'), + it => this.$extractActor(this.html.find(it)) + ) + actors.filter(it => it.selected) + .forEach(async it => { + const perteFatigue = fatigueBase + it.ajustement + ChatMessage.create({ + whisper: ChatUtility.getWhisperRecipientsAndGMs(it.actor.name), + content: await renderTemplate( + 'systems/foundryvtt-reve-de-dragon/templates/voyage/chat-fatigue_voyage.hbs', mergeObject(it, + { + parameters: this.parameters, + fatigueBase: fatigueBase, + perteFatigue: perteFatigue, + isVoyage: fatigueBase == this.parameters.nombreHeures * this.parameters.fatigueHoraire + }) + ), + }) + await it.actor.santeIncDec("fatigue", perteFatigue) + }) + } + + $extractActor(actorRow) { + const actor = game.actors.get(actorRow.data('actor-id')) + if (!actor) { + ui.notifications.warn(`Acteur ${it.actorId} introuvable`) + } + return { + actor: actor, + ajustement: parseInt(actorRow.find('input[name="ajustement-fatigue"]').val() ?? 0), + selected: actor && actorRow.find('input[name="selectionner-acteur"]').is(':checked') + } + } + + async close() { + DialogFatigueVoyage.dialog = undefined + await super.close() + } + + +} \ No newline at end of file diff --git a/styles/simple.css b/styles/simple.css index 619b839e..d13871b1 100644 --- a/styles/simple.css +++ b/styles/simple.css @@ -440,7 +440,13 @@ table {border: 1px solid #7a7971;} .flex-grow-2 { flex-grow: 2; } +.voyage-liste-survies { + max-width: 12rem; +} /* Styles limited to foundryvtt-reve-de-dragon sheets */ +.texte-dans-liste { + text-align: left; +} .equipement-nom { flex-grow : 4; margin: 0; @@ -1168,7 +1174,7 @@ ul.chat-list li:nth-child(odd) { border-radius: 0.25rem; padding: 0.1rem; flex: 1 1 1.5rem; - display: flex !important; + display: flex; align-items: center !important; } diff --git a/system.json b/system.json index bd83e1cf..8998132b 100644 --- a/system.json +++ b/system.json @@ -1,8 +1,8 @@ { "id": "foundryvtt-reve-de-dragon", "title": "Rêve de Dragon", - "version": "11.2.12", - "download": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/archive/foundryvtt-reve-de-dragon-11.2.12.zip", + "version": "11.2.13", + "download": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/archive/foundryvtt-reve-de-dragon-11.2.13.zip", "manifest": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/raw/v11/system.json", "changelog": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/raw/branch/v11/changelog.md", "compatibility": { diff --git a/templates/chat-actor-turn-acteur.hbs b/templates/chat-actor-turn-acteur.hbs index ad174c7f..665f5473 100644 --- a/templates/chat-actor-turn-acteur.hbs +++ b/templates/chat-actor-turn-acteur.hbs @@ -1 +1,2 @@ +
{{actor.name}} s'est fatigué de {{perteFatigue}} +{{#if (and isVoyage (gt parameters.nombreHeures 0))}} +après {{parameters.nombreHeures}} heure{{#if (gt parameters.nombreHeures 1)}}s{{/if}} +de voyage {{lowerFirst parameters.typeTerrain.label}} +à {{parameters.vitesseDeplacement}} km/hdr (Fatigue par heure: {{parameters.fatigueHoraire}}) +
+{{parameters.typeTerrain.description}} +{{/if}} +
diff --git a/templates/voyage/dialog-fatigue-voyage.hbs b/templates/voyage/dialog-fatigue-voyage.hbs new file mode 100644 index 00000000..4dbd1f9a --- /dev/null +++ b/templates/voyage/dialog-fatigue-voyage.hbs @@ -0,0 +1,58 @@ + \ No newline at end of file diff --git a/templates/voyage/fatigue-actor.hbs b/templates/voyage/fatigue-actor.hbs new file mode 100644 index 00000000..ce8ee826 --- /dev/null +++ b/templates/voyage/fatigue-actor.hbs @@ -0,0 +1,23 @@ +